Merge branch 'release-3.29' into release-3.30
diff --git a/.clang-tidy b/.clang-tidy index 03e6a51..6d2edd4 100644 --- a/.clang-tidy +++ b/.clang-tidy
@@ -5,12 +5,16 @@ -bugprone-easily-swappable-parameters,\ -bugprone-empty-catch,\ -bugprone-implicit-widening-of-multiplication-result,\ +-bugprone-inc-dec-in-conditions,\ -bugprone-macro-parentheses,\ -bugprone-misplaced-widening-cast,\ +-bugprone-multi-level-implicit-pointer-conversion,\ -bugprone-narrowing-conversions,\ -bugprone-switch-missing-default-case,\ -bugprone-too-small-loop-variable,\ -bugprone-unchecked-optional-access,\ +-bugprone-unused-local-non-trivial-variable,\ +-bugprone-unused-return-value,\ misc-*,\ -misc-confusable-identifiers,\ -misc-const-correctness,\ @@ -31,9 +35,12 @@ -modernize-use-transparent-functors,\ performance-*,\ -performance-avoid-endl,\ +-performance-enum-size,\ -performance-inefficient-vector-operation,\ -performance-noexcept-swap,\ readability-*,\ +-readability-avoid-nested-conditional-operator,\ +-readability-avoid-return-with-void-value,\ -readability-avoid-unconditional-preprocessor-if,\ -readability-convert-member-functions-to-static,\ -readability-function-cognitive-complexity,\ @@ -45,8 +52,11 @@ -readability-magic-numbers,\ -readability-make-member-function-const,\ -readability-named-parameter,\ +-readability-redundant-casting,\ -readability-redundant-declaration,\ +-readability-redundant-inline-specifier,\ -readability-redundant-member-init,\ +-readability-reference-to-constructed-temporary,\ -readability-simplify-boolean-expr,\ -readability-static-accessed-through-instance,\ -readability-suspicious-call-argument,\
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b2c90eb..fd13400 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml
@@ -35,7 +35,7 @@ # Additionally, jobs may also contain: # # - artifacts -# - dependency/needs jobs for required jobs +# - needs jobs for required jobs ################################################################################ # Job prefixes: @@ -59,7 +59,7 @@ p:doc-package: extends: - - .fedora39_sphinx_package + - .fedora40_sphinx_package - .cmake_prep_doc_linux - .linux_x86_64_tags - .cmake_doc_artifacts @@ -69,8 +69,6 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - p:source-package needs: - p:source-package @@ -80,15 +78,12 @@ extends: - .cmake_org_help - .run_cmake_org_help - dependencies: [] needs: [] u:cmake.org-help: extends: - .rsync_upload_help - .run_cmake_org_help - dependencies: - - b:cmake.org-help needs: - b:cmake.org-help @@ -108,18 +103,18 @@ - .cmake_cdash_artifacts - .run_automatically -l:tidy-fedora39: +l:tidy-fedora40: extends: - - .fedora39_tidy + - .fedora40_tidy - .cmake_build_linux - .cmake_tidy_artifacts - .linux_x86_64_tags - .cmake_cdash_artifacts - .run_automatically -l:sphinx-fedora39: +l:sphinx-fedora40: extends: - - .fedora39_sphinx + - .fedora40_sphinx - .cmake_build_linux - .cmake_sphinx_artifacts - .linux_x86_64_tags @@ -128,9 +123,9 @@ CMAKE_CI_JOB_CONTINUOUS: "true" CMAKE_CI_JOB_HELP: "true" -l:clang-analyzer-fedora39: +l:clang-analyzer-fedora40: extends: - - .fedora39_clang_analyzer + - .fedora40_clang_analyzer - .cmake_build_linux - .linux_x86_64_tags - .run_automatically @@ -222,7 +217,7 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" t:fedora39-hip-radeon: extends: @@ -232,11 +227,11 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-ninja-clang: +t:fedora40-ninja-clang: extends: - - .fedora39_ninja_clang + - .fedora40_ninja_clang - .cmake_test_linux_release - .linux_x86_64_tags - .run_dependent @@ -244,9 +239,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-ninja-multi-clang: +t:fedora40-ninja-multi-clang: extends: - - .fedora39_ninja_multi_clang + - .fedora40_ninja_multi_clang - .cmake_test_linux_release - .linux_x86_64_tags - .run_dependent @@ -254,9 +249,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-makefiles-clang: +t:fedora40-makefiles-clang: extends: - - .fedora39_makefiles_clang + - .fedora40_makefiles_clang - .cmake_test_linux_release - .linux_x86_64_tags - .run_dependent @@ -264,17 +259,17 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-makefiles: +t:fedora40-makefiles: extends: - - .fedora39_makefiles + - .fedora40_makefiles - .cmake_test_linux_release - .linux_x86_64_tags - .run_dependent - .needs_centos7_x86_64 -t:fedora39-makefiles-nospace: +t:fedora40-makefiles-nospace: extends: - - .fedora39_makefiles + - .fedora40_makefiles - .cmake_test_linux_release - .linux_x86_64_tags - .cmake_junit_artifacts @@ -282,7 +277,7 @@ - .needs_centos7_x86_64 variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci" - CMAKE_CI_BUILD_NAME: fedora39_makefiles_nospace + CMAKE_CI_BUILD_NAME: fedora40_makefiles_nospace CMAKE_CI_JOB_NIGHTLY: "true" t:nvhpc22.11-ninja: @@ -303,7 +298,7 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda10.2-nvidia: extends: @@ -313,6 +308,8 @@ - .cmake_junit_artifacts - .run_dependent - .needs_centos7_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda10.2-clang: extends: @@ -322,7 +319,7 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda11.6-nvidia: extends: @@ -332,6 +329,8 @@ - .cmake_junit_artifacts - .run_dependent - .needs_centos7_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda11.6-clang: extends: @@ -341,7 +340,7 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda11.8-minimal-ninja: extends: @@ -351,7 +350,7 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" t:cuda11.8-minimal-splayed-ninja: extends: @@ -361,7 +360,26 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" + +t:cuda12.2-nvidia: + extends: + - .cuda12.2_nvidia + - .cmake_test_linux_release + - .linux_x86_64_tags_cuda + - .cmake_junit_artifacts + - .run_dependent + - .needs_centos7_x86_64 + +t:cuda12.2-clang: + extends: + - .cuda12.2_clang + - .cmake_test_linux_release + - .linux_x86_64_tags_cuda + - .run_dependent + - .needs_centos7_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" t:hip5.5-nvidia: extends: @@ -371,7 +389,7 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" + CMAKE_CI_JOB_NIGHTLY: "true" t:hip5.5-radeon: extends: @@ -381,26 +399,6 @@ - .run_dependent - .needs_centos7_x86_64 variables: - CMAKE_CI_NO_MR: "true" - -t:linux-gcc-cxx-modules-ninja: - extends: - - .gcc_cxx_modules_ninja - - .cmake_test_linux_release - - .linux_x86_64_tags - - .run_dependent - - .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 - - .run_dependent - - .needs_centos7_x86_64 - variables: CMAKE_CI_JOB_NIGHTLY: "true" t:debian10-legacy: @@ -414,9 +412,9 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -b:fedora39-ninja: +b:fedora40-ninja: extends: - - .fedora39_ninja + - .fedora40_ninja - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -424,9 +422,9 @@ variables: CMAKE_CI_JOB_CONTINUOUS: "true" -b:fedora39-makefiles-symlinked: +b:fedora40-makefiles-symlinked: extends: - - .fedora39_makefiles_symlinked + - .fedora40_makefiles_symlinked - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -461,68 +459,48 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -b:fedora39-extdeps: +b:fedora40-extdeps: extends: - - .fedora39_extdeps + - .fedora40_extdeps - .cmake_build_linux_standalone - .linux_x86_64_tags - .run_manually variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-ninja: +t:fedora40-ninja: extends: - - .fedora39_ninja + - .fedora40_ninja - .cmake_test_linux - .linux_x86_64_tags_x11 - .cmake_test_artifacts - .run_dependent - dependencies: - - b:fedora39-ninja needs: - - b:fedora39-ninja + - b:fedora40-ninja variables: CMAKE_CI_JOB_CONTINUOUS: "true" -t:fedora39-makefiles-symlinked: +t:fedora40-makefiles-symlinked: extends: - - .fedora39_makefiles_symlinked + - .fedora40_makefiles_symlinked - .cmake_test_linux - .linux_x86_64_tags_x11 - .cmake_test_artifacts - .run_dependent - dependencies: - - b:fedora39-makefiles-symlinked needs: - - b:fedora39-makefiles-symlinked + - b:fedora40-makefiles-symlinked variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-ninja-multi: +t:fedora40-ninja-multi: extends: - - .fedora39_ninja_multi + - .fedora40_ninja_multi - .cmake_test_linux_external - .linux_x86_64_tags - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:fedora39-ninja needs: - - t:fedora39-ninja - -t:intel2016-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2016_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2016-el7 - -t:intel2016u1-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2016u1_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2016u1-el7 + - t:fedora40-ninja t:intel2016u2-makefiles: extends: @@ -531,62 +509,6 @@ CMAKE_CI_BUILD_NAME: intel2016u2_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2016u2-el7 -t:intel2017-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017-el7 - -t:intel2017u1-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u1_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u1-el7 - -t:intel2017u2-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u2-el7 - -t:intel2017u3-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u3_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u3-el7 - -t:intel2017u4-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u4_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u4-el7 - -t:intel2017u5-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u5_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u5-el7 - -t:intel2017u6-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u6_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u6-el7 - -t:intel2017u7-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2017u7_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u7-el7 - t:intel2017u8-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -594,34 +516,6 @@ CMAKE_CI_BUILD_NAME: intel2017u8_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u8-el7 -t:intel2018-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2018_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018-el7 - -t:intel2018u1-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2018u1_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u1-el7 - -t:intel2018u2-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2018u2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u2-el7 - -t:intel2018u3-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2018u3_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u3-el7 - t:intel2018u4-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -629,34 +523,6 @@ CMAKE_CI_BUILD_NAME: intel2018u4_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u4-el7 -t:intel2019-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2019_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019-el7 - -t:intel2019u1-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2019u1_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u1-el7 - -t:intel2019u2-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2019u2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u2-el7 - -t:intel2019u3-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2019u3_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u3-el7 - t:intel2019u4-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -664,20 +530,6 @@ CMAKE_CI_BUILD_NAME: intel2019u4_makefiles CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u4-el7 -t:intel2020-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2020_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2020-el8 - -t:intel2020u2-makefiles: - extends: - - .cmake_test_linux_intelclassic_makefiles - variables: - CMAKE_CI_BUILD_NAME: intel2020u2_makefiles - CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2020u2-el8 - t:intel2020u4-makefiles: extends: - .cmake_test_linux_intelclassic_makefiles @@ -846,6 +698,13 @@ 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 + b:linux-x86_64-package: extends: - .linux_package @@ -854,8 +713,6 @@ - .cmake_release_artifacts - .linux_x86_64_tags - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -865,8 +722,6 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:linux-x86_64-package needs: - b:linux-x86_64-package @@ -878,8 +733,6 @@ - .cmake_release_artifacts - .linux_aarch64_tags - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -889,16 +742,14 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:linux-aarch64-package needs: - b:linux-aarch64-package ## Sanitizer builds -b:fedora39-asan: +b:fedora40-asan: extends: - - .fedora39_asan + - .fedora40_asan - .cmake_build_linux - .cmake_build_artifacts - .linux_x86_64_tags @@ -906,16 +757,14 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:fedora39-asan: +t:fedora40-asan: extends: - - .fedora39_asan + - .fedora40_asan - .cmake_memcheck_linux - .linux_x86_64_tags - .run_dependent - dependencies: - - b:fedora39-asan needs: - - b:fedora39-asan + - b:fedora40-asan variables: CMAKE_CI_JOB_NIGHTLY: "true" @@ -951,6 +800,16 @@ variables: CMAKE_CI_JOB_NIGHTLY: "true" +b:macos-arm64-curl: + extends: + - .macos_arm64_curl + - .cmake_build_macos + - .cmake_build_artifacts + - .macos_arm64_tags + - .run_manually + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + b:macos-arm64-pch: extends: - .macos_arm64_pch @@ -967,8 +826,6 @@ - .cmake_test_artifacts - .macos_x86_64_tags - .run_dependent - dependencies: - - b:macos-x86_64-ninja needs: - b:macos-x86_64-ninja variables: @@ -981,8 +838,6 @@ - .cmake_test_artifacts - .macos_arm64_tags - .run_dependent - dependencies: - - b:macos-arm64-ninja needs: - b:macos-arm64-ninja variables: @@ -996,14 +851,23 @@ - .cmake_test_artifacts - .macos_arm64_tags - .run_dependent - dependencies: - - b:macos-arm64-ninja-symlinked needs: - b:macos-arm64-ninja-symlinked variables: CMAKE_CI_JOB_NIGHTLY: "true" CMAKE_CI_JOB_NIGHTLY_NINJA: "true" +t:macos-arm64-curl: + extends: + - .macos_arm64_curl + - .cmake_test_macos + - .macos_arm64_tags + - .run_dependent + needs: + - b:macos-arm64-curl + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + b:macos-x86_64-makefiles: extends: - .macos_x86_64_makefiles @@ -1018,8 +882,6 @@ - .cmake_test_macos - .macos_x86_64_tags - .run_dependent - dependencies: - - b:macos-x86_64-makefiles needs: - b:macos-x86_64-makefiles @@ -1030,8 +892,6 @@ - .macos_arm64_tags_ext - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:macos-arm64-ninja needs: - t:macos-arm64-ninja variables: @@ -1044,8 +904,6 @@ - .macos_x86_64_tags_ext - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:macos-x86_64-ninja needs: - t:macos-x86_64-ninja variables: @@ -1058,8 +916,6 @@ - .macos_arm64_tags_ext - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:macos-arm64-ninja needs: - t:macos-arm64-ninja @@ -1070,8 +926,6 @@ - .macos_arm64_tags_ext - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:macos-arm64-ninja needs: - t:macos-arm64-ninja variables: @@ -1084,8 +938,6 @@ - .macos_x86_64_tags_ext - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:macos-x86_64-ninja needs: - t:macos-x86_64-ninja variables: @@ -1098,8 +950,6 @@ - .macos_arm64_tags_ext - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:macos-arm64-ninja needs: - t:macos-arm64-ninja variables: @@ -1112,8 +962,6 @@ - .cmake_release_artifacts - .macos_arm64_tags_package - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -1123,8 +971,6 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:macos-package needs: - b:macos-package @@ -1135,8 +981,6 @@ - .cmake_release_artifacts - .macos_arm64_tags_package - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -1146,8 +990,6 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:macos10.10-package needs: - b:macos10.10-package @@ -1160,6 +1002,8 @@ - .cmake_build_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022 - .run_manually + variables: + CMAKE_CI_JOB_CONTINUOUS: "true" b:windows-vs2022-x64-pch: extends: @@ -1177,11 +1021,10 @@ - .windows_x86_64_tags_nonconcurrent_vs2022 - .cmake_test_artifacts - .run_dependent - dependencies: - - b:windows-vs2022-x64-ninja needs: - b:windows-vs2022-x64-ninja variables: + CMAKE_CI_JOB_CONTINUOUS: "true" CMAKE_CI_JOB_NIGHTLY_NINJA: "true" t:windows-vs2022-x64-ninja-multi: @@ -1191,34 +1034,41 @@ - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-vs2022-x64: +.t:windows-vs2022-x64: extends: - .windows_vs2022_x64 - .cmake_test_windows_external - - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja +t:windows-vs2022-x64: + extends: + - .t:windows-vs2022-x64 + - .windows_x86_64_tags_concurrent_vs2022 + variables: + CMAKE_CI_JOB_NIGHTLY: "false" + +t:windows-vs2022-x64-nightly: + extends: + - .t:windows-vs2022-x64 + - .windows_x86_64_tags_concurrent_vs2022_android + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + t:windows-vs2019-x64: extends: - .windows_vs2019_x64 - .cmake_test_windows_external - - .windows_x86_64_tags_concurrent_vs2019 + - .windows_x86_64_tags_concurrent_vs2019_android - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1231,8 +1081,6 @@ - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1245,8 +1093,6 @@ - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1259,8 +1105,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1273,8 +1117,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1287,8 +1129,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1302,8 +1142,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1317,8 +1155,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1332,8 +1168,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1344,30 +1178,26 @@ extends: - .windows_intelclassic_ninja - .cmake_test_windows_external - - .windows_x86_64_tags_concurrent + - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: CMAKE_CI_BUILD_NAME: windows_intel2021.9.0_ninja CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-oneapi2023.1.0-ninja: +t:windows-oneapi2024.1.0-ninja: extends: - .windows_inteloneapi_ninja - .cmake_test_windows_external - - .windows_x86_64_tags_concurrent + - .windows_x86_64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_oneapi2023.1.0_ninja + CMAKE_CI_BUILD_NAME: windows_oneapi2024.1.0_ninja CMAKE_CI_JOB_NIGHTLY: "true" t:mingw_osdn_io-mingw_makefiles: @@ -1377,8 +1207,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1391,8 +1219,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1405,8 +1231,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1419,8 +1243,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1433,8 +1255,6 @@ - .windows_x86_64_tags_concurrent - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-vs2022-x64-ninja needs: - t:windows-vs2022-x64-ninja variables: @@ -1459,8 +1279,6 @@ - .windows_arm64_tags_nonconcurrent_vs2022 - .cmake_test_artifacts - .run_dependent - dependencies: - - b:windows-arm64-vs2022-ninja needs: - b:windows-arm64-vs2022-ninja variables: @@ -1473,8 +1291,6 @@ - .windows_arm64_tags_concurrent_vs2022 - .cmake_junit_artifacts - .run_dependent - dependencies: - - t:windows-arm64-vs2022-ninja needs: - t:windows-arm64-vs2022-ninja variables: @@ -1489,8 +1305,6 @@ - .cmake_release_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022 - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -1500,8 +1314,6 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:windows-x86_64-package needs: - b:windows-x86_64-package @@ -1512,8 +1324,6 @@ - .cmake_release_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022 - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -1523,8 +1333,6 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:windows-i386-package needs: - b:windows-i386-package @@ -1535,8 +1343,6 @@ - .cmake_release_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022_arm64 - .run_only_for_package - dependencies: - - p:doc-package needs: - p:doc-package variables: @@ -1546,7 +1352,5 @@ extends: - .rsync_upload_package - .run_only_for_package - dependencies: - - b:windows-arm64-package needs: - b:windows-arm64-package
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore index d8efc1e..0558700 100644 --- a/.gitlab/.gitignore +++ b/.gitlab/.gitignore
@@ -10,15 +10,19 @@ /mingw /msvc* /ninja* +/nuget /openmp /open-watcom* /orangec /python* /qt* /sccache* +/swift /ticlang +/tmp /unstable-jom* /watcom -/wix* +/wix3 +/wix4 /clang-tidy-fixes /num_warnings.txt
diff --git a/.gitlab/ci/borland-env.ps1 b/.gitlab/ci/borland-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/borland.ps1 b/.gitlab/ci/borland.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/clang-env.ps1 b/.gitlab/ci/clang-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/clang.ps1 b/.gitlab/ci/clang.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/cmake-env.ps1 b/.gitlab/ci/cmake-env.ps1 new file mode 100644 index 0000000..505fa44 --- /dev/null +++ b/.gitlab/ci/cmake-env.ps1
@@ -0,0 +1,5 @@ +$pwdpath = $pwd.Path +& "$pwsh" -File ".gitlab/ci/cmake.ps1" +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\cmake\bin;$env:PATH" +cmake --version +$cmake = "cmake"
diff --git a/.gitlab/ci/cmake-env.sh b/.gitlab/ci/cmake-env.sh new file mode 100644 index 0000000..686a78f --- /dev/null +++ b/.gitlab/ci/cmake-env.sh
@@ -0,0 +1,3 @@ +.gitlab/ci/cmake.sh +export PATH="$PWD/.gitlab/cmake/bin:$PATH" +cmake --version
diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1 old mode 100755 new mode 100644 index f6b5cc7..6fe28e4 --- a/.gitlab/ci/cmake.ps1 +++ b/.gitlab/ci/cmake.ps1
@@ -1,12 +1,12 @@ $erroractionpreference = "stop" -$version = "3.27.6" +$version = "3.30.1" if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { - $sha256sum = "F013A0CCA091AA953F9A60A99292EC7A20AE3F9CEB05CB5C08EBE164097C526F" + $sha256sum = "CF7788FF9D92812DA194847D4EC874FC576F34079987D0F20C96CD09E2A16220" $platform = "windows-x86_64" } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { - $sha256sum = "980B7E532D77BFB4E5814C76403242C503019F1C0699440220CF2D527C8FF350" + $sha256sum = "02B433F70AA549449BE2D53046D0179590BF3B6290D9FDA3FBBB23F96A4F2802" $platform = "windows-arm64" } else { throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE")
diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh index 21da466b..3244e6d 100755 --- a/.gitlab/ci/cmake.sh +++ b/.gitlab/ci/cmake.sh
@@ -2,22 +2,22 @@ set -e -readonly version="3.27.6" +readonly version="3.30.1" case "$(uname -s)-$(uname -m)" in Linux-x86_64) shatool="sha256sum" - sha256sum="26373a283daa8490d772dc8a179450cd6d391cb2a9db8d4242fe09e361efc42e" + sha256sum="ac31f077ef3378641fa25a3cb980d21b2f083982d3149a8f2eb9154f2b53696b" platform="linux-x86_64" ;; Linux-aarch64) shatool="sha256sum" - sha256sum="811e5040ad7f3fb4924a875373d2a1a174a01400233a81a638a989157438a5e3" + sha256sum="ad234996f8750f11d7bd0d17b03f55c434816adf1f1671aab9e8bab21a43286a" platform="linux-aarch64" ;; Darwin-*) shatool="shasum -a 256" - sha256sum="a66b497289ab8c769b601d93833448eaae985beb762993837a51a79916d12f23" + sha256sum="51e12618829b811bba6f033ee8f39f6192da1b6abb20d82a7899d5134e879a4c" platform="macos-universal" ;; *)
diff --git a/.gitlab/ci/configure_cuda12.2_clang.cmake b/.gitlab/ci/configure_cuda12.2_clang.cmake new file mode 100644 index 0000000..8f10f86 --- /dev/null +++ b/.gitlab/ci/configure_cuda12.2_clang.cmake
@@ -0,0 +1,4 @@ +set(CMake_TEST_CUDA "Clang" CACHE STRING "") +set(CMake_TEST_CUDA_STANDARDS "03;11;14;17;20;23" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_cuda12.2_nvidia.cmake b/.gitlab/ci/configure_cuda12.2_nvidia.cmake new file mode 100644 index 0000000..51a2511 --- /dev/null +++ b/.gitlab/ci/configure_cuda12.2_nvidia.cmake
@@ -0,0 +1,5 @@ +set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "") +set(CMake_TEST_CUDA_CUPTI "ON" CACHE STRING "") +set(CMake_TEST_CUDA_STANDARDS "03;11;14;17;20" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian12_aarch64_ninja.cmake b/.gitlab/ci/configure_debian12_aarch64_ninja.cmake index 5b97e4f..0ebf604 100644 --- a/.gitlab/ci/configure_debian12_aarch64_ninja.cmake +++ b/.gitlab/ci/configure_debian12_aarch64_ninja.cmake
@@ -1,3 +1,6 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") + set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "") set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "") set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "") @@ -61,6 +64,9 @@ 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 "") @@ -92,6 +98,8 @@ set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "") set(CMake_TEST_Qt5 "ON" CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +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_UseSWIG "ON" CACHE BOOL "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian12_hip_radeon.cmake b/.gitlab/ci/configure_debian12_hip_radeon.cmake index c7d7004..d12922a 100644 --- a/.gitlab/ci/configure_debian12_hip_radeon.cmake +++ b/.gitlab/ci/configure_debian12_hip_radeon.cmake
@@ -1,3 +1,4 @@ set(CMake_TEST_HIP "amd" CACHE BOOL "") +set(CMake_TEST_HIP_STANDARDS "98;11;14;17;20;23" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian12_makefiles_clang.cmake b/.gitlab/ci/configure_debian12_makefiles_clang.cmake index 69f1e7c..7cba6f3 100644 --- a/.gitlab/ci/configure_debian12_makefiles_clang.cmake +++ b/.gitlab/ci/configure_debian12_makefiles_clang.cmake
@@ -1,3 +1,6 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") + if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") set(CMake_TEST_TICLANG_TOOLCHAINS "$ENV{CI_PROJECT_DIR}/.gitlab/ticlang" CACHE PATH "") endif()
diff --git a/.gitlab/ci/configure_debian12_ninja.cmake b/.gitlab/ci/configure_debian12_ninja.cmake index 89c1108..efca7a1 100644 --- a/.gitlab/ci/configure_debian12_ninja.cmake +++ b/.gitlab/ci/configure_debian12_ninja.cmake
@@ -1,2 +1,4 @@ +set(CMake_TEST_ASM_NASM "ON" CACHE BOOL "") + include("${CMAKE_CURRENT_LIST_DIR}/configure_debian12_ninja_common.cmake") set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_debian12_ninja_common.cmake b/.gitlab/ci/configure_debian12_ninja_common.cmake index d4d117b..0e2ecce 100644 --- a/.gitlab/ci/configure_debian12_ninja_common.cmake +++ b/.gitlab/ci/configure_debian12_ninja_common.cmake
@@ -1,3 +1,6 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") + set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "") set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "") set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "") @@ -8,6 +11,7 @@ endif() set(CMake_TEST_FindALSA "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 "") @@ -65,6 +69,9 @@ 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 "") @@ -90,6 +97,8 @@ 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 "") @@ -97,6 +106,8 @@ set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "") set(CMake_TEST_Qt5 "ON" CACHE BOOL "") set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "") +set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") if (NOT "$ENV{SWIFTC}" STREQUAL "") set(CMAKE_Swift_COMPILER "$ENV{SWIFTC}" CACHE FILEPATH "")
diff --git a/.gitlab/ci/configure_fedora39_asan.cmake b/.gitlab/ci/configure_fedora39_asan.cmake deleted file mode 100644 index 872517c..0000000 --- a/.gitlab/ci/configure_fedora39_asan.cmake +++ /dev/null
@@ -1,4 +0,0 @@ -set(CMAKE_C_FLAGS "-fsanitize=address" CACHE STRING "") -set(CMAKE_CXX_FLAGS "-fsanitize=address" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake")
diff --git a/.gitlab/ci/configure_fedora39_clang_analyzer.cmake b/.gitlab/ci/configure_fedora39_clang_analyzer.cmake deleted file mode 100644 index 1bfb3fb..0000000 --- a/.gitlab/ci/configure_fedora39_clang_analyzer.cmake +++ /dev/null
@@ -1,3 +0,0 @@ -set(configure_no_sccache 1) - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake")
diff --git a/.gitlab/ci/configure_fedora39_common_clang.cmake b/.gitlab/ci/configure_fedora39_common_clang.cmake deleted file mode 100644 index 1b3835a..0000000 --- a/.gitlab/ci/configure_fedora39_common_clang.cmake +++ /dev/null
@@ -1,5 +0,0 @@ -set(CMAKE_Fortran_COMPILER "/usr/bin/flang-new" CACHE FILEPATH "") -set(CMAKE_Fortran_COMPILER_ID "LLVMFlang" CACHE STRING "") -set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 "1" CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora39_hip_radeon.cmake b/.gitlab/ci/configure_fedora39_hip_radeon.cmake index c7d7004..8271af3 100644 --- a/.gitlab/ci/configure_fedora39_hip_radeon.cmake +++ b/.gitlab/ci/configure_fedora39_hip_radeon.cmake
@@ -1,3 +1,4 @@ set(CMake_TEST_HIP "amd" CACHE BOOL "") +set(CMake_TEST_HIP_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora39_makefiles.cmake b/.gitlab/ci/configure_fedora39_makefiles.cmake deleted file mode 100644 index 953b2a8..0000000 --- a/.gitlab/ci/configure_fedora39_makefiles.cmake +++ /dev/null
@@ -1,103 +0,0 @@ -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_CTestUpdate_P4 "ON" CACHE BOOL "") -endif() - -set(CMake_TEST_FindALSA "ON" CACHE BOOL "") -set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "") -set(CMake_TEST_FindBoost "ON" CACHE BOOL "") -set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "") -set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "") -set(CMake_TEST_FindCups "ON" CACHE BOOL "") -set(CMake_TEST_FindCURL "ON" CACHE BOOL "") -set(CMake_TEST_FindDevIL "ON" CACHE BOOL "") -set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "") -set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "") -set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "") -set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "") -set(CMake_TEST_FindFreetype "ON" CACHE BOOL "") -set(CMake_TEST_FindGDAL "ON" CACHE BOOL "") -set(CMake_TEST_FindGIF "ON" CACHE BOOL "") -set(CMake_TEST_FindGit "ON" CACHE BOOL "") -set(CMake_TEST_FindGLEW "ON" CACHE BOOL "") -set(CMake_TEST_FindGLUT "ON" CACHE BOOL "") -set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "") -set(CMake_TEST_FindGSL "ON" CACHE BOOL "") -set(CMake_TEST_FindGTest "ON" CACHE BOOL "") -set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/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_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_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_FindPython2 "ON" CACHE BOOL "") -set(CMake_TEST_FindPython3 "ON" CACHE BOOL "") -set(CMake_TEST_FindPython2_NumPy "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_RVM "ON" CACHE BOOL "") -set(CMake_TEST_FindSDL "ON" CACHE BOOL "") -set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "") -set(CMake_TEST_FindTIFF "ON" CACHE BOOL "") -set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "") -set(CMake_TEST_FindX11 "ON" CACHE BOOL "") -set(CMake_TEST_FindXalanC "ON" CACHE BOOL "") -set(CMake_TEST_FindXercesC "ON" CACHE BOOL "") -set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "") -set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "") -set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "") -set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "") -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMake_TEST_ISPC "ON" CACHE STRING "") -endif() -set(CMake_TEST_Qt5 "ON" CACHE BOOL "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") -set(CMake_TEST_UseSWIG "ON" CACHE BOOL "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora39_makefiles_clang.cmake b/.gitlab/ci/configure_fedora39_makefiles_clang.cmake deleted file mode 100644 index a85ae70..0000000 --- a/.gitlab/ci/configure_fedora39_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_fedora39_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora39_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora39_makefiles_symlinked.cmake deleted file mode 100644 index b53ab66..0000000 --- a/.gitlab/ci/configure_fedora39_makefiles_symlinked.cmake +++ /dev/null
@@ -1,2 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake")
diff --git a/.gitlab/ci/configure_fedora39_ninja.cmake b/.gitlab/ci/configure_fedora39_ninja.cmake deleted file mode 100644 index ff233a3..0000000 --- a/.gitlab/ci/configure_fedora39_ninja.cmake +++ /dev/null
@@ -1,14 +0,0 @@ -set(CMake_TEST_GUI "ON" CACHE BOOL "") -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMake_TEST_ISPC "ON" CACHE STRING "") -endif() -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" 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 "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake")
diff --git a/.gitlab/ci/configure_fedora39_ninja_clang.cmake b/.gitlab/ci/configure_fedora39_ninja_clang.cmake deleted file mode 100644 index 79d3eaa..0000000 --- a/.gitlab/ci/configure_fedora39_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" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora39_ninja_multi.cmake b/.gitlab/ci/configure_fedora39_ninja_multi.cmake deleted file mode 100644 index 94af721..0000000 --- a/.gitlab/ci/configure_fedora39_ninja_multi.cmake +++ /dev/null
@@ -1,5 +0,0 @@ -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMake_TEST_ISPC "ON" CACHE STRING "") -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora39_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora39_ninja_multi_clang.cmake deleted file mode 100644 index 79d3eaa..0000000 --- a/.gitlab/ci/configure_fedora39_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" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora39_tidy.cmake b/.gitlab/ci/configure_fedora39_tidy.cmake deleted file mode 100644 index 7a3eaa6..0000000 --- a/.gitlab/ci/configure_fedora39_tidy.cmake +++ /dev/null
@@ -1,6 +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 "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake")
diff --git a/.gitlab/ci/configure_fedora40_asan.cmake b/.gitlab/ci/configure_fedora40_asan.cmake new file mode 100644 index 0000000..ccdba4e --- /dev/null +++ b/.gitlab/ci/configure_fedora40_asan.cmake
@@ -0,0 +1,4 @@ +set(CMAKE_C_FLAGS "-fsanitize=address" CACHE STRING "") +set(CMAKE_CXX_FLAGS "-fsanitize=address" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common.cmake")
diff --git a/.gitlab/ci/configure_fedora40_clang_analyzer.cmake b/.gitlab/ci/configure_fedora40_clang_analyzer.cmake new file mode 100644 index 0000000..c210126 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_clang_analyzer.cmake
@@ -0,0 +1,3 @@ +set(configure_no_sccache 1) + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common.cmake")
diff --git a/.gitlab/ci/configure_fedora39_common.cmake b/.gitlab/ci/configure_fedora40_common.cmake similarity index 100% rename from .gitlab/ci/configure_fedora39_common.cmake rename to .gitlab/ci/configure_fedora40_common.cmake
diff --git a/.gitlab/ci/configure_fedora40_common_clang.cmake b/.gitlab/ci/configure_fedora40_common_clang.cmake new file mode 100644 index 0000000..a4ae3f3 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_common_clang.cmake
@@ -0,0 +1,17 @@ +set(CMAKE_Fortran_COMPILER "/usr/bin/flang-new" 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_fedora39_extdeps.cmake b/.gitlab/ci/configure_fedora40_extdeps.cmake similarity index 100% rename from .gitlab/ci/configure_fedora39_extdeps.cmake rename to .gitlab/ci/configure_fedora40_extdeps.cmake
diff --git a/.gitlab/ci/configure_fedora40_makefiles.cmake b/.gitlab/ci/configure_fedora40_makefiles.cmake new file mode 100644 index 0000000..21a5be5 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_makefiles.cmake
@@ -0,0 +1,116 @@ +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_CTestUpdate_P4 "ON" CACHE BOOL "") +endif() + +set(CMake_TEST_ASM_NASM "ON" CACHE BOOL "") +set(CMake_TEST_FindALSA "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_FindPython2 "ON" CACHE BOOL "") +set(CMake_TEST_FindPython3 "ON" CACHE BOOL "") +set(CMake_TEST_FindPython2_NumPy "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_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_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_UseSWIG "ON" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora40_makefiles_clang.cmake b/.gitlab/ci/configure_fedora40_makefiles_clang.cmake new file mode 100644 index 0000000..d5739c3 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_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_fedora40_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora40_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora40_makefiles_symlinked.cmake new file mode 100644 index 0000000..6a677fd --- /dev/null +++ b/.gitlab/ci/configure_fedora40_makefiles_symlinked.cmake
@@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common.cmake")
diff --git a/.gitlab/ci/configure_fedora40_ninja.cmake b/.gitlab/ci/configure_fedora40_ninja.cmake new file mode 100644 index 0000000..ae968a3 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_ninja.cmake
@@ -0,0 +1,17 @@ +set(CMake_TEST_GUI "ON" CACHE BOOL "") +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_ISPC "ON" CACHE STRING "") +endif() +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" 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 "") + +# "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 "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common.cmake")
diff --git a/.gitlab/ci/configure_fedora40_ninja_clang.cmake b/.gitlab/ci/configure_fedora40_ninja_clang.cmake new file mode 100644 index 0000000..c760603 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_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" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora40_ninja_multi.cmake b/.gitlab/ci/configure_fedora40_ninja_multi.cmake new file mode 100644 index 0000000..f77606e --- /dev/null +++ b/.gitlab/ci/configure_fedora40_ninja_multi.cmake
@@ -0,0 +1,6 @@ +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_ISPC "ON" CACHE STRING "") +endif() +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora40_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora40_ninja_multi_clang.cmake new file mode 100644 index 0000000..c760603 --- /dev/null +++ b/.gitlab/ci/configure_fedora40_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" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora39_sphinx.cmake b/.gitlab/ci/configure_fedora40_sphinx.cmake similarity index 100% rename from .gitlab/ci/configure_fedora39_sphinx.cmake rename to .gitlab/ci/configure_fedora40_sphinx.cmake
diff --git a/.gitlab/ci/configure_fedora39_sphinx_package.cmake b/.gitlab/ci/configure_fedora40_sphinx_package.cmake similarity index 100% rename from .gitlab/ci/configure_fedora39_sphinx_package.cmake rename to .gitlab/ci/configure_fedora40_sphinx_package.cmake
diff --git a/.gitlab/ci/configure_fedora40_tidy.cmake b/.gitlab/ci/configure_fedora40_tidy.cmake new file mode 100644 index 0000000..037732c --- /dev/null +++ b/.gitlab/ci/configure_fedora40_tidy.cmake
@@ -0,0 +1,6 @@ +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 "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora40_common.cmake")
diff --git a/.gitlab/ci/configure_hip5.5_nvidia.cmake b/.gitlab/ci/configure_hip5.5_nvidia.cmake index 4b3511a..036a227 100644 --- a/.gitlab/ci/configure_hip5.5_nvidia.cmake +++ b/.gitlab/ci/configure_hip5.5_nvidia.cmake
@@ -1,3 +1,4 @@ set(CMake_TEST_HIP "nvidia" CACHE BOOL "") +set(CMake_TEST_HIP_STANDARDS "98;11;14;17" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_hip5.5_radeon.cmake b/.gitlab/ci/configure_hip5.5_radeon.cmake index c7d7004..06e47c9 100644 --- a/.gitlab/ci/configure_hip5.5_radeon.cmake +++ b/.gitlab/ci/configure_hip5.5_radeon.cmake
@@ -1,3 +1,4 @@ set(CMake_TEST_HIP "amd" CACHE BOOL "") +set(CMake_TEST_CUDA_STANDARDS "98;11;14;17;20;23;26" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.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 f0ba9eb..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" 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 f0ba9eb..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" CACHE STRING "") - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_curl.cmake b/.gitlab/ci/configure_macos_arm64_curl.cmake new file mode 100644 index 0000000..af49873 --- /dev/null +++ b/.gitlab/ci/configure_macos_arm64_curl.cmake
@@ -0,0 +1,12 @@ +# Build with our vendored curl instead of the default system version. +set(CMAKE_USE_SYSTEM_CURL "OFF" 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 "") + +# Test that our vendored curl accepts CURL_SSLVERSION_TLSv1_3. It is passed +# through to Secure Transport, but macOS does not actually enforce it. +set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_ninja.cmake b/.gitlab/ci/configure_macos_arm64_ninja.cmake index f2068a1..de0ffc0 100644 --- a/.gitlab/ci/configure_macos_arm64_ninja.cmake +++ b/.gitlab/ci/configure_macos_arm64_ninja.cmake
@@ -1,8 +1,14 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") + set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") set(CMake_TEST_GUI "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 "") + include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake index 5d1620d..43505db 100644 --- a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake +++ b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
@@ -7,6 +7,8 @@ set(CMake_TEST_ISPC "ON" CACHE STRING "") endif() 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 "") include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_x86_64_ninja.cmake b/.gitlab/ci/configure_macos_x86_64_ninja.cmake index 5d1620d..83d1e2c 100644 --- a/.gitlab/ci/configure_macos_x86_64_ninja.cmake +++ b/.gitlab/ci/configure_macos_x86_64_ninja.cmake
@@ -1,3 +1,6 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") + set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "") set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") @@ -7,6 +10,8 @@ set(CMake_TEST_ISPC "ON" CACHE STRING "") endif() 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 "") include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_x86_64_xcode.cmake b/.gitlab/ci/configure_macos_x86_64_xcode.cmake index 1b976d2..f1b91ec 100644 --- a/.gitlab/ci/configure_macos_x86_64_xcode.cmake +++ b/.gitlab/ci/configure_macos_x86_64_xcode.cmake
@@ -1,2 +1,3 @@ +set(CMake_TEST_RunCMake_ExternalProject_RUN_SERIAL ON CACHE BOOL "") include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake b/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake index a12ee6c..64a8913 100644 --- a/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake +++ b/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake
@@ -1,8 +1,12 @@ # Qt host tools are not yet available natively on windows-arm64. set(CMake_TEST_GUI "OFF" CACHE BOOL "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") set(BUILD_QtDialog "OFF" CACHE BOOL "") set(CMAKE_PREFIX_PATH "" 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.2" CACHE STRING "") + include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_msvc_cxx_modules_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_wix_common.cmake") include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_vs_common_ninja.cmake")
diff --git a/.gitlab/ci/configure_windows_clang_common.cmake b/.gitlab/ci/configure_windows_clang_common.cmake index 3d93aae..89d0c9c 100644 --- a/.gitlab/ci/configure_windows_clang_common.cmake +++ b/.gitlab/ci/configure_windows_clang_common.cmake
@@ -1,3 +1,12 @@ +if("$ENV{CMAKE_CI_BUILD_NAME}" MATCHES "(^|_)gnu(_|$)") + 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 "") +else() + # FIXME: Implement C23 and C++23 support for clang-cl. + set(CMake_TEST_C_STANDARDS "90;99;11;17" CACHE STRING "") + set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20" CACHE STRING "") +endif() + 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_i386_package.cmake b/.gitlab/ci/configure_windows_i386_package.cmake index 65e1dcb..c80ebcc 100644 --- a/.gitlab/ci/configure_windows_i386_package.cmake +++ b/.gitlab/ci/configure_windows_i386_package.cmake
@@ -1,6 +1,9 @@ # CPack package file name component for this platform. set(CPACK_SYSTEM_NAME "windows-i386" CACHE STRING "") +# Tell WiX to package for this architecture. +set(CPACK_WIX_ARCHITECTURE "x86" CACHE STRING "") + # Use APIs from at most Windows 7 set(CMAKE_C_FLAGS "-D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000" CACHE STRING "") set(CMAKE_CXX_FLAGS "-GR -EHsc -D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000" CACHE STRING "")
diff --git a/.gitlab/ci/configure_windows_msvc_common.cmake b/.gitlab/ci/configure_windows_msvc_common.cmake index 6d66a05..946eca7 100644 --- a/.gitlab/ci/configure_windows_msvc_common.cmake +++ b/.gitlab/ci/configure_windows_msvc_common.cmake
@@ -1,2 +1,4 @@ +set(CMake_TEST_Java OFF CACHE BOOL "") + set(configure_no_sccache 1) include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake b/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake index e378d62..207f2a5 100644 --- a/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake +++ b/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake
@@ -1 +1 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,shared,export_bmi,install_bmi,bmionly" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,shared,export_bmi,install_bmi,bmionly,import_std23" CACHE STRING "")
diff --git a/.gitlab/ci/configure_windows_package_common.cmake b/.gitlab/ci/configure_windows_package_common.cmake index 541a541..11bfad5 100644 --- a/.gitlab/ci/configure_windows_package_common.cmake +++ b/.gitlab/ci/configure_windows_package_common.cmake
@@ -19,6 +19,8 @@ set(CMake_TEST_Qt6 OFF CACHE BOOL "") set(Python_FIND_REGISTRY NEVER CACHE STRING "") +set(CMake_BUILD_WIX_CUSTOM_ACTION ON CACHE BOOL "") + set(CMake_CPACK_CUSTOM_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/CMakeCPack.cmake" CACHE FILEPATH "") include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2019_x64.cmake b/.gitlab/ci/configure_windows_vs2019_x64.cmake index b859525..e4795b9 100644 --- a/.gitlab/ci/configure_windows_vs2019_x64.cmake +++ b/.gitlab/ci/configure_windows_vs2019_x64.cmake
@@ -1,4 +1,5 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_ANDROID_VS16 ON CACHE BOOL "") set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "") endif()
diff --git a/.gitlab/ci/configure_windows_vs2022_x64.cmake b/.gitlab/ci/configure_windows_vs2022_x64.cmake index 51ee514..c29a9ec 100644 --- a/.gitlab/ci/configure_windows_vs2022_x64.cmake +++ b/.gitlab/ci/configure_windows_vs2022_x64.cmake
@@ -1,3 +1,7 @@ +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_ANDROID_VS17 ON CACHE BOOL "") +endif() + set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions,shared" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_msvc_cxx_modules_common.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake index 54abf72..d2064e6 100644 --- a/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake +++ b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
@@ -1,8 +1,17 @@ +set(CMake_TEST_C_STANDARDS "90;99;11;17" CACHE STRING "") +set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "") + if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") set(CMake_TEST_CPACK_INNOSETUP "ON" CACHE STRING "") + set(CMake_TEST_CPACK_NUGET "ON" CACHE STRING "") set(CMake_TEST_ISPC "ON" CACHE STRING "") + set(CMake_TEST_Swift "ON" CACHE STRING "") endif() + 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 "") include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_msvc_cxx_modules_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_wix_common.cmake") include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_vs_common_ninja.cmake")
diff --git a/.gitlab/ci/configure_windows_wix_common.cmake b/.gitlab/ci/configure_windows_wix_common.cmake new file mode 100644 index 0000000..4219c41 --- /dev/null +++ b/.gitlab/ci/configure_windows_wix_common.cmake
@@ -0,0 +1,5 @@ +get_filename_component(wix3_dir "${CMAKE_CURRENT_LIST_DIR}/../wix3" ABSOLUTE) +set(CMake_TEST_CPACK_WIX3 "${wix3_dir}" CACHE PATH "") + +get_filename_component(wix4_dir "${CMAKE_CURRENT_LIST_DIR}/../wix4" ABSOLUTE) +set(CMake_TEST_CPACK_WIX4 "${wix4_dir}" CACHE PATH "")
diff --git a/.gitlab/ci/configure_windows_x86_64_package.cmake b/.gitlab/ci/configure_windows_x86_64_package.cmake index 3a141a7..051e3fc 100644 --- a/.gitlab/ci/configure_windows_x86_64_package.cmake +++ b/.gitlab/ci/configure_windows_x86_64_package.cmake
@@ -1,6 +1,9 @@ # CPack package file name component for this platform. set(CPACK_SYSTEM_NAME "windows-x86_64" CACHE STRING "") +# Tell WiX to package for this architecture. +set(CPACK_WIX_ARCHITECTURE "x64" CACHE STRING "") + # Use APIs from at most Windows 7 set(CMAKE_C_FLAGS "-D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000" CACHE STRING "") set(CMAKE_CXX_FLAGS "-GR -EHsc -D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000" CACHE STRING "")
diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake index a2789c3..5d3ebae 100644 --- a/.gitlab/ci/ctest_exclusions.cmake +++ b/.gitlab/ci/ctest_exclusions.cmake
@@ -13,6 +13,13 @@ "^ExternalProjectUpdateSetup$") endif () +if (CTEST_CMAKE_GENERATOR MATCHES "Xcode") + list(APPEND test_exclusions + # FIXME(#26301): The XCTest fails with Xcode 16.0. + "^XCTest$" + ) +endif () + if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "_asan") list(APPEND test_exclusions CTestTest2 # crashes on purpose @@ -34,6 +41,13 @@ ) endif() +if ("$ENV{CMAKE_CONFIGURATION}" STREQUAL "windows_vs2022_x64_ninja") + list(APPEND test_exclusions + # FIXME(#25573): This test failure needs further investigation. + "^SwiftMixLib$" + ) +endif() + string(REPLACE ";" "|" test_exclusions "${test_exclusions}") if (test_exclusions) set(test_exclusions "(${test_exclusions})")
diff --git a/.gitlab/ci/ctest_memcheck_fedora39_asan.lsan.supp b/.gitlab/ci/ctest_memcheck_fedora40_asan.lsan.supp similarity index 100% rename from .gitlab/ci/ctest_memcheck_fedora39_asan.lsan.supp rename to .gitlab/ci/ctest_memcheck_fedora40_asan.lsan.supp
diff --git a/.gitlab/ci/docker/cuda12.2/Dockerfile b/.gitlab/ci/docker/cuda12.2/Dockerfile new file mode 100644 index 0000000..e46f228 --- /dev/null +++ b/.gitlab/ci/docker/cuda12.2/Dockerfile
@@ -0,0 +1,9 @@ +FROM kitware/nvidia-cuda:12.2.2-devel-ubuntu22.04 +MAINTAINER Brad King <brad.king@kitware.com> + +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 + +COPY install_deps.sh /root/install_deps.sh +RUN sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/cuda12.2/install_deps.sh b/.gitlab/ci/docker/cuda12.2/install_deps.sh new file mode 100755 index 0000000..94fbd98 --- /dev/null +++ b/.gitlab/ci/docker/cuda12.2/install_deps.sh
@@ -0,0 +1,20 @@ +#!/bin/sh + +set -e + +apt-get update + +# Install dependency without interaction. +env DEBIAN_FRONTEND=noninteractive \ + TZ=America/New_York \ + apt-get install -y \ + tzdata + +# Install development tools. +apt-get install -y \ + g++ \ + clang-18 \ + curl \ + git + +apt-get clean
diff --git a/.gitlab/ci/docker/cuda12.2/llvm-snapshot.gpg.key b/.gitlab/ci/docker/cuda12.2/llvm-snapshot.gpg.key new file mode 100644 index 0000000..aa6b105 --- /dev/null +++ b/.gitlab/ci/docker/cuda12.2/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/cuda12.2/llvm.list b/.gitlab/ci/docker/cuda12.2/llvm.list new file mode 100644 index 0000000..350a712 --- /dev/null +++ b/.gitlab/ci/docker/cuda12.2/llvm.list
@@ -0,0 +1,2 @@ +deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main +deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main
diff --git a/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst b/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst index a59f2c9..014c7ea 100644 --- a/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst +++ b/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst
@@ -32,6 +32,9 @@ subversion mercurial +# Install ASM_NASM language toolchain. +nasm + # Install HIP language toolchain. hipcc
diff --git a/.gitlab/ci/docker/fedora39-hip/Dockerfile b/.gitlab/ci/docker/fedora39-hip/Dockerfile new file mode 100644 index 0000000..0347cc4 --- /dev/null +++ b/.gitlab/ci/docker/fedora39-hip/Dockerfile
@@ -0,0 +1,27 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=fedora:39 + +FROM ${BASE_IMAGE} AS dnf-cache +# Populate DNF cache w/ the fresh metadata and prefetch packages. +RUN --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install \ + --setopt=install_weak_deps=False \ + --setopt=fastestmirror=True \ + --setopt=max_parallel_downloads=10 \ + --downloadonly \ + -y \ + $(grep -h '^[^#]\+$' /root/*.lst) + +FROM ${BASE_IMAGE} +LABEL 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=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ + --mount=type=cache,target=/var/cache/pip \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/fedora39-hip/deps_packages.lst b/.gitlab/ci/docker/fedora39-hip/deps_packages.lst new file mode 100644 index 0000000..70b114c --- /dev/null +++ b/.gitlab/ci/docker/fedora39-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. +hsakmt-devel +lld +llvm +rocm-comgr-devel +rocm-hip-devel +rocm-runtime-devel
diff --git a/.gitlab/ci/docker/fedora39-hip/install_deps.sh b/.gitlab/ci/docker/fedora39-hip/install_deps.sh new file mode 100755 index 0000000..eedff9c --- /dev/null +++ b/.gitlab/ci/docker/fedora39-hip/install_deps.sh
@@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +dnf install \ + --setopt=install_weak_deps=False \ + --setopt=fastestmirror=True \ + --setopt=max_parallel_downloads=10 \ + -y \ + $(grep '^[^#]\+$' /root/deps_packages.lst)
diff --git a/.gitlab/ci/docker/fedora39/Dockerfile b/.gitlab/ci/docker/fedora39/Dockerfile deleted file mode 100644 index f14e017..0000000 --- a/.gitlab/ci/docker/fedora39/Dockerfile +++ /dev/null
@@ -1,58 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG BASE_IMAGE=fedora:39 - -FROM ${BASE_IMAGE} AS dnf-cache -# Populate DNF cache w/ the fresh metadata and prefetch packages. -RUN --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=rvm_packages.lst,target=/root/rvm_packages.lst \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - dnf install \ - --setopt=install_weak_deps=False \ - --setopt=fastestmirror=True \ - --setopt=max_parallel_downloads=10 \ - --downloadonly \ - -y \ - $(grep -h '^[^#]\+$' /root/*.lst) - - -FROM ${BASE_IMAGE} AS rvm-build -LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>" - -RUN --mount=type=bind,source=install_rvm.sh,target=/root/install_rvm.sh \ - --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - sh /root/install_rvm.sh - - -FROM ${BASE_IMAGE} AS iwyu-build -LABEL maintainer="Kyle Edwards <kyle.edwards@kitware.com>" - -RUN --mount=type=bind,source=install_iwyu.sh,target=/root/install_iwyu.sh \ - --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \ - --mount=type=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - sh /root/install_iwyu.sh - - -FROM ${BASE_IMAGE} -LABEL maintainer="Ben Boeckel <ben.boeckel@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=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ - --mount=type=cache,target=/var/cache/pip \ - --mount=type=tmpfs,target=/var/log \ - --mount=type=tmpfs,target=/tmp \ - sh /root/install_deps.sh - -RUN --mount=type=bind,from=rvm-build,source=/root,target=/root \ - tar -C /usr/local -xf /root/rvm.tar - -RUN --mount=type=bind,from=iwyu-build,source=/root,target=/root \ - tar -C / -xf /root/iwyu.tar
diff --git a/.gitlab/ci/docker/fedora39/deps_packages.lst b/.gitlab/ci/docker/fedora39/deps_packages.lst deleted file mode 100644 index 7656cbc..0000000 --- a/.gitlab/ci/docker/fedora39/deps_packages.lst +++ /dev/null
@@ -1,124 +0,0 @@ -# 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 -flang-devel -gcc-c++ -git-core -llvm-devel -make - -# Install optional external build dependencies. -bzip2-devel -expat-devel -jsoncpp-devel -libarchive-devel -libcurl-devel -libuv-devel -libuv-devel -libzstd-devel -rhash-devel -xz-devel -zlib-devel - -# Install documentation tools. -python3-sphinx -texinfo -qt5-qttools-devel -qt6-qttools-devel - -# Install lint tools. -clang-analyzer -codespell - -# Tools needed for the test suite. -findutils -file -jq -which - -# Install HIP language toolchain. -hsakmt-devel -lld -rocm-comgr-devel -rocm-hip-devel -rocm-runtime-devel - -# Packages needed to test CTest. -breezy -subversion -mercurial - -# Packages needed to test CPack. -rpm-build - -# Packages needed to test find modules. -alsa-lib-devel -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 -java-11-openjdk-devel -jsoncpp-devel -lapack-devel -libarchive-devel -libcurl-devel -libicu-devel -libinput-devel systemd-devel -libjpeg-turbo-devel -libpng-devel -opensp-devel -postgresql-server-devel -libtiff-devel -libuv-devel -libxml2-devel -libxslt-devel -mpich-devel -openal-soft-devel -openmpi-devel -patch -perl -protobuf-devel protobuf-c-devel protobuf-lite-devel -pypy2 pypy2-devel -pypy3 pypy3-devel -python2 python2-devel -python3 python3-devel python3-numpy -python3-jsmin python3-jsonschema -ruby rubygems ruby-devel -SDL-devel -sqlite-devel -swig -unixODBC-devel -wxGTK-devel -xalan-c-devel -xerces-c-devel -xz-devel
diff --git a/.gitlab/ci/docker/fedora39/install_iwyu.sh b/.gitlab/ci/docker/fedora39/install_iwyu.sh deleted file mode 100755 index 684e355..0000000 --- a/.gitlab/ci/docker/fedora39/install_iwyu.sh +++ /dev/null
@@ -1,42 +0,0 @@ -#!/bin/sh - -set -e - -# Install development tools. -dnf install \ - --setopt=install_weak_deps=False \ - --setopt=fastestmirror=True \ - --setopt=max_parallel_downloads=10 \ - -y \ - $(grep '^[^#]\+$' /root/iwyu_packages.lst) - -cd /root -git clone "https://github.com/include-what-you-use/include-what-you-use.git" -cd include-what-you-use -readonly llvm_full_version="$( clang --version | head -n1 | cut -d' ' -f3 )" -readonly llvm_version="$( echo "$llvm_full_version" | cut -d. -f-1 )" -git checkout "clang_$llvm_version" -git apply <<EOF -diff --git a/iwyu_driver.cc b/iwyu_driver.cc -index 42fea35..fbb77a9 100644 ---- a/iwyu_driver.cc -+++ b/iwyu_driver.cc -@@ -167,6 +167,7 @@ CompilerInstance* CreateCompilerInstance(int argc, const char **argv) { - DiagnosticsEngine diagnostics(diagnostic_id, &*diagnostic_options, - diagnostic_client); - Driver driver(path, getDefaultTargetTriple(), diagnostics); -+ driver.ResourceDir = "/usr/lib64/clang/$llvm_full_version"; - driver.setTitle("include what you use"); - - // Expand out any response files passed on the command line -EOF -mkdir build -cd build - -cmake -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - "-DCMAKE_INSTALL_PREFIX=/usr/local/lib64/llvm-$llvm_version" \ - .. -ninja -DESTDIR=/root/iwyu-destdir ninja install -tar -C /root/iwyu-destdir -cf /root/iwyu.tar .
diff --git a/.gitlab/ci/docker/fedora40/Dockerfile b/.gitlab/ci/docker/fedora40/Dockerfile new file mode 100644 index 0000000..896456c --- /dev/null +++ b/.gitlab/ci/docker/fedora40/Dockerfile
@@ -0,0 +1,58 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=fedora:40 + +FROM ${BASE_IMAGE} AS dnf-cache +# Populate DNF cache w/ the fresh metadata and prefetch packages. +RUN --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=rvm_packages.lst,target=/root/rvm_packages.lst \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + dnf install \ + --setopt=install_weak_deps=False \ + --setopt=fastestmirror=True \ + --setopt=max_parallel_downloads=10 \ + --downloadonly \ + -y \ + $(grep -h '^[^#]\+$' /root/*.lst) + + +FROM ${BASE_IMAGE} AS rvm-build +LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>" + +RUN --mount=type=bind,source=install_rvm.sh,target=/root/install_rvm.sh \ + --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + sh /root/install_rvm.sh + + +FROM ${BASE_IMAGE} AS iwyu-build +LABEL maintainer="Kyle Edwards <kyle.edwards@kitware.com>" + +RUN --mount=type=bind,source=install_iwyu.sh,target=/root/install_iwyu.sh \ + --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \ + --mount=type=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + sh /root/install_iwyu.sh + + +FROM ${BASE_IMAGE} +LABEL maintainer="Ben Boeckel <ben.boeckel@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=cache,from=dnf-cache,source=/var/cache/dnf,target=/var/cache/dnf,sharing=private \ + --mount=type=cache,target=/var/cache/pip \ + --mount=type=tmpfs,target=/var/log \ + --mount=type=tmpfs,target=/tmp \ + sh /root/install_deps.sh + +RUN --mount=type=bind,from=rvm-build,source=/root,target=/root \ + tar -C /usr/local -xf /root/rvm.tar + +RUN --mount=type=bind,from=iwyu-build,source=/root,target=/root \ + tar -C / -xf /root/iwyu.tar
diff --git a/.gitlab/ci/docker/fedora40/deps_packages.lst b/.gitlab/ci/docker/fedora40/deps_packages.lst new file mode 100644 index 0000000..c7bad7c --- /dev/null +++ b/.gitlab/ci/docker/fedora40/deps_packages.lst
@@ -0,0 +1,125 @@ +# 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 +llvm-devel +make + +# Install optional external build dependencies. +bzip2-devel +expat-devel +jsoncpp-devel +libarchive-devel +libcurl-devel +libuv-devel +libuv-devel +libzstd-devel +rhash-devel +xz-devel +zlib-devel + +# Install documentation tools. +python3-sphinx +python3-sphinxcontrib-qthelp +texinfo +qt5-qttools-devel +qt6-qttools-devel + +# Install lint tools. +clang-analyzer +codespell + +# Tools needed for the test suite. +findutils +file +jq +which + +# Install ASM_NASM language toolchain. +nasm + +# Packages needed to test CTest. +breezy +subversion +mercurial + +# Packages needed to test CPack. +rpm-build + +# Packages needed to test find modules. +alsa-lib-devel +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-11-openjdk-devel +jsoncpp-devel +lapack-devel +libarchive-devel +libcurl-devel +libicu-devel +libinput-devel systemd-devel +libjpeg-turbo-devel +libomp-devel +libpng-devel +opensp-devel +postgresql-server-devel +libtiff-devel +libuv-devel +libxml2-devel +libxslt-devel +mpich-devel +openal-soft-devel +openmpi-devel +patch +perl +protobuf-devel protobuf-c-devel protobuf-lite-devel +pypy2 pypy2-devel +pypy3 pypy3-devel +python2 python2-devel +python3 python3-devel python3-numpy +python3-jsmin python3-jsonschema +ruby rubygems ruby-devel +SDL-devel +sqlite-devel +swig +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/fedora39/install_deps.sh b/.gitlab/ci/docker/fedora40/install_deps.sh similarity index 100% rename from .gitlab/ci/docker/fedora39/install_deps.sh rename to .gitlab/ci/docker/fedora40/install_deps.sh
diff --git a/.gitlab/ci/docker/fedora40/install_iwyu.sh b/.gitlab/ci/docker/fedora40/install_iwyu.sh new file mode 100755 index 0000000..50ed612 --- /dev/null +++ b/.gitlab/ci/docker/fedora40/install_iwyu.sh
@@ -0,0 +1,42 @@ +#!/bin/sh + +set -e + +# Install development tools. +dnf install \ + --setopt=install_weak_deps=False \ + --setopt=fastestmirror=True \ + --setopt=max_parallel_downloads=10 \ + -y \ + $(grep '^[^#]\+$' /root/iwyu_packages.lst) + +cd /root +git clone "https://github.com/include-what-you-use/include-what-you-use.git" +cd include-what-you-use +readonly llvm_full_version="$( clang --version | head -n1 | cut -d' ' -f3 )" +readonly llvm_version="$( echo "$llvm_full_version" | cut -d. -f-1 )" +git checkout "clang_$llvm_version" +git apply <<EOF +diff --git a/iwyu_driver.cc b/iwyu_driver.cc +index dd4b046..cfd568a 100644 +--- a/iwyu_driver.cc ++++ b/iwyu_driver.cc +@@ -249,6 +249,7 @@ bool ExecuteAction(int argc, const char** argv, + /*CodeGenOpts=*/nullptr); + + Driver driver(path, getDefaultTargetTriple(), *diagnostics); ++ driver.ResourceDir = "/usr/lib64/clang/$llvm_full_version"; + driver.setTitle("include what you use"); + + // Expand out any response files passed on the command line +EOF +mkdir build +cd build + +cmake -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + "-DCMAKE_INSTALL_PREFIX=/usr/local/lib64/llvm-$llvm_version" \ + .. +ninja +DESTDIR=/root/iwyu-destdir ninja install +tar -C /root/iwyu-destdir -cf /root/iwyu.tar .
diff --git a/.gitlab/ci/docker/fedora39/install_rvm.sh b/.gitlab/ci/docker/fedora40/install_rvm.sh similarity index 100% rename from .gitlab/ci/docker/fedora39/install_rvm.sh rename to .gitlab/ci/docker/fedora40/install_rvm.sh
diff --git a/.gitlab/ci/docker/fedora39/iwyu_packages.lst b/.gitlab/ci/docker/fedora40/iwyu_packages.lst similarity index 100% rename from .gitlab/ci/docker/fedora39/iwyu_packages.lst rename to .gitlab/ci/docker/fedora40/iwyu_packages.lst
diff --git a/.gitlab/ci/docker/fedora39/rvm_packages.lst b/.gitlab/ci/docker/fedora40/rvm_packages.lst similarity index 100% rename from .gitlab/ci/docker/fedora39/rvm_packages.lst rename to .gitlab/ci/docker/fedora40/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 d8e8238..0000000 --- a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile +++ /dev/null
@@ -1,9 +0,0 @@ -FROM fedora:38 -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 15cfe39..0000000 --- a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh +++ /dev/null
@@ -1,25 +0,0 @@ -#!/bin/sh - -set -e - -readonly revision="29862e21f6d656eca59284c927d0c4c0698eb99c" # master as of 21 Sep 2023 -readonly tarball="git://gcc.gnu.org/git/gcc.git" - -readonly workdir="$HOME/gcc" -readonly srcdir="$workdir/gcc" -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-p1689" -make "-j$njobs" -make "-j$njobs" install-strip -rm -rf "$workdir"
diff --git a/.gitlab/ci/docker/ninja/centos7-aarch64.bash b/.gitlab/ci/docker/ninja/centos7-aarch64.bash deleted file mode 100755 index 4052f29..0000000 --- a/.gitlab/ci/docker/ninja/centos7-aarch64.bash +++ /dev/null
@@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -cleanup() { - docker container rm -fv "$build_container" >/dev/null 2>&1 || true - docker image rm -f "$build_image" >/dev/null 2>&1 || true -} - -readonly suffix="-$(date -u +%Y-%m-%d)-${RANDOM}" -readonly build_container="ninja-build-linux-aarch64$suffix" -readonly build_image="ninja:build-linux-aarch64$suffix" -readonly git_tag="${1-v1.11.0}" - -trap "cleanup" EXIT INT TERM - -docker image build --build-arg GIT_TAG="$git_tag" --tag="$build_image" "${BASH_SOURCE%/*}/centos7-aarch64" -docker container create --name "$build_container" "$build_image" -docker cp "$build_container:/ninja/ninja" "ninja"
diff --git a/.gitlab/ci/docker/ninja/centos7-aarch64/Dockerfile b/.gitlab/ci/docker/ninja/centos7-aarch64/Dockerfile deleted file mode 100644 index 3fb13b0..0000000 --- a/.gitlab/ci/docker/ninja/centos7-aarch64/Dockerfile +++ /dev/null
@@ -1,7 +0,0 @@ -FROM kitware/cmake:build-linux-aarch64-base-2020-12-21 -MAINTAINER Brad King <brad.king@kitware.com> - -ARG GIT_TAG=v1.11.0 - -COPY build_ninja.sh /root/build_ninja.sh -RUN scl enable devtoolset-7 -- sh /root/build_ninja.sh $GIT_TAG
diff --git a/.gitlab/ci/docker/ninja/centos7-aarch64/build_ninja.sh b/.gitlab/ci/docker/ninja/centos7-aarch64/build_ninja.sh deleted file mode 100755 index 91fb2d4..0000000 --- a/.gitlab/ci/docker/ninja/centos7-aarch64/build_ninja.sh +++ /dev/null
@@ -1,11 +0,0 @@ -#!/bin/sh - -set -e - -git clone https://github.com/ninja-build/ninja.git -cd ninja -git checkout "${1-v1.11.0}" -./configure.py --bootstrap -./ninja all -./ninja_test -strip ninja
diff --git a/.gitlab/ci/env.ps1 b/.gitlab/ci/env.ps1 old mode 100755 new mode 100644 index 72a8cb8..754521b --- a/.gitlab/ci/env.ps1 +++ b/.gitlab/ci/env.ps1
@@ -1,4 +1,12 @@ $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName + +# Place temporary files inside job directory. +$tmp = New-Item -Force -ItemType Directory -Path "$pwd\.gitlab\tmp" +$tmp = (New-Object -ComObject Scripting.FileSystemObject).GetFolder("$tmp").ShortPath +Set-Item -Force -Path "env:TEMP" -Value "$tmp" +Set-Item -Force -Path "env:TMP" -Value "$tmp" +$tmp = $null + if (Test-Path -Path ".gitlab/ci/env_$env:CMAKE_CONFIGURATION.ps1" -PathType Leaf) { . ".gitlab/ci/env_$env:CMAKE_CONFIGURATION.ps1" }
diff --git a/.gitlab/ci/env_cuda12.2_clang.sh b/.gitlab/ci/env_cuda12.2_clang.sh new file mode 100644 index 0000000..4b71b42 --- /dev/null +++ b/.gitlab/ci/env_cuda12.2_clang.sh
@@ -0,0 +1,3 @@ +export CC=/usr/bin/clang-18 +export CXX=/usr/bin/clang++-18 +export CUDACXX=/usr/bin/clang++-18
diff --git a/.gitlab/ci/env_fedora39_common_clang.sh b/.gitlab/ci/env_fedora39_common_clang.sh deleted file mode 100644 index 076669b..0000000 --- a/.gitlab/ci/env_fedora39_common_clang.sh +++ /dev/null
@@ -1,3 +0,0 @@ -export CC=/usr/bin/clang-17 -export CXX=/usr/bin/clang++-17 -export FC=/usr/bin/flang-new
diff --git a/.gitlab/ci/env_fedora39_makefiles_clang.sh b/.gitlab/ci/env_fedora39_makefiles_clang.sh deleted file mode 100644 index 6200f82..0000000 --- a/.gitlab/ci/env_fedora39_makefiles_clang.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora39_common_clang.sh
diff --git a/.gitlab/ci/env_fedora39_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora39_makefiles_symlinked.cmake deleted file mode 100644 index 052e9a7..0000000 --- a/.gitlab/ci/env_fedora39_makefiles_symlinked.cmake +++ /dev/null
@@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/env_fedora39_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora39_makefiles_symlinked.sh b/.gitlab/ci/env_fedora39_makefiles_symlinked.sh deleted file mode 100644 index 39ac189..0000000 --- a/.gitlab/ci/env_fedora39_makefiles_symlinked.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora39_makefiles.sh
diff --git a/.gitlab/ci/env_fedora39_ninja_clang.sh b/.gitlab/ci/env_fedora39_ninja_clang.sh deleted file mode 100644 index 6200f82..0000000 --- a/.gitlab/ci/env_fedora39_ninja_clang.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora39_common_clang.sh
diff --git a/.gitlab/ci/env_fedora39_ninja_multi_clang.sh b/.gitlab/ci/env_fedora39_ninja_multi_clang.sh deleted file mode 100644 index 6200f82..0000000 --- a/.gitlab/ci/env_fedora39_ninja_multi_clang.sh +++ /dev/null
@@ -1 +0,0 @@ -. .gitlab/ci/env_fedora39_common_clang.sh
diff --git a/.gitlab/ci/env_fedora39_asan.sh b/.gitlab/ci/env_fedora40_asan.sh similarity index 100% rename from .gitlab/ci/env_fedora39_asan.sh rename to .gitlab/ci/env_fedora40_asan.sh
diff --git a/.gitlab/ci/env_fedora39_clang_analyzer.sh b/.gitlab/ci/env_fedora40_clang_analyzer.sh similarity index 100% rename from .gitlab/ci/env_fedora39_clang_analyzer.sh rename to .gitlab/ci/env_fedora40_clang_analyzer.sh
diff --git a/.gitlab/ci/env_fedora40_common_clang.sh b/.gitlab/ci/env_fedora40_common_clang.sh new file mode 100644 index 0000000..a3861d0 --- /dev/null +++ b/.gitlab/ci/env_fedora40_common_clang.sh
@@ -0,0 +1,3 @@ +export CC=/usr/bin/clang-18 +export CXX=/usr/bin/clang++-18 +export FC=/usr/bin/flang-new
diff --git a/.gitlab/ci/env_fedora39_extdeps.sh b/.gitlab/ci/env_fedora40_extdeps.sh similarity index 100% rename from .gitlab/ci/env_fedora39_extdeps.sh rename to .gitlab/ci/env_fedora40_extdeps.sh
diff --git a/.gitlab/ci/env_fedora39_makefiles.cmake b/.gitlab/ci/env_fedora40_makefiles.cmake similarity index 100% rename from .gitlab/ci/env_fedora39_makefiles.cmake rename to .gitlab/ci/env_fedora40_makefiles.cmake
diff --git a/.gitlab/ci/env_fedora39_makefiles.sh b/.gitlab/ci/env_fedora40_makefiles.sh similarity index 100% rename from .gitlab/ci/env_fedora39_makefiles.sh rename to .gitlab/ci/env_fedora40_makefiles.sh
diff --git a/.gitlab/ci/env_fedora40_makefiles_clang.sh b/.gitlab/ci/env_fedora40_makefiles_clang.sh new file mode 100644 index 0000000..2021086 --- /dev/null +++ b/.gitlab/ci/env_fedora40_makefiles_clang.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora40_common_clang.sh
diff --git a/.gitlab/ci/env_fedora40_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora40_makefiles_symlinked.cmake new file mode 100644 index 0000000..d381807 --- /dev/null +++ b/.gitlab/ci/env_fedora40_makefiles_symlinked.cmake
@@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/env_fedora40_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora40_makefiles_symlinked.sh b/.gitlab/ci/env_fedora40_makefiles_symlinked.sh new file mode 100644 index 0000000..5a7584a --- /dev/null +++ b/.gitlab/ci/env_fedora40_makefiles_symlinked.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora40_makefiles.sh
diff --git a/.gitlab/ci/env_fedora39_ninja.sh b/.gitlab/ci/env_fedora40_ninja.sh similarity index 100% rename from .gitlab/ci/env_fedora39_ninja.sh rename to .gitlab/ci/env_fedora40_ninja.sh
diff --git a/.gitlab/ci/env_fedora40_ninja_clang.sh b/.gitlab/ci/env_fedora40_ninja_clang.sh new file mode 100644 index 0000000..2021086 --- /dev/null +++ b/.gitlab/ci/env_fedora40_ninja_clang.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora40_common_clang.sh
diff --git a/.gitlab/ci/env_fedora39_ninja_multi.sh b/.gitlab/ci/env_fedora40_ninja_multi.sh similarity index 100% rename from .gitlab/ci/env_fedora39_ninja_multi.sh rename to .gitlab/ci/env_fedora40_ninja_multi.sh
diff --git a/.gitlab/ci/env_fedora40_ninja_multi_clang.sh b/.gitlab/ci/env_fedora40_ninja_multi_clang.sh new file mode 100644 index 0000000..2021086 --- /dev/null +++ b/.gitlab/ci/env_fedora40_ninja_multi_clang.sh
@@ -0,0 +1 @@ +. .gitlab/ci/env_fedora40_common_clang.sh
diff --git a/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 b/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 b/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_arm64_package.ps1 b/.gitlab/ci/env_windows_arm64_package.ps1 new file mode 100644 index 0000000..74c9c2b --- /dev/null +++ b/.gitlab/ci/env_windows_arm64_package.ps1
@@ -0,0 +1 @@ +. .gitlab/ci/wix4-env.ps1
diff --git a/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1 b/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1 new file mode 100644 index 0000000..eb7bf6e --- /dev/null +++ b/.gitlab/ci/env_windows_arm64_vs2022_ninja.ps1
@@ -0,0 +1,2 @@ +& "$pwsh" -File .gitlab/ci/wix3.ps1 +& "$pwsh" -File .gitlab/ci/wix4.ps1
diff --git a/.gitlab/ci/env_windows_borland5.5.ps1 b/.gitlab/ci/env_windows_borland5.5.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_borland5.8.ps1 b/.gitlab/ci/env_windows_borland5.8.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_clang_ninja.ps1 b/.gitlab/ci/env_windows_clang_ninja.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_clang_nmake.ps1 b/.gitlab/ci/env_windows_clang_nmake.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_i386_package.ps1 b/.gitlab/ci/env_windows_i386_package.ps1 new file mode 100644 index 0000000..74c9c2b --- /dev/null +++ b/.gitlab/ci/env_windows_i386_package.ps1
@@ -0,0 +1 @@ +. .gitlab/ci/wix4-env.ps1
diff --git a/.gitlab/ci/env_windows_intelclassic_ninja.ps1 b/.gitlab/ci/env_windows_intelclassic_ninja.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_inteloneapi_ninja.ps1 b/.gitlab/ci/env_windows_inteloneapi_ninja.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_msvc_v71_nmake.ps1 b/.gitlab/ci/env_windows_msvc_v71_nmake.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_openwatcom1.9.ps1 b/.gitlab/ci/env_windows_openwatcom1.9.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_orangec6.73.1.ps1 b/.gitlab/ci/env_windows_orangec6.73.1.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_vs2022_x64.ps1 b/.gitlab/ci/env_windows_vs2022_x64.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_vs2022_x64_jom.ps1 b/.gitlab/ci/env_windows_vs2022_x64_jom.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 old mode 100755 new mode 100644 index 50a03ca..fe010eb --- a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 +++ b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
@@ -1,4 +1,9 @@ if ("$env:CMAKE_CI_NIGHTLY" -eq "true") { . ".gitlab/ci/innosetup-env.ps1" . ".gitlab/ci/ispc-env.ps1" + . ".gitlab/ci/nuget-env.ps1" + . ".gitlab/ci/swift-env.ps1" } + +& "$pwsh" -File .gitlab/ci/wix3.ps1 +& "$pwsh" -File .gitlab/ci/wix4.ps1
diff --git a/.gitlab/ci/env_windows_vs2022_x64_ninja_multi.ps1 b/.gitlab/ci/env_windows_vs2022_x64_ninja_multi.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1 b/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/env_windows_x86_64_package.ps1 b/.gitlab/ci/env_windows_x86_64_package.ps1 new file mode 100644 index 0000000..74c9c2b --- /dev/null +++ b/.gitlab/ci/env_windows_x86_64_package.ps1
@@ -0,0 +1 @@ +. .gitlab/ci/wix4-env.ps1
diff --git a/.gitlab/ci/innosetup-env.ps1 b/.gitlab/ci/innosetup-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/innosetup.ps1 b/.gitlab/ci/innosetup.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/intel-env.ps1 b/.gitlab/ci/intel-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/intel-vars.ps1 b/.gitlab/ci/intel-vars.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/intel.ps1 b/.gitlab/ci/intel.ps1 old mode 100755 new mode 100644 index 2262669..171e6a7 --- a/.gitlab/ci/intel.ps1 +++ b/.gitlab/ci/intel.ps1
@@ -1,8 +1,19 @@ $erroractionpreference = "stop" -if ("$env:CMAKE_CI_BUILD_NAME" -match "(^|_)(oneapi2023\.1\.0|intel2021\.9\.0)(_|$)") { +if ("$env:CMAKE_CI_BUILD_NAME" -match "(^|_)(oneapi2024\.1\.0)(_|$)") { + # Intel oneAPI 2024.1.0 + $version = "2024.1.0" + $version_dir = "2024.1" + $bin_dir = "bin" + $llvm_dir = "bin\compiler" + $filename = "intel-oneapi-$version-windows-1" + $sha256sum = "CB6857C08CD815722913358EC495AA575C2EED730646098A6CF7178E55FA7323" +} elseif ("$env:CMAKE_CI_BUILD_NAME" -match "(^|_)(intel2021\.9\.0)(_|$)") { # Intel oneAPI 2023.1.0 $version = "2023.1.0" + $version_dir = "2023.1.0" + $bin_dir = "windows\bin" + $llvm_dir = "windows\bin-llvm" $filename = "intel-oneapi-$version-windows-1" $sha256sum = "5AFCA9E0B03894565209B1295476163ABEBB1F1388E0F3EF5B4D0F9189E65BDC" } else { @@ -26,12 +37,12 @@ Remove-Item "$outdir\$tarball" $compiler = "$outdir\intel\compiler" -$bin = "$compiler\$version\windows\bin" -$null = New-Item -ItemType Junction -Path "$compiler\latest" -Target "$compiler\$version" +$bin = "$compiler\$version_dir\$bin_dir" +$null = New-Item -ItemType Junction -Path "$compiler\latest" -Target "$compiler\$version_dir" $null = New-Item -ItemType HardLink -Path "$bin\icx-cl.exe" -Target "$bin\icx.exe" $null = New-Item -ItemType HardLink -Path "$bin\icx-cc.exe" -Target "$bin\icx.exe" $null = New-Item -ItemType HardLink -Path "$bin\icpx.exe" -Target "$bin\icx.exe" -$bin = "$compiler\$version\windows\bin-llvm" +$bin = "$compiler\$version_dir\$llvm_dir" $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\clang++.exe" -Target "$bin\clang.exe" @@ -40,3 +51,8 @@ $null = New-Item -ItemType HardLink -Path "$bin\llvm-lib.exe" -Target "$bin\llvm-ar.exe" Clear-Variable -Name bin Clear-Variable -Name compiler + +Clear-Variable -Name llvm_dir +Clear-Variable -Name bin_dir +Clear-Variable -Name version_dir +Clear-Variable -Name version
diff --git a/.gitlab/ci/ispc-env.ps1 b/.gitlab/ci/ispc-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/ispc.ps1 b/.gitlab/ci/ispc.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/jom.ps1 b/.gitlab/ci/jom.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/mingw-env.ps1 b/.gitlab/ci/mingw-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/mingw.ps1 b/.gitlab/ci/mingw.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/msvc.ps1 b/.gitlab/ci/msvc.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/ninja-env.ps1 b/.gitlab/ci/ninja-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/ninja-env.sh b/.gitlab/ci/ninja-env.sh new file mode 100644 index 0000000..744b9f3 --- /dev/null +++ b/.gitlab/ci/ninja-env.sh
@@ -0,0 +1,3 @@ +.gitlab/ci/ninja.sh +export PATH=$PWD/.gitlab:$PATH +ninja --version
diff --git a/.gitlab/ci/ninja-nightly.ps1 b/.gitlab/ci/ninja-nightly.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/ninja.ps1 b/.gitlab/ci/ninja.ps1 old mode 100755 new mode 100644 index 0af3b4f..70b372c --- a/.gitlab/ci/ninja.ps1 +++ b/.gitlab/ci/ninja.ps1
@@ -5,9 +5,18 @@ exit $LASTEXITCODE } -$version = "1.11.0" -$sha256sum = "D0EE3DA143211AA447E750085876C9B9D7BCDD637AB5B2C5B41349C617F22F3B" -$filename = "ninja-win" +$version = "1.12.1" + +if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { + $sha256sum = "F550FEC705B6D6FF58F2DB3C374C2277A37691678D6ABA463ADCBB129108467A" + $filename = "ninja-win" +} elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { + $sha256sum = "79C96A50E0DEAFEC212CFA85AA57C6B74003F52D9D1673DDCD1EAB1C958C5900" + $filename = "ninja-winarm64" +} else { + throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE") +} + $tarball = "$filename.zip" $outdir = $pwd.Path
diff --git a/.gitlab/ci/ninja.sh b/.gitlab/ci/ninja.sh index ce39b46..02d1fa5 100755 --- a/.gitlab/ci/ninja.sh +++ b/.gitlab/ci/ninja.sh
@@ -6,25 +6,23 @@ exec .gitlab/ci/ninja-nightly.sh fi -readonly version="1.11.0" +readonly version="1.12.1" baseurl="https://github.com/ninja-build/ninja/releases/download/v$version" case "$(uname -s)-$(uname -m)" in Linux-x86_64) shatool="sha256sum" - sha256sum="9726e730d5b8599f82654dc80265e64a10a8a817552c34153361ed0c017f9f02" + sha256sum="6f98805688d19672bd699fbbfa2c2cf0fc054ac3df1f0e6a47664d963d530255" filename="ninja-linux" ;; Linux-aarch64) shatool="sha256sum" - sha256sum="b002eb77cfcef6d329cccf8b1cc7ad138302d6e19b5b76b10b4c4d38564b47b5" - # Use binary built by adjacent 'docker/ninja/centos7-aarch64.bash' script. - baseurl="https://cmake.org/files/dependencies" - filename="ninja-$version-1-linux-aarch64" + sha256sum="5c25c6570b0155e95fce5918cb95f1ad9870df5768653afe128db822301a05a1" + filename="ninja-linux-aarch64" ;; Darwin-*) shatool="shasum -a 256" - sha256sum="21915277db59756bfc61f6f281c1f5e3897760b63776fd3d360f77dd7364137f" + sha256sum="89a287444b5b3e98f88a945afa50ce937b8ffd1dcc59c555ad9b1baf855298c9" filename="ninja-mac" ;; *)
diff --git a/.gitlab/ci/nuget-env.ps1 b/.gitlab/ci/nuget-env.ps1 new file mode 100644 index 0000000..7dee5a0 --- /dev/null +++ b/.gitlab/ci/nuget-env.ps1
@@ -0,0 +1,4 @@ +$pwdpath = $pwd.Path +& "$pwsh" -File ".gitlab/ci/nuget.ps1" +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\nuget;$env:PATH" +nuget | Select -First 1
diff --git a/.gitlab/ci/nuget.ps1 b/.gitlab/ci/nuget.ps1 new file mode 100644 index 0000000..1decb01 --- /dev/null +++ b/.gitlab/ci/nuget.ps1
@@ -0,0 +1,21 @@ +$erroractionpreference = "stop" + +$version = "6.9.1.3" +$sha256sum = "562A2CE2D570D68DB4472CB82CDF1FC4245D5C73B84BC8361880CBE389702F65" +$filename = "nuget-$version-win-i386-1" +$tarball = "$filename.zip" + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +$ProgressPreference = 'SilentlyContinue' +# This URL is only visible inside of Kitware's network. +Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/internal/$tarball" -OutFile "$outdir\$tarball" +$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir") +Move-Item -Path "$outdir\$filename" -Destination "$outdir\nuget" +Remove-Item "$outdir\$tarball"
diff --git a/.gitlab/ci/openwatcom-env.ps1 b/.gitlab/ci/openwatcom-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/openwatcom.ps1 b/.gitlab/ci/openwatcom.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/orangec-env.ps1 b/.gitlab/ci/orangec-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/orangec.ps1 b/.gitlab/ci/orangec.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/package_windows.ps1 b/.gitlab/ci/package_windows.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/post_build.ps1 b/.gitlab/ci/post_build.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/post_build_fedora39_tidy.sh b/.gitlab/ci/post_build_fedora40_tidy.sh similarity index 100% rename from .gitlab/ci/post_build_fedora39_tidy.sh rename to .gitlab/ci/post_build_fedora40_tidy.sh
diff --git a/.gitlab/ci/post_build_windows_arm64_package.ps1 b/.gitlab/ci/post_build_windows_arm64_package.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/post_build_windows_i386_package.ps1 b/.gitlab/ci/post_build_windows_i386_package.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/post_build_windows_x86_64_package.ps1 b/.gitlab/ci/post_build_windows_x86_64_package.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/pre_build.ps1 b/.gitlab/ci/pre_build.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/pre_build_fedora39_tidy.sh b/.gitlab/ci/pre_build_fedora40_tidy.sh similarity index 100% rename from .gitlab/ci/pre_build_fedora39_tidy.sh rename to .gitlab/ci/pre_build_fedora40_tidy.sh
diff --git a/.gitlab/ci/python-env.ps1 b/.gitlab/ci/python-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/python.ps1 b/.gitlab/ci/python.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/qt-env.ps1 b/.gitlab/ci/qt-env.ps1 old mode 100755 new mode 100644
diff --git a/.gitlab/ci/repackage/wix.ps1 b/.gitlab/ci/repackage/wix.ps1 new file mode 100644 index 0000000..6dbd466 --- /dev/null +++ b/.gitlab/ci/repackage/wix.ps1
@@ -0,0 +1,55 @@ +# WiX Toolset 4+ is provided only via nuget packages. +# Download the package artifacts, extract the parts we need, and repackage them. + +param ( + [Parameter(Mandatory=$true)] + [string]$version + ) + +$erroractionpreference = "stop" + +$version_major = $version.Substring(0, $version.IndexOf('.')) + +$release = "v" + $version +$pkg_wix = "wix.$version.nupkg" +$pkg_wixui = "WixToolset.UI.wixext.$version.nupkg" +$packages = $pkg_wix, $pkg_wixui + +$wix_artifacts = "wix-artifacts.zip" + +$ProgressPreference = 'SilentlyContinue' +Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix/releases/download/$release/artifacts.zip" -OutFile "$wix_artifacts" + +Add-Type -AssemblyName System.IO.Compression.FileSystem + +$zip = [System.IO.Compression.ZipFile]::Open("$wix_artifacts", "read") +$zip.Entries | Where-Object FullName -in $packages | ForEach-Object { + [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$($_.Name)", $true) +} +$zip.Dispose() +Remove-Item "$wix_artifacts" + +$wix_dir = "wix-$version-win-any-1" +[System.IO.Compression.ZipFile]::ExtractToDirectory($pkg_wix, "wix-tmp") +Move-Item -Path "wix-tmp/tools/net6.0/any" -Destination "$wix_dir" +Remove-Item "wix-tmp" -Recurse -Force +Remove-Item "$pkg_wix" + +$ext_dir = New-Item -Force -ItemType Directory -Path "$wix_dir/.wix/extensions/WixToolset.UI.wixext/$version/wixext$version_major" +$zip = [System.IO.Compression.ZipFile]::Open($pkg_wixui, "read") +$zip.Entries | Where-Object Name -eq "WixToolset.UI.wixext.dll" | ForEach-Object { + [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, (Join-Path $ext_dir $_.Name), $true) +} +$zip.Dispose() +Remove-Item "$pkg_wixui" + +@" +This was extracted from WiX Toolset nuget packages and repackaged. +Point both PATH and WIX_EXTENSIONS environment variables at this directory. + +"@ | Add-Content -NoNewline "$wix_dir/README.txt" + +$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal +$includeBaseDirectory = $true +[System.IO.Compression.ZipFile]::CreateFromDirectory("$wix_dir", "$wix_dir.zip", $compressionLevel, $includeBaseDirectory) +Remove-Item "$wix_dir" -Recurse -Force
diff --git a/.gitlab/ci/sccache-env.ps1 b/.gitlab/ci/sccache-env.ps1 new file mode 100644 index 0000000..66dc6eb --- /dev/null +++ b/.gitlab/ci/sccache-env.ps1
@@ -0,0 +1 @@ +Set-Item -Force -Path "env:PATH" -Value "$env:PATH;$env:SCCACHE_PATH"
diff --git a/.gitlab/ci/sccache-env.sh b/.gitlab/ci/sccache-env.sh new file mode 100644 index 0000000..4b170a4 --- /dev/null +++ b/.gitlab/ci/sccache-env.sh
@@ -0,0 +1,2 @@ +.gitlab/ci/sccache.sh +export PATH="$PWD/.gitlab:$PATH"
diff --git a/.gitlab/ci/swift-env.ps1 b/.gitlab/ci/swift-env.ps1 new file mode 100644 index 0000000..871b31c --- /dev/null +++ b/.gitlab/ci/swift-env.ps1
@@ -0,0 +1,6 @@ +$pwdpath = $pwd.Path +& "$pwsh" -File ".gitlab/ci/swift.ps1" +Set-Item -Force -Path "env:DEVELOPER_DIR" -Value "$pwdpath\.gitlab\swift" +Set-Item -Force -Path "env:SDKROOT" -Value "$pwdpath\.gitlab\swift\Platforms\Windows.platform\Developer\SDKs\Windows.sdk" +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\swift\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin;$env:PATH" +swiftc --version
diff --git a/.gitlab/ci/swift.ps1 b/.gitlab/ci/swift.ps1 new file mode 100644 index 0000000..b970dce --- /dev/null +++ b/.gitlab/ci/swift.ps1
@@ -0,0 +1,38 @@ +$erroractionpreference = "stop" + +$version = "5.9.2" +$sha256sum = "8C053108528EB2DAD84C33D6F0834A3A1444D21BA1A89D591AB149304A62F6B5" +$filename = "swift-$version-win-x86_64-1" +$tarball = "$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/$tarball" -OutFile "$outdir\$tarball" +$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir") +Move-Item -Path "$outdir\$filename" -Destination "$outdir\swift" +Remove-Item "$outdir\$tarball" + +$bin = "$outdir\swift\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin" +$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\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" +$null = New-Item -ItemType HardLink -Path "$bin\llvm-dlltool.exe" -Target "$bin\llvm-ar.exe" +$null = New-Item -ItemType HardLink -Path "$bin\llvm-lib.exe" -Target "$bin\llvm-ar.exe" +$null = New-Item -ItemType HardLink -Path "$bin\llvm-ranlib.exe" -Target "$bin\llvm-ar.exe" +$null = New-Item -ItemType HardLink -Path "$bin\llvm-objcopy.exe" -Target "$bin\llvm-strip.exe" +$null = New-Item -ItemType HardLink -Path "$bin\swiftc.exe" -Target "$bin\swift.exe" +$null = New-Item -ItemType HardLink -Path "$bin\swift-api-digester.exe" -Target "$bin\swift-frontend.exe" +$null = New-Item -ItemType HardLink -Path "$bin\swift-autolink-extract.exe" -Target "$bin\swift-frontend.exe" +$null = New-Item -ItemType HardLink -Path "$bin\swift-symbolgraph-extract.exe" -Target "$bin\swift-frontend.exe" +Clear-Variable -Name bin
diff --git a/.gitlab/ci/vcvarsall.ps1 b/.gitlab/ci/vcvarsall.ps1 old mode 100755 new mode 100644 index f91b100..f8c4150 --- a/.gitlab/ci/vcvarsall.ps1 +++ b/.gitlab/ci/vcvarsall.ps1
@@ -1,9 +1,7 @@ $erroractionpreference = "stop" -cmd /c "`"$env:VCVARSALL`" $env:VCVARSPLATFORM -vcvars_ver=$env:VCVARSVERSION & set" | -foreach { - if ($_ -match "=") { - $v = $_.split("=") - [Environment]::SetEnvironmentVariable($v[0], $v[1]) - } +$all_env = cmd /c "`"$env:VCVARSALL`" $env:VCVARSPLATFORM -vcvars_ver=$env:VCVARSVERSION >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/wix.ps1 b/.gitlab/ci/wix.ps1 deleted file mode 100755 index 9313c0f..0000000 --- a/.gitlab/ci/wix.ps1 +++ /dev/null
@@ -1,20 +0,0 @@ -$erroractionpreference = "stop" - -$release = "wix314rtm" -$sha256sum = "13F067F38969FAF163D93A804B48EA0576790A202C8F10291F2000F0E356E934" -#$filename = "wix314-binaries" -$filename = "wix-3.14.0.8606-win-i386" -$tarball = "$filename.zip" - -$outdir = $pwd.Path -$outdir = "$outdir\.gitlab" -$ProgressPreference = 'SilentlyContinue' -#Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/$release/$tarball" -OutFile "$outdir\$tarball" -Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/$tarball" -OutFile "$outdir\$tarball" -$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 -if ($hash.Hash -ne $sha256sum) { - exit 1 -} - -Add-Type -AssemblyName System.IO.Compression.FileSystem -[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir\wix\bin")
diff --git a/.gitlab/ci/wix3.ps1 b/.gitlab/ci/wix3.ps1 new file mode 100644 index 0000000..8f5ae4b --- /dev/null +++ b/.gitlab/ci/wix3.ps1
@@ -0,0 +1,21 @@ +$erroractionpreference = "stop" + +$release = "wix314rtm" +$sha256sum = "13F067F38969FAF163D93A804B48EA0576790A202C8F10291F2000F0E356E934" +#$filename = "wix314-binaries" +$filename = "wix-3.14.0.8606-win-i386" +$tarball = "$filename.zip" + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +$ProgressPreference = 'SilentlyContinue' +#Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/$release/$tarball" -OutFile "$outdir\$tarball" +Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/$tarball" -OutFile "$outdir\$tarball" +$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir\wix3") +Remove-Item "$outdir\$tarball"
diff --git a/.gitlab/ci/wix4-env.ps1 b/.gitlab/ci/wix4-env.ps1 new file mode 100644 index 0000000..f1fe8b6 --- /dev/null +++ b/.gitlab/ci/wix4-env.ps1
@@ -0,0 +1,7 @@ +& "$pwsh" -File .gitlab/ci/wix4.ps1 + +$pwdpath = $pwd.Path +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\wix4;$env:PATH" +Set-Item -Force -Path "env:WIX_EXTENSIONS" -Value "$pwdpath\.gitlab\wix4" +Write-Host "wix version: $(wix --version)" +Write-Host "wix extensions: $(wix extension list -g)"
diff --git a/.gitlab/ci/wix4.ps1 b/.gitlab/ci/wix4.ps1 new file mode 100644 index 0000000..6209f2b --- /dev/null +++ b/.gitlab/ci/wix4.ps1
@@ -0,0 +1,20 @@ +$erroractionpreference = "stop" + +$version = "4.0.4" +$sha256sum = "FB6E94C89B12FB65D3AA0CF9E9C630DAFCC7D57F1E66C7D6035CAD37A38CC284" +$filename = "wix-$version-win-any-1" +$tarball = "$filename.zip" + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +$ProgressPreference = 'SilentlyContinue' +Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/$tarball" -OutFile "$outdir\$tarball" +$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir") +Move-Item -Path "$outdir\$filename" -Destination "$outdir\wix4" +Remove-Item "$outdir\$tarball"
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index e0364a5..8685f02 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml
@@ -5,7 +5,7 @@ ### Release .linux_prep_source: - image: "fedora:39" + image: "fedora:40" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" @@ -31,21 +31,17 @@ BOOTSTRAP_ARGS: '-- "-DCMake_DOC_ARTIFACT_PREFIX=$CI_PROJECT_DIR/build/install-doc"' .needs_centos7_x86_64: - dependencies: - - b:centos7-x86_64 needs: - b:centos7-x86_64 .needs_centos7_aarch64: - dependencies: - - b:centos7-aarch64 needs: - b:centos7-aarch64 ### Debian .debian12: - image: "kitware/cmake:ci-debian12-x86_64-2023-07-27" + image: "kitware/cmake:ci-debian12-x86_64-2024-03-04" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" @@ -68,45 +64,53 @@ ### Fedora -.fedora39: - image: "kitware/cmake:ci-fedora39-x86_64-2023-11-16" +.fedora40: + image: "kitware/cmake:ci-fedora40-x86_64-2024-04-24" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes" CMAKE_ARCH: x86_64 +# FIXME(#25932): Our HIP tests do not fully work in CI with Fedora 40. +.fedora39_hip: + image: "kitware/cmake:ci-fedora39-hip-x86_64-2024-04-24" + + variables: + GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" + CMAKE_ARCH: x86_64 + #### Lint builds -.fedora39_tidy: - extends: .fedora39 +.fedora40_tidy: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_tidy + CMAKE_CONFIGURATION: fedora40_tidy CMAKE_CI_NO_INSTALL: 1 -.fedora39_clang_analyzer: - extends: .fedora39 +.fedora40_clang_analyzer: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_clang_analyzer + CMAKE_CONFIGURATION: fedora40_clang_analyzer CMAKE_CI_BUILD_TYPE: Debug CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_CI_NO_INSTALL: 1 -.fedora39_sphinx: - extends: .fedora39 +.fedora40_sphinx: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_sphinx + CMAKE_CONFIGURATION: fedora40_sphinx CTEST_NO_WARNINGS_ALLOWED: 1 CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx" CMAKE_CI_NO_INSTALL: 1 -.fedora39_sphinx_package: - extends: .fedora39 +.fedora40_sphinx_package: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_sphinx_package + CMAKE_CONFIGURATION: fedora40_sphinx_package CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx" #### Build and test @@ -162,43 +166,43 @@ CMAKE_CI_BUILD_TYPE: Release CTEST_NO_WARNINGS_ALLOWED: 1 -.fedora39_extdeps: - extends: .fedora39 +.fedora40_extdeps: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_extdeps + CMAKE_CONFIGURATION: fedora40_extdeps CMAKE_CI_BUILD_TYPE: Release CTEST_NO_WARNINGS_ALLOWED: 1 -.fedora39_ninja: - extends: .fedora39 +.fedora40_ninja: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_ninja + CMAKE_CONFIGURATION: fedora40_ninja CMAKE_CI_BUILD_TYPE: Release CTEST_NO_WARNINGS_ALLOWED: 1 -.fedora39_ninja_multi: - extends: .fedora39 +.fedora40_ninja_multi: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_ninja_multi + CMAKE_CONFIGURATION: fedora40_ninja_multi CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "Ninja Multi-Config" -.fedora39_makefiles: - extends: .fedora39 +.fedora40_makefiles: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_makefiles + CMAKE_CONFIGURATION: fedora40_makefiles CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "Unix Makefiles" -.fedora39_makefiles_symlinked: - extends: .fedora39 +.fedora40_makefiles_symlinked: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_makefiles_symlinked + CMAKE_CONFIGURATION: fedora40_makefiles_symlinked CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "Unix Makefiles" CMAKE_CI_IN_SYMLINK_TREE: 1 @@ -219,24 +223,24 @@ variables: CMAKE_CONFIGURATION: debian12_ninja_clang -.fedora39_makefiles_clang: - extends: .fedora39 +.fedora40_makefiles_clang: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_makefiles_clang + CMAKE_CONFIGURATION: fedora40_makefiles_clang CMAKE_GENERATOR: "Unix Makefiles" -.fedora39_ninja_clang: - extends: .fedora39 +.fedora40_ninja_clang: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_ninja_clang + CMAKE_CONFIGURATION: fedora40_ninja_clang -.fedora39_ninja_multi_clang: - extends: .fedora39 +.fedora40_ninja_multi_clang: + extends: .fedora40 variables: - CMAKE_CONFIGURATION: fedora39_ninja_multi_clang + CMAKE_CONFIGURATION: fedora40_ninja_multi_clang CMAKE_GENERATOR: "Ninja Multi-Config" ### Sanitizers @@ -252,13 +256,13 @@ CTEST_MEMORYCHECK_TYPE: AddressSanitizer CTEST_MEMORYCHECK_SANITIZER_OPTIONS: "" -.fedora39_asan: +.fedora40_asan: extends: - - .fedora39 + - .fedora40 - .fedora_asan_addon variables: - CMAKE_CONFIGURATION: fedora39_asan + CMAKE_CONFIGURATION: fedora40_asan ### Intel Compiler @@ -367,6 +371,24 @@ CMAKE_CONFIGURATION: cuda11.8_splayed_nvidia CTEST_NO_WARNINGS_ALLOWED: 1 +.cuda12.2: + extends: .cuda + image: "kitware/cmake:ci-cuda12.2-x86_64-2024-04-05" + variables: + CMAKE_ARCH: x86_64 + +.cuda12.2_nvidia: + extends: .cuda12.2 + variables: + CMAKE_CONFIGURATION: cuda12.2_nvidia + CTEST_NO_WARNINGS_ALLOWED: 1 + +.cuda12.2_clang: + extends: .cuda12.2 + variables: + CMAKE_CONFIGURATION: cuda12.2_clang + CTEST_NO_WARNINGS_ALLOWED: 1 + ### HIP builds .hip5.5: @@ -392,7 +414,7 @@ CTEST_LABELS: "HIP" .fedora39_hip_radeon: - extends: .fedora39 + extends: .fedora39_hip variables: CMAKE_CONFIGURATION: fedora39_hip_radeon @@ -405,30 +427,6 @@ CMAKE_CONFIGURATION: hip5.5_nvidia CTEST_LABELS: "HIP" -### C++ modules - -.gcc_cxx_modules_x86_64: - image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2023-09-21" - - variables: - GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" - CMAKE_ARCH: x86_64 - CC: "/opt/gcc-p1689/bin/gcc" - CXX: "/opt/gcc-p1689/bin/g++" - -.gcc_cxx_modules_ninja: - extends: .gcc_cxx_modules_x86_64 - - variables: - CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja - -.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: @@ -493,12 +491,8 @@ .before_script_linux: &before_script_linux - source .gitlab/ci/env.sh - - .gitlab/ci/cmake.sh - - export PATH=$PWD/.gitlab/cmake/bin:$PATH - - .gitlab/ci/ninja.sh - - export PATH=$PWD/.gitlab:$PATH - - cmake --version - - ninja --version + - source .gitlab/ci/cmake-env.sh + - source .gitlab/ci/ninja-env.sh .cmake_prep_source_linux: stage: prep @@ -508,8 +502,8 @@ - dnf install --setopt=install_weak_deps=False -y git-core - v="$(.gitlab/ci/cmake_version.sh)" - mkdir -p build/ - - git archive --format=tgz "--prefix=cmake-$v/" -o "build/cmake-$v.tar.gz" HEAD - - git -c core.autocrlf=true -c core.eol=crlf archive --format=zip --prefix="cmake-$v/" -o "build/cmake-$v.zip" HEAD + - git archive --format=tgz --prefix="cmake-$v/" -o "build/cmake-$v.tar.gz" HEAD + - git archive --format=zip --prefix="cmake-$v/" -o "build/cmake-$v.zip" HEAD interruptible: true @@ -525,7 +519,7 @@ .cmake_codespell_linux: stage: build - extends: .fedora39 + extends: .fedora40 script: - .gitlab/ci/codespell.sh interruptible: true @@ -573,8 +567,7 @@ - mkdir -p build/ - cp Utilities/Release/linux/$CMAKE_ARCH/cache.txt build/CMakeCache.txt # Make sccache available. - - .gitlab/ci/sccache.sh - - export PATH=$PWD/.gitlab:$PATH + - source .gitlab/ci/sccache-env.sh # Append sccache settings to the cache. - echo "CMAKE_C_COMPILER_LAUNCHER:STRING=sccache" >> build/CMakeCache.txt - echo "CMAKE_CXX_COMPILER_LAUNCHER:STRING=sccache" >> build/CMakeCache.txt @@ -669,7 +662,7 @@ .cmake_org_help: stage: build extends: - - .fedora39 + - .fedora40 - .linux_x86_64_tags - .cmake_org_help_artifacts script:
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 23c68d5..ccddfe9 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml
@@ -7,7 +7,7 @@ GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci ext/$CI_CONCURRENT_ID" # TODO: Factor this out so that each job selects the Xcode version to # use so that different versions can be tested in a single pipeline. - DEVELOPER_DIR: "/Applications/Xcode-15.3.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode-16.0.app/Contents/Developer" # Avoid conflicting with other projects running on the same machine. SCCACHE_SERVER_PORT: 4227 @@ -47,6 +47,13 @@ CMAKE_CI_IN_SYMLINK_TREE: 1 CMAKE_CI_BUILD_DIR: "real_work/work/build" +.macos_arm64_curl: + extends: .macos_build + + variables: + CMAKE_CONFIGURATION: macos_arm64_curl + CTEST_NO_WARNINGS_ALLOWED: 1 + .macos_arm64_pch: extends: .macos_arm64_ninja @@ -135,7 +142,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-x86_64 - shell - - xcode-15.3 + - xcode-16.0 - nonconcurrent .macos_x86_64_tags_ext: @@ -143,7 +150,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-x86_64 - shell - - xcode-15.3 + - xcode-16.0 - concurrent .macos_arm64_tags: @@ -151,7 +158,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-15.3 + - xcode-16.0 - nonconcurrent .macos_arm64_tags_ext: @@ -159,7 +166,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-15.3 + - xcode-16.0 - concurrent .macos_arm64_tags_package: @@ -167,7 +174,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-15.3 + - xcode-16.0 - nonconcurrent - finder @@ -175,12 +182,8 @@ .before_script_macos: &before_script_macos - source .gitlab/ci/env.sh - - .gitlab/ci/cmake.sh - - export PATH=$PWD/.gitlab/cmake/bin:$PATH - - .gitlab/ci/ninja.sh - - export PATH=$PWD/.gitlab:$PATH - - cmake --version - - ninja --version + - source .gitlab/ci/cmake-env.sh + - source .gitlab/ci/ninja-env.sh # Download Qt - cmake -P .gitlab/ci/download_qt.cmake - export CMAKE_PREFIX_PATH=$PWD/.gitlab/qt${CMAKE_PREFIX_PATH:+:$CMAKE_PREFIX_PATH}
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index c449ab8..fdde3b7 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml
@@ -35,25 +35,25 @@ variables: VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "x64" - VCVARSVERSION: "14.36.32532" + VCVARSVERSION: "14.41.34120" .windows_vcvarsall_vs2022_x86: variables: VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "x86" - VCVARSVERSION: "14.36.32532" + VCVARSVERSION: "14.41.34120" .windows_vcvarsall_vs2022_x64_arm64: variables: VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "x64_arm64" - VCVARSVERSION: "14.36.32532" + VCVARSVERSION: "14.41.34120" .windows_arm64_vcvarsall_vs2022: variables: VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat" VCVARSPLATFORM: "arm64" - VCVARSVERSION: "14.36.32532" + VCVARSVERSION: "14.41.34120" .windows_vs2022_x64_pch: extends: @@ -119,7 +119,7 @@ CMAKE_CONFIGURATION: windows_vs2022_x64 CMAKE_GENERATOR: "Visual Studio 17 2022" CMAKE_GENERATOR_PLATFORM: "x64" - CMAKE_GENERATOR_TOOLSET: "v143,version=14.36.32532" + CMAKE_GENERATOR_TOOLSET: "v143,version=14.41.34120" CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" .windows_vs2019_x64: @@ -282,7 +282,7 @@ CMAKE_CONFIGURATION: windows_arm64_vs2022 CMAKE_GENERATOR: "Visual Studio 17 2022" CMAKE_GENERATOR_PLATFORM: "ARM64" - CMAKE_GENERATOR_TOOLSET: "v143,version=14.36.32532" + CMAKE_GENERATOR_TOOLSET: "v143,version=14.41.34120" CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" .mingw_osdn_io: @@ -316,7 +316,7 @@ - windows-x86_64 - shell - vs2022 - - msvc-19.36 + - msvc-14.41 - nonconcurrent .windows_x86_64_tags_nonconcurrent_vs2022_arm64: @@ -325,7 +325,7 @@ - windows-x86_64 - shell - vs2022 - - msvc-19.36-arm64 + - msvc-14.41-arm64 - nonconcurrent .windows_x86_64_tags_concurrent_vs2022: @@ -334,15 +334,26 @@ - windows-x86_64 - shell - vs2022 - - msvc-19.36 + - msvc-14.41 - concurrent -.windows_x86_64_tags_concurrent_vs2019: +.windows_x86_64_tags_concurrent_vs2022_android: + tags: + - cmake # Since this is a bare runner, pin to a project. + - windows-x86_64 + - shell + - vs2022 + - vs17-android + - msvc-14.41 + - concurrent + +.windows_x86_64_tags_concurrent_vs2019_android: tags: - cmake # Since this is a bare runner, pin to a project. - windows-x86_64 - shell - vs2019 + - vs16-android - msvc-19.29-16.11 - concurrent @@ -359,7 +370,7 @@ - windows-arm64 - shell - vs2022 - - msvc-19.36 + - msvc-14.41 - nonconcurrent .windows_arm64_tags_concurrent_vs2022: @@ -368,22 +379,15 @@ - windows-arm64 - shell - vs2022 - - msvc-19.36 + - msvc-14.41 - concurrent ## Windows-specific scripts .before_script_windows: &before_script_windows - . .gitlab/ci/env.ps1 - - $pwdpath = $pwd.Path - - (& "$pwsh" -File ".gitlab/ci/wix.ps1") - - Set-Item -Force -Path "env:WIX" -Value "$pwdpath\.gitlab\wix" - - (& "$pwsh" -File ".gitlab/ci/cmake.ps1") - - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\cmake\bin;$env:PATH" - - $cmake = "cmake" + - . .gitlab/ci/cmake-env.ps1 - . .gitlab/ci/ninja-env.ps1 - - (& "$env:WIX\bin\light.exe" -help) | Select -First 1 - - cmake --version - . .gitlab/ci/qt-env.ps1 - . .gitlab/ci/python-env.ps1 @@ -396,7 +400,7 @@ script: - *before_script_windows - - Set-Item -Force -Path "env:PATH" -Value "$env:PATH;$env:SCCACHE_PATH" + - . .gitlab/ci/sccache-env.ps1 - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 - sccache --start-server - sccache --show-stats
diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml index b85b728..0402d33 100644 --- a/.gitlab/rules.yml +++ b/.gitlab/rules.yml
@@ -4,6 +4,8 @@ rules: - if: '$CMAKE_CI_PACKAGE != null' when: never + - if: '($CMAKE_CI_NIGHTLY == "true" && $CMAKE_CI_JOB_NIGHTLY == "false")' + when: never - if: '$CMAKE_CI_NIGHTLY == "true"' when: on_success - if: '$CMAKE_CI_JOB_NIGHTLY == "true"' @@ -25,6 +27,8 @@ rules: - if: '$CMAKE_CI_PACKAGE != null' when: never + - if: '($CMAKE_CI_NIGHTLY == "true" && $CMAKE_CI_JOB_NIGHTLY == "false")' + when: never - if: '$CMAKE_CI_NIGHTLY == "true"' when: on_success - if: '$CMAKE_CI_JOB_NIGHTLY == "true"' @@ -46,6 +50,8 @@ rules: - if: '$CMAKE_CI_PACKAGE != null' when: never + - if: '($CMAKE_CI_NIGHTLY == "true" && $CMAKE_CI_JOB_NIGHTLY == "false")' + when: never - if: '($CMAKE_CI_NIGHTLY == "true" && $CMAKE_CI_NIGHTLY_IGNORE_DEPS == "true")' when: always - if: '$CMAKE_CI_NIGHTLY == "true"'
diff --git a/.gitlab/upload.yml b/.gitlab/upload.yml index 56793af..6b46313 100644 --- a/.gitlab/upload.yml +++ b/.gitlab/upload.yml
@@ -1,7 +1,7 @@ # Steps for uploading artifacts .rsync_upload_package: - image: "fedora:39" + image: "fedora:40" stage: upload tags: - cmake @@ -21,7 +21,7 @@ .rsync_upload_help: stage: upload - image: "fedora:39" + image: "fedora:40" tags: - cmake - docker
diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake index bed7248..803e09e 100644 --- a/Auxiliary/bash-completion/cmake +++ b/Auxiliary/bash-completion/cmake
@@ -2,14 +2,22 @@ _cmake() { - local cur prev words cword split=false - if type -t _init_completion >/dev/null; then - _init_completion -n = || return + local is_old_completion=false + local is_init_completion=false + + local cur prev words cword split was_split + if type -t _comp_initialize >/dev/null; then + _comp_initialize -s || return + elif type -t _init_completion >/dev/null; then + _init_completion -s || return + is_init_completion=true else # manual initialization for older bash completion versions COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" + is_old_completion=true + split=false fi # Workaround for options like -DCMAKE_BUILD_TYPE=Release @@ -89,7 +97,9 @@ ;; esac - _split_longopt && split=true + if $is_old_completion; then + _split_longopt && split=true + fi case "$prev" in -C|-P|--graphviz|--system-information) @@ -187,7 +197,11 @@ ;; esac - $split && return + if ($is_old_completion || $is_init_completion); then + $split && return + else + [[ $was_split ]] && return + fi if [[ "$cur" == -* ]]; then COMPREPLY=( $(compgen -W '$( _parse_help "$1" --help )' -- ${cur}) )
diff --git a/Auxiliary/bash-completion/cpack b/Auxiliary/bash-completion/cpack index cf5751f..5fc47ab 100644 --- a/Auxiliary/bash-completion/cpack +++ b/Auxiliary/bash-completion/cpack
@@ -3,7 +3,9 @@ _cpack() { local cur prev words cword - if type -t _init_completion >/dev/null; then + if type -t _comp_initialize >/dev/null; then + _comp_initialize -n = || return + elif type -t _init_completion >/dev/null; then _init_completion -n = || return else # manual initialization for older bash completion versions
diff --git a/Auxiliary/bash-completion/ctest b/Auxiliary/bash-completion/ctest index 3c629d2..51cc682 100644 --- a/Auxiliary/bash-completion/ctest +++ b/Auxiliary/bash-completion/ctest
@@ -3,7 +3,9 @@ _ctest() { local cur prev words cword - if type -t _init_completion >/dev/null; then + if type -t _comp_initialize >/dev/null; then + _comp_initialize -n = || return + elif type -t _init_completion >/dev/null; then _init_completion -n = || return else # manual initialization for older bash completion versions
diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el index b5e5ff4..893c0d4 100644 --- a/Auxiliary/cmake-mode.el +++ b/Auxiliary/cmake-mode.el
@@ -1,4 +1,4 @@ -;;; cmake-mode.el --- major-mode for editing CMake sources +;;; cmake-mode.el --- major-mode for editing CMake sources -*- lexical-binding: t; -*- ;; Package-Requires: ((emacs "24.1")) @@ -279,7 +279,7 @@ ;------------------------------------------------------------------------------ -(defun cmake--syntax-propertize-until-bracket-close (syntax) +(defun cmake--syntax-propertize-until-bracket-close (syntax end) ;; This function assumes that a previous search has matched the ;; beginning of a bracket_comment or bracket_argument and that the ;; second capture group has matched the equal signs between the two @@ -307,10 +307,10 @@ (syntax-propertize-rules ("\\(#\\)\\[\\(=*\\)\\[" (1 - (prog1 "!" (cmake--syntax-propertize-until-bracket-close "!")))) + (prog1 "!" (cmake--syntax-propertize-until-bracket-close "!" end)))) ("\\(\\[\\)\\(=*\\)\\[" (1 - (prog1 "|" (cmake--syntax-propertize-until-bracket-close "|")))))) + (prog1 "|" (cmake--syntax-propertize-until-bracket-close "|" end)))))) ;; Syntax table for this mode. (defvar cmake-mode-syntax-table nil @@ -480,7 +480,8 @@ ;;;###autoload (defun cmake-help () - "Queries for any of the four available help topics and prints out the appropriate page." + "Queries for any of the four available help topics and prints out the +appropriate page." (interactive) (let* ((default-entry (cmake-symbol-at-point)) (command-list (cmake-get-list "command"))
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 5629356..b504e4e 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim
@@ -450,6 +450,7 @@ \ VS_STARTUP_PROJECT \ VS_TOOL_OVERRIDE \ VS_USER_PROPS + \ VS_FILTER_PROPS \ VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION \ VS_WINRT_COMPONENT \ VS_WINRT_EXTENSIONS @@ -2635,7 +2636,6 @@ \ SKIP_REGULAR_EXPRESSION \ TARGET_FILE \ WILL_FAIL - \ WILL_FALL \ WORKING_DIRECTORY syn keyword cmakeKWblock contained
diff --git a/CMakeCPack.cmake b/CMakeCPack.cmake index 798affd..a0c0e54 100644 --- a/CMakeCPack.cmake +++ b/CMakeCPack.cmake
@@ -229,15 +229,12 @@ set(CPACK_WIX_UPGRADE_GUID "8ffd1d72-b7f1-11e2-8ee5-00238bca4991") -if(MSVC AND NOT "$ENV{WIX}" STREQUAL "") - set(WIX_CUSTOM_ACTION_ENABLED TRUE) +if(CMake_BUILD_WIX_CUSTOM_ACTION) if(CMAKE_CONFIGURATION_TYPES) - set(WIX_CUSTOM_ACTION_MULTI_CONFIG TRUE) + set(CMake_BUILD_WIX_CUSTOM_ACTION_MULTI_CONFIG TRUE) else() - set(WIX_CUSTOM_ACTION_MULTI_CONFIG FALSE) + set(CMake_BUILD_WIX_CUSTOM_ACTION_MULTI_CONFIG FALSE) endif() -else() - set(WIX_CUSTOM_ACTION_ENABLED FALSE) endif() # Set the options file that needs to be included inside CMakeCPackOptions.cmake
diff --git a/CMakeCPackOptions.cmake.in b/CMakeCPackOptions.cmake.in index 7aacf8e..65f266e 100644 --- a/CMakeCPackOptions.cmake.in +++ b/CMakeCPackOptions.cmake.in
@@ -206,6 +206,9 @@ endif() if("${CPACK_GENERATOR}" STREQUAL "WIX") + set(CPACK_WIX_VERSION 4) + set(CPACK_WIX_BUILD_EXTRA_FLAGS "") + # Reset CPACK_PACKAGE_VERSION to deal with WiX restriction. # But the file names still use the full CMake_VERSION value: set(CPACK_PACKAGE_FILE_NAME @@ -246,14 +249,7 @@ CPACK_START_MENU_SHORTCUTS "CMake Web Site" ) - set(CPACK_WIX_LIGHT_EXTRA_FLAGS "-dcl:high") - - if(NOT "$ENV{CMAKE_CI_PACKAGE}" STREQUAL "") - # Suppress validation. It does not work without - # an interactive session or an admin account. - # https://github.com/wixtoolset/issues/issues/3968 - list(APPEND CPACK_WIX_LIGHT_EXTRA_FLAGS "-sval") - endif() + list(APPEND CPACK_WIX_BUILD_EXTRA_FLAGS -dcl high) set(CPACK_WIX_UI_BANNER "@CMake_SOURCE_DIR@/Utilities/Release/WiX/ui_banner.jpg" @@ -265,17 +261,18 @@ set(CPACK_WIX_EXTRA_SOURCES "@CMake_SOURCE_DIR@/Utilities/Release/WiX/install_dir.wxs" - "@CMake_SOURCE_DIR@/Utilities/Release/WiX/cmake_extra_dialog.wxs" + "@CMake_SOURCE_DIR@/Utilities/Release/WiX/options.wxs" + "@CMake_SOURCE_DIR@/Utilities/Release/WiX/options_dlg.wxs" ) - set(_WIX_CUSTOM_ACTION_ENABLED "@WIX_CUSTOM_ACTION_ENABLED@") + set(_WIX_CUSTOM_ACTION_ENABLED "@CMake_BUILD_WIX_CUSTOM_ACTION@") if(_WIX_CUSTOM_ACTION_ENABLED) list(APPEND CPACK_WIX_EXTRA_SOURCES "@CMake_SOURCE_DIR@/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs" ) - list(APPEND CPACK_WIX_CANDLE_EXTRA_FLAGS -dCHECK_NSIS=1) + list(APPEND CPACK_WIX_BUILD_EXTRA_FLAGS -d CHECK_NSIS=1) - set(_WIX_CUSTOM_ACTION_MULTI_CONFIG "@WIX_CUSTOM_ACTION_MULTI_CONFIG@") + set(_WIX_CUSTOM_ACTION_MULTI_CONFIG "@CMake_BUILD_WIX_CUSTOM_ACTION_MULTI_CONFIG@") if(_WIX_CUSTOM_ACTION_MULTI_CONFIG) if(CPACK_BUILD_CONFIG) set(_WIX_CUSTOM_ACTION_CONFIG "${CPACK_BUILD_CONFIG}") @@ -291,7 +288,7 @@ endif() endif() - set(CPACK_WIX_UI_REF "CMakeUI_InstallDir") + set(CPACK_WIX_UI_REF "CMakeUI_InstallDir_$(sys.BUILDARCHSHORT)") set(CPACK_WIX_PATCH_FILE "@CMake_SOURCE_DIR@/Utilities/Release/WiX/patch_path_env.xml" @@ -307,6 +304,6 @@ list(APPEND CPACK_WIX_PATCH_FILE "@CMake_SOURCE_DIR@/Utilities/Release/WiX/patch_desktop_shortcut.xml" ) - list(APPEND CPACK_WIX_CANDLE_EXTRA_FLAGS -dBUILD_QtDialog=1) + list(APPEND CPACK_WIX_BUILD_EXTRA_FLAGS -d BUILD_QtDialog=1) endif() endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt index dfbb38d..081bd7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,7 +1,7 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR) set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake) @@ -178,6 +178,9 @@ string(TOLOWER "${util}" lutil) set(CMAKE_USE_SYSTEM_${util} "${CMAKE_USE_SYSTEM_LIBRARY_${util}}" CACHE BOOL "Use system-installed ${lutil}" FORCE) + elseif(util STREQUAL "CURL" AND APPLE) + # macOS provides a curl with backends configured by Apple. + set(CMAKE_USE_SYSTEM_LIBRARY_${util} ON) else() set(CMAKE_USE_SYSTEM_LIBRARY_${util} OFF) endif() @@ -215,6 +218,10 @@ mark_as_advanced(CMAKE_USE_SYSTEM_KWIML) # Mention to the user what system libraries are being used. + if(CMAKE_USE_SYSTEM_CURL) + # Avoid messaging about curl-only dependencies. + list(REMOVE_ITEM UTILITIES NGHTTP2) + endif() foreach(util IN LISTS UTILITIES ITEMS KWIML) if(CMAKE_USE_SYSTEM_${util}) message(STATUS "Using system-installed ${util}")
diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index 85af8ed..ae55715 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in
@@ -12,11 +12,12 @@ "warning: \\(Long double usage is reported only once for each file" "warning: To disable this warning use" "could not be inlined" - "libcmexpat.*has no symbols" - "libcmcurl.*has no symbols" + "libcm(curl|expat).*has no symbols" + "cm(curl|expat).build/[^ ]*.o has no symbols" "not sorted slower link editing will result" "stl_deque.h:479" "Utilities.cmzlib." + "Utilities.cmzstd." "Utilities.cmbzip2." "Source.CTest.Curl" "Source.CursesDialog.form" @@ -85,6 +86,7 @@ "[0-9]+ Warning\\(s\\) detected" # SunPro # Ignore false positive on `cm::optional` usage from GCC + "cmFileCommand.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& tls_verify \\+2\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]" "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '.*cm::optional<CxxModuleMapFormat>::_mem\\)\\)' may be used uninitialized \\[-Wmaybe-uninitialized\\]" "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: note: '.*cm::optional<CxxModuleMapFormat>::_mem\\)\\)' was declared here" "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& modmap_fmt \\+4\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]"
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index bd80e6e..77357c0 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst
@@ -84,8 +84,8 @@ .. versionadded:: 3.20 Arguments to ``BYPRODUCTS`` may use a restricted set of :manual:`generator expressions <cmake-generator-expressions(7)>`. - :ref:`Target-dependent expressions <Target-Dependent Queries>` are not - permitted. + :ref:`Target-dependent expressions <Target-Dependent Expressions>` + are not permitted. .. versionchanged:: 3.28 In targets using :ref:`file sets`, custom command byproducts are now @@ -269,17 +269,23 @@ source tree is mentioned as an absolute source file path elsewhere in the current directory. + The output file path may not contain ``<`` or ``>`` characters. + .. versionadded:: 3.20 Arguments to ``OUTPUT`` may use a restricted set of :manual:`generator expressions <cmake-generator-expressions(7)>`. - :ref:`Target-dependent expressions <Target-Dependent Queries>` are not - permitted. + :ref:`Target-dependent expressions <Target-Dependent Expressions>` + are not permitted. .. versionchanged:: 3.28 In targets using :ref:`file sets`, custom command outputs are now considered private unless they are listed in a non-private file set. See policy :policy:`CMP0154`. + .. versionchanged:: 3.30 + The output file path may now use ``#`` characters, except + when using the :generator:`Borland Makefiles` generator. + ``USES_TERMINAL`` .. versionadded:: 3.2
diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst index 0385a93..d88e0f0 100644 --- a/Help/command/add_custom_target.rst +++ b/Help/command/add_custom_target.rst
@@ -60,8 +60,8 @@ .. versionadded:: 3.20 Arguments to ``BYPRODUCTS`` may use a restricted set of :manual:`generator expressions <cmake-generator-expressions(7)>`. - :ref:`Target-dependent expressions <Target-Dependent Queries>` are not - permitted. + :ref:`Target-dependent expressions <Target-Dependent Expressions>` + are not permitted. .. versionchanged:: 3.28 In custom targets using :ref:`file sets`, byproducts are now
diff --git a/Help/command/add_dependencies.rst b/Help/command/add_dependencies.rst index 23cb405..3a51a30 100644 --- a/Help/command/add_dependencies.rst +++ b/Help/command/add_dependencies.rst
@@ -20,6 +20,17 @@ .. versionadded:: 3.3 Allow adding dependencies to interface libraries. +.. versionadded:: 3.8 + Dependencies will populate the :prop_tgt:`MANUALLY_ADDED_DEPENDENCIES` + property of ``<target>``. + +.. versionchanged:: 3.9 + The :ref:`Ninja Generators` use weaker ordering than + other generators in order to improve available concurrency. + They only guarantee that the dependencies' custom commands are + finished before sources in ``<target>`` start compiling; this + ensures generated sources are available. + See Also ^^^^^^^^
diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst index 5b22cb1..cab380e 100644 --- a/Help/command/add_library.rst +++ b/Help/command/add_library.rst
@@ -84,6 +84,13 @@ pre-processed, and you want to have the original sources reachable from within IDE. +.. versionchanged:: 3.30 + + On platforms that do not support shared libraries, ``add_library`` + now fails on calls creating ``SHARED`` libraries instead of + automatically converting them to ``STATIC`` libraries as before. + See policy :policy:`CMP0164`. + Object Libraries ^^^^^^^^^^^^^^^^
diff --git a/Help/command/add_subdirectory.rst b/Help/command/add_subdirectory.rst index ae063e6..b5fa459 100644 --- a/Help/command/add_subdirectory.rst +++ b/Help/command/add_subdirectory.rst
@@ -20,19 +20,10 @@ directory will be processed immediately by CMake before processing in the current input file continues beyond this command. -If the ``EXCLUDE_FROM_ALL`` argument is provided then targets in the -subdirectory will not be included in the ``ALL`` target of the parent -directory by default, and will be excluded from IDE project files. -Users must explicitly build targets in the subdirectory. This is -meant for use when the subdirectory contains a separate part of the -project that is useful but not necessary, such as a set of examples. -Typically the subdirectory should contain its own :command:`project` -command invocation so that a full build system will be generated in the -subdirectory (such as a Visual Studio IDE solution file). Note that -inter-target dependencies supersede this exclusion. If a target built by -the parent project depends on a target in the subdirectory, the dependee -target will be included in the parent project build system to satisfy -the dependency. +If the ``EXCLUDE_FROM_ALL`` argument is provided then the +:prop_dir:`EXCLUDE_FROM_ALL` property will be set on the added directory. +This will exclude the directory from a default build. See the directory +property :prop_dir:`EXCLUDE_FROM_ALL` for full details. .. versionadded:: 3.25 If the ``SYSTEM`` argument is provided, the :prop_dir:`SYSTEM` directory
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst index 557c858..2a3c759 100644 --- a/Help/command/add_test.rst +++ b/Help/command/add_test.rst
@@ -80,7 +80,7 @@ If the test command exits with code ``0`` the test passes. Non-zero exit code is a "failed" test. The test property :prop_test:`WILL_FAIL` inverts this logic. Note that system-level test failures such as segmentation faults or -heap errors will still fail the test even if ``WILL_FALL`` is true. Output +heap errors will still fail the test even if ``WILL_FAIL`` is true. Output written to stdout or stderr is captured by :manual:`ctest(1)` and only affects the pass/fail status via the :prop_test:`PASS_REGULAR_EXPRESSION`, :prop_test:`FAIL_REGULAR_EXPRESSION`, or :prop_test:`SKIP_REGULAR_EXPRESSION`
diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst index 38d06bb..a9c7653 100644 --- a/Help/command/cmake_language.rst +++ b/Help/command/cmake_language.rst
@@ -271,6 +271,11 @@ :command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)`` outside of that context will result in an error. + .. versionadded:: 3.30 + The :prop_gbl:`PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE` global + property can be set if the dependency provider also wants to be enabled + in whole-project calls to :command:`try_compile`. + .. note:: The choice of dependency provider should always be under the user's control. As a convenience, a project may choose to provide a file that users can
diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst index 4e6bedb..39b7b99 100644 --- a/Help/command/cmake_path.rst +++ b/Help/command/cmake_path.rst
@@ -600,7 +600,7 @@ # filename is now already empty, the following removes nothing cmake_path(REMOVE_FILENAME path) - message("Second path is \"${result}\"") + message("Second path is \"${path}\"") Output::
diff --git a/Help/command/enable_language.rst b/Help/command/enable_language.rst index 2f1cc23..d587310 100644 --- a/Help/command/enable_language.rst +++ b/Help/command/enable_language.rst
@@ -9,15 +9,19 @@ Enables support for the named languages in CMake. This is the same as the :command:`project` command but does not create any of the extra -variables that are created by the project command. +variables that are created by the :command:`project` command. .. include:: SUPPORTED_LANGUAGES.txt -This command must be called in file scope, not in a function call. -Furthermore, it must be called in the highest directory common to all -targets using the named language directly for compiling sources or -indirectly through link dependencies. It is simplest to enable all -needed languages in the top-level directory of a project. +The following restrictions apply to where ``enable_language()`` may be called: + +* It must be called in file scope, not in a function call. +* It must not be called before the first call to :command:`project`. + See policy :policy:`CMP0165`. +* It must be called in the highest directory common to all targets + using the named language directly for compiling sources or + indirectly through link dependencies. It is simplest to enable all + needed languages in the top-level directory of a project. The ``OPTIONAL`` keyword is a placeholder for future implementation and does not currently work. Instead you can use the :module:`CheckLanguage`
diff --git a/Help/command/file.rst b/Help/command/file.rst index f373e24..34e97e7 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst
@@ -28,7 +28,6 @@ file(`STRINGS`_ <filename> <out-var> [...]) file(`\<HASH\>`_ <filename> <out-var>) file(`TIMESTAMP`_ <filename> <out-var> [...]) - file(`GET_RUNTIME_DEPENDENCIES`_ [...]) `Writing`_ file({`WRITE`_ | `APPEND`_} <filename> <content>...) @@ -65,6 +64,10 @@ file(`ARCHIVE_CREATE`_ OUTPUT <archive> PATHS <paths>... [...]) file(`ARCHIVE_EXTRACT`_ INPUT <archive> [...]) + `Handling Runtime Binaries`_ + file(`GET_RUNTIME_DEPENDENCIES`_ [...]) + + Reading ^^^^^^^ @@ -157,323 +160,6 @@ See the :command:`string(TIMESTAMP)` command for documentation of the ``<format>`` and ``UTC`` options. -.. signature:: - file(GET_RUNTIME_DEPENDENCIES [...]) - - .. versionadded:: 3.16 - - Recursively get the list of libraries depended on by the given files: - - .. code-block:: cmake - - file(GET_RUNTIME_DEPENDENCIES - [RESOLVED_DEPENDENCIES_VAR <deps_var>] - [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>] - [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>] - [EXECUTABLES <executable_files>...] - [LIBRARIES <library_files>...] - [MODULES <module_files>...] - [DIRECTORIES <directories>...] - [BUNDLE_EXECUTABLE <bundle_executable_file>] - [PRE_INCLUDE_REGEXES <regexes>...] - [PRE_EXCLUDE_REGEXES <regexes>...] - [POST_INCLUDE_REGEXES <regexes>...] - [POST_EXCLUDE_REGEXES <regexes>...] - [POST_INCLUDE_FILES <files>...] - [POST_EXCLUDE_FILES <files>...] - ) - - Please note that this sub-command is not intended to be used in project mode. - It is intended for use at install time, either from code generated by the - :command:`install(RUNTIME_DEPENDENCY_SET)` command, or from code provided by - the project via :command:`install(CODE)` or :command:`install(SCRIPT)`. - For example: - - .. code-block:: cmake - - install(CODE [[ - file(GET_RUNTIME_DEPENDENCIES - # ... - ) - ]]) - - The arguments are as follows: - - ``RESOLVED_DEPENDENCIES_VAR <deps_var>`` - Name of the variable in which to store the list of resolved dependencies. - - ``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>`` - Name of the variable in which to store the list of unresolved - dependencies. If this variable is not specified, and there are any - unresolved dependencies, an error is issued. - - ``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>`` - Variable prefix in which to store conflicting dependency information. - Dependencies are conflicting if two files with the same name are found in - two different directories. The list of filenames that conflict are stored - in ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list - of paths that were found for that filename are stored in - ``<conflicting_deps_prefix>_<filename>``. - - ``EXECUTABLES <executable_files>...`` - List of executable files to read for dependencies. These are executables - that are typically created with :command:`add_executable`, but they do - not have to be created by CMake. On Apple platforms, the paths to these - files determine the value of ``@executable_path`` when recursively - resolving the libraries. Specifying any kind of library (``STATIC``, - ``MODULE``, or ``SHARED``) here will result in undefined behavior. - - ``LIBRARIES <library_files>...`` - List of library files to read for dependencies. These are libraries that - are typically created with :command:`add_library(SHARED)`, but they do - not have to be created by CMake. Specifying ``STATIC`` libraries, - ``MODULE`` libraries, or executables here will result in undefined - behavior. - - ``MODULES <module_files>...`` - List of loadable module files to read for dependencies. These are modules - that are typically created with :command:`add_library(MODULE)`, but they - do not have to be created by CMake. They are typically used by calling - ``dlopen()`` at runtime rather than linked at link time with ``ld -l``. - Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables - here will result in undefined behavior. - - ``DIRECTORIES <directories>...`` - List of additional directories to search for dependencies. On Linux - platforms, these directories are searched if the dependency is not found - in any of the other usual paths. If it is found in such a directory, a - warning is issued, because it means that the file is incomplete (it does - not list all of the directories that contain its dependencies). - On Windows platforms, these directories are searched if the dependency - is not found in any of the other search paths, but no warning is issued, - because searching other paths is a normal part of Windows dependency - resolution. On Apple platforms, this argument has no effect. - - ``BUNDLE_EXECUTABLE <bundle_executable_file>`` - Executable to treat as the "bundle executable" when resolving libraries. - On Apple platforms, this argument determines the value of - ``@executable_path`` when recursively resolving libraries for - ``LIBRARIES`` and ``MODULES`` files. It has no effect on ``EXECUTABLES`` - files. On other platforms, it has no effect. This is typically (but not - always) one of the executables in the ``EXECUTABLES`` argument which - designates the "main" executable of the package. - - The following arguments specify filters for including or excluding libraries - to be resolved. See below for a full description of how they work. - - ``PRE_INCLUDE_REGEXES <regexes>...`` - List of pre-include regexes through which to filter the names of - not-yet-resolved dependencies. - - ``PRE_EXCLUDE_REGEXES <regexes>...`` - List of pre-exclude regexes through which to filter the names of - not-yet-resolved dependencies. - - ``POST_INCLUDE_REGEXES <regexes>...`` - List of post-include regexes through which to filter the names of - resolved dependencies. - - ``POST_EXCLUDE_REGEXES <regexes>...`` - List of post-exclude regexes through which to filter the names of - resolved dependencies. - - ``POST_INCLUDE_FILES <files>...`` - .. versionadded:: 3.21 - - List of post-include filenames through which to filter the names of - resolved dependencies. Symlinks are resolved when attempting to match - these filenames. - - ``POST_EXCLUDE_FILES <files>...`` - .. versionadded:: 3.21 - - List of post-exclude filenames through which to filter the names of - resolved dependencies. Symlinks are resolved when attempting to match - these filenames. - - These arguments can be used to exclude unwanted system libraries when - resolving the dependencies, or to include libraries from a specific - directory. The filtering works as follows: - - 1. If the not-yet-resolved dependency matches any of the - ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency - resolution proceeds to step 4. - - 2. If the not-yet-resolved dependency matches any of the - ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency. - - 3. Otherwise, dependency resolution proceeds. - - 4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according - to the linking rules of the platform (see below). - - 5. If the dependency is found, and its full path matches one of the - ``POST_INCLUDE_REGEXES`` or ``POST_INCLUDE_FILES``, the full path is added - to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` - recursively resolves that library's own dependencies. Otherwise, resolution - proceeds to step 6. - - 6. If the dependency is found, but its full path matches one of the - ``POST_EXCLUDE_REGEXES`` or ``POST_EXCLUDE_FILES``, it is not added to the - resolved dependencies, and dependency resolution stops for that dependency. - - 7. If the dependency is found, and its full path does not match either - ``POST_INCLUDE_REGEXES``, ``POST_INCLUDE_FILES``, ``POST_EXCLUDE_REGEXES``, - or ``POST_EXCLUDE_FILES``, the full path is added to the resolved - dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves - that library's own dependencies. - - Different platforms have different rules for how dependencies are resolved. - These specifics are described here. - - On Linux platforms, library resolution works as follows: - - 1. If the depending file does not have any ``RUNPATH`` entries, and the - library exists in one of the depending file's ``RPATH`` entries, or its - parents', in that order, the dependency is resolved to that file. - 2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the - library exists in one of those entries, the dependency is resolved to that - file. - 3. Otherwise, if the library exists in one of the directories listed by - ``ldconfig``, the dependency is resolved to that file. - 4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, - the dependency is resolved to that file. In this case, a warning is - issued, because finding a file in one of the ``DIRECTORIES`` means that - the depending file is not complete (it does not list all the directories - from which it pulls dependencies). - - 5. Otherwise, the dependency is unresolved. - - On Windows platforms, library resolution works as follows: - - 1. DLL dependency names are converted to lowercase for matching filters. - Windows DLL names are case-insensitive, and some linkers mangle the - case of the DLL dependency names. However, this makes it more difficult - for ``PRE_INCLUDE_REGEXES``, ``PRE_EXCLUDE_REGEXES``, - ``POST_INCLUDE_REGEXES``, and ``POST_EXCLUDE_REGEXES`` to properly - filter DLL names - every regex would have to check for both uppercase - and lowercase letters. For example: - - .. code-block:: cmake - - file(GET_RUNTIME_DEPENDENCIES - # ... - PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$" - ) - - Converting the DLL name to lowercase allows the regexes to only match - lowercase names, thus simplifying the regex. For example: - - .. code-block:: cmake - - file(GET_RUNTIME_DEPENDENCIES - # ... - PRE_INCLUDE_REGEXES "^mylibrary\\.dll$" - ) - - This regex will match ``mylibrary.dll`` regardless of how it is cased, - either on disk or in the depending file. (For example, it will match - ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.) - - .. versionchanged:: 3.27 - - The conversion to lowercase only applies while matching filters. - Results reported after filtering case-preserve each DLL name as it is - found on disk, if resolved, and otherwise as it is referenced by the - dependent binary. - - Prior to CMake 3.27, the results were reported with lowercase DLL - file names, but the directory portion retained its casing. - - 2. (**Not yet implemented**) If the depending file is a Windows Store app, - and the dependency is listed as a dependency in the application's package - manifest, the dependency is resolved to that file. - - 3. Otherwise, if the library exists in the same directory as the depending - file, the dependency is resolved to that file. - - 4. Otherwise, if the library exists in either the operating system's - ``system32`` directory or the ``Windows`` directory, in that order, the - dependency is resolved to that file. - - 5. Otherwise, if the library exists in one of the directories specified by - ``DIRECTORIES``, in the order they are listed, the dependency is resolved - to that file. In this case, a warning is not issued, because searching - other directories is a normal part of Windows library resolution. - - 6. Otherwise, the dependency is unresolved. - - On Apple platforms, library resolution works as follows: - - 1. If the dependency starts with ``@executable_path/``, and an - ``EXECUTABLES`` argument is in the process of being resolved, and - replacing ``@executable_path/`` with the directory of the executable - yields an existing file, the dependency is resolved to that file. - - 2. Otherwise, if the dependency starts with ``@executable_path/``, and there - is a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` - with the directory of the bundle executable yields an existing file, the - dependency is resolved to that file. - - 3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing - ``@loader_path/`` with the directory of the depending file yields an - existing file, the dependency is resolved to that file. - - 4. Otherwise, if the dependency starts with ``@rpath/``, and replacing - ``@rpath/`` with one of the ``RPATH`` entries of the depending file - yields an existing file, the dependency is resolved to that file. - Note that ``RPATH`` entries that start with ``@executable_path/`` or - ``@loader_path/`` also have these items replaced with the appropriate - path. - - 5. Otherwise, if the dependency is an absolute file that exists, - the dependency is resolved to that file. - - 6. Otherwise, the dependency is unresolved. - - This function accepts several variables that determine which tool is used for - dependency resolution: - - .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM - - Determines which operating system and executable format the files are built - for. This could be one of several values: - - * ``linux+elf`` - * ``windows+pe`` - * ``macos+macho`` - - If this variable is not specified, it is determined automatically by system - introspection. - - .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL - - Determines the tool to use for dependency resolution. It could be one of - several values, depending on the value of - :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`: - - ================================================= ============================================= - ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`` ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL`` - ================================================= ============================================= - ``linux+elf`` ``objdump`` - ``windows+pe`` ``objdump`` or ``dumpbin`` - ``macos+macho`` ``otool`` - ================================================= ============================================= - - If this variable is not specified, it is determined automatically by system - introspection. - - .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND - - Determines the path to the tool to use for dependency resolution. This is - the actual path to ``objdump``, ``dumpbin``, or ``otool``. - - If this variable is not specified, it is determined by the value of - ``CMAKE_OBJDUMP`` if set, else by system introspection. - - .. versionadded:: 3.18 - Use ``CMAKE_OBJDUMP`` if set. - Writing ^^^^^^^ @@ -506,6 +192,10 @@ With ``TOUCH`` and ``TOUCH_NOCREATE``, the contents of an existing file will not be modified. + .. versionchanged:: 3.30 + ``<files>`` can be an empty list. CMake 3.29 and earlier required + at least one file to be given. + .. signature:: file(GENERATE [...]) @@ -612,7 +302,9 @@ variable values referenced as ``@VAR@`` or ``${VAR}`` contained therein. The substitution rules behave the same as the :command:`configure_file` command. In order to match :command:`configure_file`'s behavior, generator expressions - are not supported for both ``OUTPUT`` and ``CONTENT``. + are not supported for both ``OUTPUT`` and ``CONTENT``, and the output file + is only modified and its timestamp updated if the content is changed or the + file previously didn't exist. The arguments are: @@ -712,6 +404,10 @@ Create the given directories and their parents as needed. + .. versionchanged:: 3.30 + ``<directories>`` can be an empty list. CMake 3.29 and earlier required + at least one directory to be given. + .. signature:: file(REMOVE <files>...) file(REMOVE_RECURSE <files>...) @@ -1097,6 +793,15 @@ is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable will be used instead. + ``TLS_VERSION <min>`` + .. versionadded:: 3.30 + + Specify minimum TLS version for ``https://`` URLs. + If this option is not specified, the value of the + :variable:`CMAKE_TLS_VERSION` variable or :envvar:`CMAKE_TLS_VERSION` + environment variable will be used instead. + See :variable:`CMAKE_TLS_VERSION` for allowed values. + ``TLS_VERIFY <ON|OFF>`` Specify whether to verify the server certificate for ``https://`` URLs. The default is to *not* verify. If this option is not specified, the @@ -1266,3 +971,323 @@ timestamp instead of extracting file timestamps from the archive. With ``VERBOSE``, the command will produce verbose output. + +Handling Runtime Binaries +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. signature:: + file(GET_RUNTIME_DEPENDENCIES [...]) + + .. versionadded:: 3.16 + + Recursively get the list of libraries depended on by the given files: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + [RESOLVED_DEPENDENCIES_VAR <deps_var>] + [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>] + [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>] + [EXECUTABLES <executable_files>...] + [LIBRARIES <library_files>...] + [MODULES <module_files>...] + [DIRECTORIES <directories>...] + [BUNDLE_EXECUTABLE <bundle_executable_file>] + [PRE_INCLUDE_REGEXES <regexes>...] + [PRE_EXCLUDE_REGEXES <regexes>...] + [POST_INCLUDE_REGEXES <regexes>...] + [POST_EXCLUDE_REGEXES <regexes>...] + [POST_INCLUDE_FILES <files>...] + [POST_EXCLUDE_FILES <files>...] + ) + + Please note that this sub-command is not intended to be used in project mode. + It is intended for use at install time, either from code generated by the + :command:`install(RUNTIME_DEPENDENCY_SET)` command, or from code provided by + the project via :command:`install(CODE)` or :command:`install(SCRIPT)`. + For example: + + .. code-block:: cmake + + install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + # ... + ) + ]]) + + The arguments are as follows: + + ``RESOLVED_DEPENDENCIES_VAR <deps_var>`` + Name of the variable in which to store the list of resolved dependencies. + + ``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>`` + Name of the variable in which to store the list of unresolved + dependencies. If this variable is not specified, and there are any + unresolved dependencies, an error is issued. + + ``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>`` + Variable prefix in which to store conflicting dependency information. + Dependencies are conflicting if two files with the same name are found in + two different directories. The list of filenames that conflict are stored + in ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list + of paths that were found for that filename are stored in + ``<conflicting_deps_prefix>_<filename>``. + + ``EXECUTABLES <executable_files>...`` + List of executable files to read for dependencies. These are executables + that are typically created with :command:`add_executable`, but they do + not have to be created by CMake. On Apple platforms, the paths to these + files determine the value of ``@executable_path`` when recursively + resolving the libraries. Specifying any kind of library (``STATIC``, + ``MODULE``, or ``SHARED``) here will result in undefined behavior. + + ``LIBRARIES <library_files>...`` + List of library files to read for dependencies. These are libraries that + are typically created with :command:`add_library(SHARED)`, but they do + not have to be created by CMake. Specifying ``STATIC`` libraries, + ``MODULE`` libraries, or executables here will result in undefined + behavior. + + ``MODULES <module_files>...`` + List of loadable module files to read for dependencies. These are modules + that are typically created with :command:`add_library(MODULE)`, but they + do not have to be created by CMake. They are typically used by calling + ``dlopen()`` at runtime rather than linked at link time with ``ld -l``. + Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables + here will result in undefined behavior. + + ``DIRECTORIES <directories>...`` + List of additional directories to search for dependencies. On Linux + platforms, these directories are searched if the dependency is not found + in any of the other usual paths. If it is found in such a directory, a + warning is issued, because it means that the file is incomplete (it does + not list all of the directories that contain its dependencies). + On Windows platforms, these directories are searched if the dependency + is not found in any of the other search paths, but no warning is issued, + because searching other paths is a normal part of Windows dependency + resolution. On Apple platforms, this argument has no effect. + + ``BUNDLE_EXECUTABLE <bundle_executable_file>`` + Executable to treat as the "bundle executable" when resolving libraries. + On Apple platforms, this argument determines the value of + ``@executable_path`` when recursively resolving libraries for + ``LIBRARIES`` and ``MODULES`` files. It has no effect on ``EXECUTABLES`` + files. On other platforms, it has no effect. This is typically (but not + always) one of the executables in the ``EXECUTABLES`` argument which + designates the "main" executable of the package. + + The following arguments specify filters for including or excluding libraries + to be resolved. See below for a full description of how they work. + + ``PRE_INCLUDE_REGEXES <regexes>...`` + List of pre-include regexes through which to filter the names of + not-yet-resolved dependencies. + + ``PRE_EXCLUDE_REGEXES <regexes>...`` + List of pre-exclude regexes through which to filter the names of + not-yet-resolved dependencies. + + ``POST_INCLUDE_REGEXES <regexes>...`` + List of post-include regexes through which to filter the names of + resolved dependencies. + + ``POST_EXCLUDE_REGEXES <regexes>...`` + List of post-exclude regexes through which to filter the names of + resolved dependencies. + + ``POST_INCLUDE_FILES <files>...`` + .. versionadded:: 3.21 + + List of post-include filenames through which to filter the names of + resolved dependencies. Symlinks are resolved when attempting to match + these filenames. + + ``POST_EXCLUDE_FILES <files>...`` + .. versionadded:: 3.21 + + List of post-exclude filenames through which to filter the names of + resolved dependencies. Symlinks are resolved when attempting to match + these filenames. + + These arguments can be used to exclude unwanted system libraries when + resolving the dependencies, or to include libraries from a specific + directory. The filtering works as follows: + + 1. If the not-yet-resolved dependency matches any of the + ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency + resolution proceeds to step 4. + + 2. If the not-yet-resolved dependency matches any of the + ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency. + + 3. Otherwise, dependency resolution proceeds. + + 4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according + to the linking rules of the platform (see below). + + 5. If the dependency is found, and its full path matches one of the + ``POST_INCLUDE_REGEXES`` or ``POST_INCLUDE_FILES``, the full path is added + to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` + recursively resolves that library's own dependencies. Otherwise, resolution + proceeds to step 6. + + 6. If the dependency is found, but its full path matches one of the + ``POST_EXCLUDE_REGEXES`` or ``POST_EXCLUDE_FILES``, it is not added to the + resolved dependencies, and dependency resolution stops for that dependency. + + 7. If the dependency is found, and its full path does not match either + ``POST_INCLUDE_REGEXES``, ``POST_INCLUDE_FILES``, ``POST_EXCLUDE_REGEXES``, + or ``POST_EXCLUDE_FILES``, the full path is added to the resolved + dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves + that library's own dependencies. + + Different platforms have different rules for how dependencies are resolved. + These specifics are described here. + + On Linux platforms, library resolution works as follows: + + 1. If the depending file does not have any ``RUNPATH`` entries, and the + library exists in one of the depending file's ``RPATH`` entries, or its + parents', in that order, the dependency is resolved to that file. + 2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the + library exists in one of those entries, the dependency is resolved to that + file. + 3. Otherwise, if the library exists in one of the directories listed by + ``ldconfig``, the dependency is resolved to that file. + 4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, + the dependency is resolved to that file. In this case, a warning is + issued, because finding a file in one of the ``DIRECTORIES`` means that + the depending file is not complete (it does not list all the directories + from which it pulls dependencies). + + 5. Otherwise, the dependency is unresolved. + + On Windows platforms, library resolution works as follows: + + 1. DLL dependency names are converted to lowercase for matching filters. + Windows DLL names are case-insensitive, and some linkers mangle the + case of the DLL dependency names. However, this makes it more difficult + for ``PRE_INCLUDE_REGEXES``, ``PRE_EXCLUDE_REGEXES``, + ``POST_INCLUDE_REGEXES``, and ``POST_EXCLUDE_REGEXES`` to properly + filter DLL names - every regex would have to check for both uppercase + and lowercase letters. For example: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + # ... + PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$" + ) + + Converting the DLL name to lowercase allows the regexes to only match + lowercase names, thus simplifying the regex. For example: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + # ... + PRE_INCLUDE_REGEXES "^mylibrary\\.dll$" + ) + + This regex will match ``mylibrary.dll`` regardless of how it is cased, + either on disk or in the depending file. (For example, it will match + ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.) + + .. versionchanged:: 3.27 + + The conversion to lowercase only applies while matching filters. + Results reported after filtering case-preserve each DLL name as it is + found on disk, if resolved, and otherwise as it is referenced by the + dependent binary. + + Prior to CMake 3.27, the results were reported with lowercase DLL + file names, but the directory portion retained its casing. + + 2. (**Not yet implemented**) If the depending file is a Windows Store app, + and the dependency is listed as a dependency in the application's package + manifest, the dependency is resolved to that file. + + 3. Otherwise, if the library exists in the same directory as the depending + file, the dependency is resolved to that file. + + 4. Otherwise, if the library exists in either the operating system's + ``system32`` directory or the ``Windows`` directory, in that order, the + dependency is resolved to that file. + + 5. Otherwise, if the library exists in one of the directories specified by + ``DIRECTORIES``, in the order they are listed, the dependency is resolved + to that file. In this case, a warning is not issued, because searching + other directories is a normal part of Windows library resolution. + + 6. Otherwise, the dependency is unresolved. + + On Apple platforms, library resolution works as follows: + + 1. If the dependency starts with ``@executable_path/``, and an + ``EXECUTABLES`` argument is in the process of being resolved, and + replacing ``@executable_path/`` with the directory of the executable + yields an existing file, the dependency is resolved to that file. + + 2. Otherwise, if the dependency starts with ``@executable_path/``, and there + is a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` + with the directory of the bundle executable yields an existing file, the + dependency is resolved to that file. + + 3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing + ``@loader_path/`` with the directory of the depending file yields an + existing file, the dependency is resolved to that file. + + 4. Otherwise, if the dependency starts with ``@rpath/``, and replacing + ``@rpath/`` with one of the ``RPATH`` entries of the depending file + yields an existing file, the dependency is resolved to that file. + Note that ``RPATH`` entries that start with ``@executable_path/`` or + ``@loader_path/`` also have these items replaced with the appropriate + path. + + 5. Otherwise, if the dependency is an absolute file that exists, + the dependency is resolved to that file. + + 6. Otherwise, the dependency is unresolved. + + This function accepts several variables that determine which tool is used for + dependency resolution: + + .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM + + Determines which operating system and executable format the files are built + for. This could be one of several values: + + * ``linux+elf`` + * ``windows+pe`` + * ``macos+macho`` + + If this variable is not specified, it is determined automatically by system + introspection. + + .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL + + Determines the tool to use for dependency resolution. It could be one of + several values, depending on the value of + :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`: + + ================================================= ============================================= + ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`` ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL`` + ================================================= ============================================= + ``linux+elf`` ``objdump`` + ``windows+pe`` ``objdump`` or ``dumpbin`` + ``macos+macho`` ``otool`` + ================================================= ============================================= + + If this variable is not specified, it is determined automatically by system + introspection. + + .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND + + Determines the path to the tool to use for dependency resolution. This is + the actual path to ``objdump``, ``dumpbin``, or ``otool``. + + If this variable is not specified, it is determined by the value of + ``CMAKE_OBJDUMP`` if set, else by system introspection. + + .. versionadded:: 3.18 + Use ``CMAKE_OBJDUMP`` if set.
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index 9a007fa..f555fe4 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst
@@ -21,6 +21,30 @@ and load its package-specific details. Calls to this command can also be intercepted by :ref:`dependency providers <dependency_providers>`. +Typical Usage +^^^^^^^^^^^^^ + +Most calls to ``find_package()`` typically have the following form: + +.. parsed-literal:: + + find_package(<PackageName> [<version>] [REQUIRED] [COMPONENTS <components>...]) + +The ``<PackageName>`` is the only mandatory argument. The ``<version>`` is +often omitted, and ``REQUIRED`` should be given if the project cannot be +configured successfully without the package. Some more complicated packages +support components which can be selected with the ``COMPONENTS`` keyword, but +most packages don't have that level of complexity. + +The above is a reduced form of the `basic signature`_. Where possible, +projects should find packages using this form. This reduces complexity and +maximizes the ways in which the package can be found or provided. + +Understanding the `basic signature`_ should be enough for general usage of +``find_package()``. Project maintainers who intend to provide a config +package should understand the bigger picture, as explained in +:ref:`Full Signature` and all subsequent sections on this page. + Search Modes ^^^^^^^^^^^^ @@ -86,12 +110,6 @@ forced to use only Module mode with a ``MODULE`` keyword. If the `full signature`_ is used, the command only searches in Config mode. -Where possible, user code should generally look for packages using the -`basic signature`_, since that allows the package to be found with any mode. -Project maintainers wishing to provide a config package should understand -the bigger picture, as explained in :ref:`Full Signature` and all subsequent -sections on this page. - .. _`basic signature`: Basic Signature
diff --git a/Help/command/macro.rst b/Help/command/macro.rst index 2858622..978f7d6 100644 --- a/Help/command/macro.rst +++ b/Help/command/macro.rst
@@ -61,7 +61,7 @@ In addition to referencing the formal parameters you can reference the values ``${ARGC}`` which will be set to the number of arguments passed -into the function as well as ``${ARGV0}``, ``${ARGV1}``, ``${ARGV2}``, +into the macro as well as ``${ARGV0}``, ``${ARGV1}``, ``${ARGV2}``, ... which will have the actual values of the arguments passed in. This facilitates creating macros with optional arguments.
diff --git a/Help/command/project.rst b/Help/command/project.rst index 2b93880..d220b83 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst
@@ -33,10 +33,39 @@ Boolean value indicating whether the project is top-level. -Further variables are set by the optional arguments described in the following. -If any of these arguments is not used, then the corresponding variables are +Further variables are set by the optional arguments described in `Options`_ +further below. Where an option is not given, its corresponding variable is set to the empty string. +Note that variables of the form ``<name>_SOURCE_DIR`` and ``<name>_BINARY_DIR`` +may also be set by other commands before ``project()`` is called (see the +:command:`FetchContent_MakeAvailable` command for one example). +Projects should not rely on ``<PROJECT-NAME>_SOURCE_DIR`` or +``<PROJECT-NAME>_BINARY_DIR`` holding a particular value outside of the scope +of the call to ``project()`` or one of its child scopes. + +.. versionchanged:: 3.30.3 + ``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, and + ``<PROJECT-NAME>_IS_TOP_LEVEL`` are always set as non-cache variables by + ``project(<PROJECT-NAME> ...)``. + +.. versionchanged:: 3.30.4 + The variables ``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, + and ``<PROJECT-NAME>_IS_TOP_LEVEL`` are only set as non-cache variables if + they are already set as cache or non-cache variables when + ``project(<PROJECT-NAME> ...)`` is called. + Note that this logic is flawed, as it can result in different behavior + between the first and subsequent runs because cache variables won't exist + on the first run, but they will on subsequent runs. + +.. versionchanged:: 3.30.5 + The variables ``<PROJECT-NAME>_SOURCE_DIR``, ``<PROJECT-NAME>_BINARY_DIR``, + and ``<PROJECT-NAME>_IS_TOP_LEVEL`` are only set as non-cache variables if + they are already set as non-cache variables when + ``project(<PROJECT-NAME> ...)`` is called. + Unlike the flawed behavior of 3.30.4, non-cache variables will not be set + if only cache variables of the same name are set. + Options ^^^^^^^
diff --git a/Help/command/string.rst b/Help/command/string.rst index 2f01653..6bd56a1 100644 --- a/Help/command/string.rst +++ b/Help/command/string.rst
@@ -136,15 +136,16 @@ or ``\\`` for a literal backslash ``\``. Escaping a non-special character is unnecessary but allowed, e.g. ``\a`` matches ``a``. ``[ ]`` - Matches any character(s) inside the brackets + Matches any character(s) inside the brackets. + To match a literal ``]``, make it the first character, e.g., ``[]ab]``. ``[^ ]`` - Matches any character(s) not inside the brackets + Matches any character(s) not inside the brackets. + To not match a literal ``]``, make it the first character, e.g., ``[^]ab]``. ``-`` - Inside brackets, specifies an inclusive range between - characters on either side e.g. ``[a-f]`` is ``[abcdef]`` - To match a literal ``-`` using brackets, make it the first - or the last character e.g. ``[+*/-]`` matches basic - mathematical operators. + Inside brackets, specifies an inclusive range between characters on + either side, e.g., ``[a-f]`` is ``[abcdef]``. + To match a literal ``-`` using brackets, make it the first or the last + character, e.g., ``[+*/-]`` matches basic mathematical operators. ``*`` Matches preceding pattern zero or more times ``+``
diff --git a/Help/command/target_compile_definitions.rst b/Help/command/target_compile_definitions.rst index 2290efb..5796b00 100644 --- a/Help/command/target_compile_definitions.rst +++ b/Help/command/target_compile_definitions.rst
@@ -15,7 +15,7 @@ :ref:`ALIAS target <Alias Targets>`. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to -specify the :ref:`scope <Target Usage Requirements>` of the following arguments. +specify the :ref:`scope <Target Command Scope>` of the following arguments. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`COMPILE_DEFINITIONS` property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` property of ``<target>``.
diff --git a/Help/command/target_compile_options.rst b/Help/command/target_compile_options.rst index 7cfb24b..64f45aa 100644 --- a/Help/command/target_compile_options.rst +++ b/Help/command/target_compile_options.rst
@@ -28,7 +28,7 @@ whether ``BEFORE`` will be ignored in certain cases. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to -specify the :ref:`scope <Target Usage Requirements>` of the following arguments. +specify the :ref:`scope <Target Command Scope>` of the following arguments. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`COMPILE_OPTIONS` property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_COMPILE_OPTIONS` property of ``<target>``.
diff --git a/Help/command/target_include_directories.rst b/Help/command/target_include_directories.rst index 2a410ec..bb5f9c3 100644 --- a/Help/command/target_include_directories.rst +++ b/Help/command/target_include_directories.rst
@@ -18,7 +18,7 @@ and prepending, independent of the default. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to specify -the :ref:`scope <Target Usage Requirements>` of the following arguments. +the :ref:`scope <Target Command Scope>` of the following arguments. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`INCLUDE_DIRECTORIES` property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` property of ``<target>``.
diff --git a/Help/command/target_link_directories.rst b/Help/command/target_link_directories.rst index 2854c96..67d3352 100644 --- a/Help/command/target_link_directories.rst +++ b/Help/command/target_link_directories.rst
@@ -21,7 +21,7 @@ :ref:`ALIAS target <Alias Targets>`. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to -specify the :ref:`scope <Target Usage Requirements>` of the items that follow +specify the :ref:`scope <Target Command Scope>` of the items that follow them. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`LINK_DIRECTORIES` property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_LINK_DIRECTORIES`
diff --git a/Help/command/target_link_libraries.rst b/Help/command/target_link_libraries.rst index 68d3598..a82adda 100644 --- a/Help/command/target_link_libraries.rst +++ b/Help/command/target_link_libraries.rst
@@ -153,7 +153,7 @@ [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) The ``PUBLIC``, ``PRIVATE`` and ``INTERFACE`` -:ref:`scope <Target Usage Requirements>` keywords can be used to +:ref:`scope <Target Command Scope>` keywords can be used to specify both the link dependencies and the link interface in one command. Libraries and targets following ``PUBLIC`` are linked to, and are made
diff --git a/Help/command/target_link_options.rst b/Help/command/target_link_options.rst index dca9598..7561c99 100644 --- a/Help/command/target_link_options.rst +++ b/Help/command/target_link_options.rst
@@ -32,7 +32,7 @@ instead of being appended. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to -specify the :ref:`scope <Target Usage Requirements>` of the following arguments. +specify the :ref:`scope <Target Command Scope>` of the following arguments. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`LINK_OPTIONS` property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_LINK_OPTIONS` property of ``<target>``.
diff --git a/Help/command/target_precompile_headers.rst b/Help/command/target_precompile_headers.rst index 50eaf22..61ea0aa 100644 --- a/Help/command/target_precompile_headers.rst +++ b/Help/command/target_precompile_headers.rst
@@ -25,7 +25,7 @@ :ref:`ALIAS target <Alias Targets>`. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to -specify the :ref:`scope <Target Usage Requirements>` of the following arguments. +specify the :ref:`scope <Target Command Scope>` of the following arguments. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`PRECOMPILE_HEADERS` property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` property of ``<target>``
diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst index 40755c5..0d31f8c 100644 --- a/Help/command/target_sources.rst +++ b/Help/command/target_sources.rst
@@ -22,7 +22,7 @@ ``<target>`` can be a custom target. The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to -specify the :ref:`scope <Target Usage Requirements>` of the source file paths +specify the :ref:`scope <Target Command Scope>` of the source file paths (``<items>``) that follow them. ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`SOURCES` property of ``<target>``, which are used when building the target itself. ``PUBLIC`` and ``INTERFACE`` items will populate the
diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst index 5021851..30ab41a 100644 --- a/Help/command/try_compile.rst +++ b/Help/command/try_compile.rst
@@ -47,6 +47,11 @@ :ref:`configure-log try_compile event <try_compile configure-log event>` if the ``NO_LOG`` option is not specified. +.. versionadded:: 3.30 + If the :prop_gbl:`PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE` global + property is set to true, :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` is + propagated into the project's build configuration. + This command supports an alternate signature for CMake older than 3.25. The signature above is recommended for clarity. @@ -156,8 +161,9 @@ Specify flags of the form :option:`-DVAR:TYPE=VALUE <cmake -D>` to be passed to the :manual:`cmake(1)` command-line used to drive the test build. The above example shows how values for variables - ``INCLUDE_DIRECTORIES``, ``LINK_DIRECTORIES``, and ``LINK_LIBRARIES`` - are used. + ``COMPILE_DEFINITIONS``, ``INCLUDE_DIRECTORIES``, ``LINK_DIRECTORIES``, + ``LINK_LIBRARIES``, and ``LINK_OPTIONS`` are used. Compiler options + can be passed in like `CMAKE_FLAGS -DCOMPILE_DEFINITIONS=-Werror`. ``COMPILE_DEFINITIONS <defs>...`` Specify ``-Ddefinition`` arguments to pass to :command:`add_definitions` @@ -387,6 +393,12 @@ :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` to specify the MSVC debug information format. +.. versionadded:: 3.30 + If the :prop_gbl:`PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE` global + property is set to true, :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` is + propagated into the test project's build configuration when using the + :ref:`whole-project signature <Try Compiling Whole Projects>`. + See Also ^^^^^^^^
diff --git a/Help/cpack_gen/innosetup.rst b/Help/cpack_gen/innosetup.rst index f48e7f5..ffdf508 100644 --- a/Help/cpack_gen/innosetup.rst +++ b/Help/cpack_gen/innosetup.rst
@@ -12,7 +12,11 @@ all features (e.g. component dependencies) are currently supported by Inno Setup and they're ignored by the generator for now. -CPack requires Inno Setup 6 or greater and only works on Windows. +CPack requires Inno Setup 6 or greater. + +.. versionadded:: 3.30 + The generator is now available on non-Windows hosts, + but requires Wine to run the Inno Setup tools. Variables specific to CPack Inno Setup generator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/cpack_gen/nuget.rst b/Help/cpack_gen/nuget.rst index 8ee2816..d97f0c0 100644 --- a/Help/cpack_gen/nuget.rst +++ b/Help/cpack_gen/nuget.rst
@@ -34,6 +34,17 @@ :Mandatory: No :Default: ``OFF`` +.. variable:: CPACK_NUGET_PACKAGE_DEBUG + + Enable debug messages while executing CPack NuGet generator. + + :Mandatory: No + :Default: ``OFF`` + + +Required metadata variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. variable:: CPACK_NUGET_PACKAGE_NAME CPACK_NUGET_<compName>_PACKAGE_NAME @@ -74,22 +85,16 @@ :Mandatory: Yes :Default: :variable:`CPACK_PACKAGE_VENDOR` -.. variable:: CPACK_NUGET_PACKAGE_TITLE - CPACK_NUGET_<compName>_PACKAGE_TITLE - A human-friendly title of the package, typically used in UI displays - as on nuget.org_ and the Package Manager in Visual Studio. If not - specified, the package ID is used. - - :Mandatory: No - :Default: - - - :variable:`CPACK_COMPONENT_<compName>_DISPLAY_NAME`, - - :variable:`!CPACK_COMPONENT_GROUP_<groupName>_DISPLAY_NAME` +Optional metadata variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. variable:: CPACK_NUGET_PACKAGE_OWNERS CPACK_NUGET_<compName>_PACKAGE_OWNERS + .. deprecated:: 3.30 + Use authors (:variable:`CPACK_NUGET_PACKAGE_AUTHORS`) instead. + A comma-separated list of the package creators using profile names on nuget.org_. This is often the same list as in authors, and is ignored when uploading the package to nuget.org_. @@ -120,6 +125,21 @@ :Mandatory: No :Default: None + :Supported: NuGet 4.9.0 and above + +.. variable:: CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME + CPACK_NUGET_<compName>_PACKAGE_LICENSE_FILE_NAME + + .. versionadded:: 3.20 + + The package's license file in :file:`.txt` or :file:`.md` format. + + If :variable:`!CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME` is specified, + :variable:`!CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION` is ignored. + + :Mandatory: No + :Default: None + :Supported: NuGet 4.9.0 and above .. variable:: CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION CPACK_NUGET_<compName>_PACKAGE_LICENSE_EXPRESSION @@ -139,19 +159,6 @@ :Mandatory: No :Default: None -.. variable:: CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME - CPACK_NUGET_<compName>_PACKAGE_LICENSE_FILE_NAME - - The package's license file in :file:`.txt` or :file:`.md` format. - - If :variable:`!CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME` is specified, - :variable:`!CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION` is ignored. - - .. versionadded:: 3.20 - - :Mandatory: No - :Default: None - .. variable:: CPACK_NUGET_PACKAGE_ICONURL CPACK_NUGET_<compName>_PACKAGE_ICONURL @@ -164,14 +171,6 @@ :Mandatory: No :Default: None -.. variable:: CPACK_NUGET_PACKAGE_REQUIRE_LICENSE_ACCEPTANCE - - When set to a true value, the user will be prompted to accept the license - before installing the package. - - :Mandatory: No - :Default: None - .. variable:: CPACK_NUGET_PACKAGE_ICON CPACK_NUGET_<compName>_PACKAGE_ICON @@ -182,10 +181,36 @@ :Mandatory: No :Default: None + :Supported: NuGet 5.3.0 and above + + +.. variable:: CPACK_NUGET_PACKAGE_README + CPACK_NUGET_<compName>_PACKAGE_README + + .. versionadded:: 3.30 + + The package path relative to the root of the package to a readme file. + Supported file formats include only Markdown (``*.md``). + + :Mandatory: No + :Default: None + :Supported: NuGet 5.10.0 preview 2 and above + +.. variable:: CPACK_NUGET_PACKAGE_REQUIRE_LICENSE_ACCEPTANCE + + When set to a true value, the user will be prompted to accept the license + before installing the package. + + :Mandatory: No + :Default: None .. variable:: CPACK_NUGET_PACKAGE_DESCRIPTION_SUMMARY CPACK_NUGET_<compName>_PACKAGE_DESCRIPTION_SUMMARY + .. deprecated:: 3.30 + Summary is being deprecated. Use description + (:variable:`CPACK_NUGET_PACKAGE_DESCRIPTION`) instead. + A short description of the package for UI display. If omitted, a truncated version of description is used. @@ -230,10 +255,75 @@ :Mandatory: No :Default: None +.. variable:: CPACK_NUGET_PACKAGE_REPOSITORY_URL + CPACK_NUGET_<compName>_REPOSITORY_URL + + .. versionadded:: 3.30 + + Repository metadata allows you to map the ``*.nupkg`` to the repository + that built it. This should be a publicly available URL that can be invoked + directly by a version control software. It should not be an HTML page as + this is meant for the computer. + + :Mandatory: No + :Default: None + :Supported: NuGet 4.0 and above + +.. variable:: CPACK_NUGET_PACKAGE_REPOSITORY_TYPE + CPACK_NUGET_<compName>_REPOSITORY_TYPE + + .. versionadded:: 3.30 + + A type of the VCS repository. When uploading a package to nuget.org_, the + type is limited to 100 characters. + + :Mandatory: Yes, if repository URL has been specified + :Default: None + :Supported: NuGet 4.0 and above + +.. variable:: CPACK_NUGET_PACKAGE_REPOSITORY_BRANCH + CPACK_NUGET_<compName>_REPOSITORY_BRANCH + + .. versionadded:: 3.30 + + A VSC branch name to build the package. + + :Mandatory: No + :Default: None + :Supported: NuGet 4.6 and above + +.. variable:: CPACK_NUGET_PACKAGE_REPOSITORY_COMMIT + CPACK_NUGET_<compName>_REPOSITORY_COMMIT + + .. versionadded:: 3.30 + + A SHA-1 hash of the commit to build the package. + + :Mandatory: No + :Default: None + :Supported: NuGet 4.6 and above + +.. variable:: CPACK_NUGET_PACKAGE_TITLE + CPACK_NUGET_<compName>_PACKAGE_TITLE + + A human-friendly title of the package, typically used in UI displays + as on nuget.org_ and the Package Manager in Visual Studio. If not + specified, the package ID is used. + + :Mandatory: No + :Default: + + - :variable:`CPACK_COMPONENT_<compName>_DISPLAY_NAME`, + - :variable:`!CPACK_COMPONENT_GROUP_<groupName>_DISPLAY_NAME` + + +Dependencies specification +~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. variable:: CPACK_NUGET_PACKAGE_DEPENDENCIES CPACK_NUGET_<compName>_PACKAGE_DEPENDENCIES - A list of package dependencies. + A list of default (not framework-specific) package dependencies. :Mandatory: No :Default: None @@ -242,23 +332,85 @@ CPACK_NUGET_<compName>_PACKAGE_DEPENDENCIES_<dependency>_VERSION A `version specification`_ for the particular dependency, where - ``<dependency>`` is an item of the dependency list (see above) - transformed with :command:`string(MAKE_C_IDENTIFIER)` command. + ``<dependency>`` is an item of the dependency list (see above). :Mandatory: No :Default: None -.. variable:: CPACK_NUGET_PACKAGE_DEBUG +.. variable:: CPACK_NUGET_PACKAGE_TFMS + CPACK_NUGET_<compName>_PACKAGE_TFMS - Enable debug messages while executing CPack NuGet generator. + .. versionadded:: 3.30 + + A list of Target Framework Monikers (TFMs) for the package, e.g., "net47;netcoreapp21". + For each of these TFMs a `dependency group`_ will be generated in the dependencies block of the NuGet + package. Framework-specific dependencies can be added to these groups with the TFM + dependency lists (see below). + + This variable is particularly useful for fixing warnings `NU5128`_. :Mandatory: No - :Default: ``OFF`` + :Default: None + +.. variable:: CPACK_NUGET_PACKAGE_DEPENDENCIES_<tfm> + CPACK_NUGET_<compName>_PACKAGE_DEPENDENCIES_<tfm> + + .. versionadded:: 3.30 + + A list of package dependencies that apply specifically to the ``<tfm>`` framework, where ``<tfm>`` + is an item from the TFMs list (see above). + + :Mandatory: No + :Default: None + +.. variable:: CPACK_NUGET_PACKAGE_DEPENDENCIES_<tfm>_<dependency>_VERSION + CPACK_NUGET_<compName>_PACKAGE_DEPENDENCIES_<tfm>_<dependency>_VERSION + + .. versionadded:: 3.30 + + A `version specification`_ for the particular framework-specific dependency, where + ``<dependency>`` is an item of the ``<tfm>``-specific dependency list (see above). + + :Mandatory: No + :Default: None + + +Example usage +^^^^^^^^^^^^^ + +.. code-block:: cmake + + set(CPACK_GENERATOR NuGet) + # Set up package metadata + set(CPACK_PACKAGE_NAME SamplePackage) + set(CPACK_PACKAGE_VERSION "1.0.0") + set(CPACK_PACKAGE_VENDOR "Example Inc") + set(CPACK_NUGET_PACKAGE_AUTHORS "ExampleInc") + set(CPACK_PACKAGE_DESCRIPTION "A .NET wrapper around the foobar library for frobbling bratchens") + set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.example.com") + set(CPACK_NUGET_PACKAGE_REPOSITORY_URL "https://github.com/example/libfoobar.git") + set(CPACK_NUGET_PACKAGE_REPOSITORY_TYPE git) + set(CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION "MIT") + # Set up dependencies + set(CPACK_NUGET_PACKAGE_TFMS "net4;net6.0") + set(CPACK_NUGET_PACKAGE_DEPENDENCIES_net4 "Foo;Bar") + # NB: If a version number is omitted, the dependency will not be created + set(CPACK_NUGET_PACKAGE_DEPENDENCIES_net4_Foo_VERSION "1.23") + set(CPACK_NUGET_PACKAGE_DEPENDENCIES_net4_Bar_VERSION "4.3.2") + # NB: General dependencies (not framework-specific) go in this variable + set(CPACK_NUGET_PACKAGE_DEPENDENCIES "Baz") + set(CPACK_NUGET_PACKAGE_DEPENDENCIES_Baz_VERSION "9.8.6") + # NB: Since "net6.0" was listed but no dependencies have been specified, an empty group + # will be added to the nuspec file for this framework. This can be used to address warning NU5128. + + include(CPack) .. _nuget.org: https://www.nuget.org .. _version specification: https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges .. _SPDX license identifier: https://spdx.org/licenses .. _SPDX specification: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions +.. _dependency group: https://learn.microsoft.com/en-us/nuget/reference/nuspec#dependency-groups +.. _NU5128: https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu5128 .. NuGet spec docs https://docs.microsoft.com/en-us/nuget/reference/nuspec
diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst index 43e3ff7..f196988 100644 --- a/Help/cpack_gen/wix.rst +++ b/Help/cpack_gen/wix.rst
@@ -1,10 +1,103 @@ CPack WIX Generator ------------------- -CPack WIX generator specific options +Use the `WiX Toolset`_ to produce a Windows Installer ``.msi`` database. + +.. _`WiX Toolset`: https://wixtoolset.org/ .. versionadded:: 3.7 - Support :variable:`CPACK_COMPONENT_<compName>_DISABLED` variable. + The :variable:`CPACK_COMPONENT_<compName>_DISABLED` variable is now + supported. + +WiX Toolsets +^^^^^^^^^^^^ + +CPack selects one of the following variants of the WiX Toolset +based on the :variable:`CPACK_WIX_VERSION` variable: + +* `WiX .NET Tools`_ +* `WiX Toolset v3`_ + +WiX .NET Tools +"""""""""""""" + +Packaging is performed using the following tools: + +``wix build`` + Build WiX source files directly into a Windows Installer ``.msi`` database. + + Invocations may be customized using tool-specific variables: + + * :variable:`CPACK_WIX_BUILD_EXTENSIONS <CPACK_WIX_<TOOL>_EXTENSIONS>` + * :variable:`CPACK_WIX_BUILD_EXTRA_FLAGS <CPACK_WIX_<TOOL>_EXTRA_FLAGS>` + +WiX extensions must be named with the form ``WixToolset.<Name>.wixext``. + +CPack expects the ``wix`` .NET tool to be available for command-line use +with any required WiX extensions already installed. Be sure the ``wix`` +version is compatible with :variable:`CPACK_WIX_VERSION`, and that WiX +extension versions match the ``wix`` tool version. For example: + +1. Install the ``wix`` command-line tool using ``dotnet``. + + To install ``wix`` globally for the current user: + + .. code-block:: bat + + dotnet tool install --global wix --version 4.0.4 + + This places ``wix.exe`` in ``%USERPROFILE%\.dotnet\tools`` and adds + the directory to the current user's ``PATH`` environment variable. + + Or, to install ``wix`` in a specific path, e.g., in ``c:\WiX``: + + .. code-block:: bat + + dotnet tool install --tool-path c:\WiX wix --version 4.0.4 + + This places ``wix.exe`` in ``c:\WiX``, but does *not* add it to the + current user's ``PATH`` environment variable. The ``WIX`` environment + variable may be set to tell CPack where to find the tool, + e.g., ``set WIX=c:\WiX``. + +2. Add the WiX ``UI`` extension, needed by CPack's default WiX template: + + .. code-block:: bat + + wix extension add --global WixToolset.UI.wixext/4.0.4 + + Extensions added globally are stored in ``%USERPROFILE%\.wix``, or if the + ``WIX_EXTENSIONS`` environment variable is set, in ``%WIX_EXTENSIONS%\.wix``. + +WiX Toolset v3 +"""""""""""""" + +Packaging is performed using the following tools: + +``candle`` + Compiles WiX source files into ``.wixobj`` files. + + Invocations may be customized using tool-specific variables: + + * :variable:`CPACK_WIX_CANDLE_EXTENSIONS <CPACK_WIX_<TOOL>_EXTENSIONS>` + * :variable:`CPACK_WIX_CANDLE_EXTRA_FLAGS <CPACK_WIX_<TOOL>_EXTRA_FLAGS>` + +``light`` + Links ``.wixobj`` files into a Windows Installer ``.msi`` database. + + Invocations may be customized using tool-specific variables: + + * :variable:`CPACK_WIX_LIGHT_EXTENSIONS <CPACK_WIX_<TOOL>_EXTENSIONS>` + * :variable:`CPACK_WIX_LIGHT_EXTRA_FLAGS <CPACK_WIX_<TOOL>_EXTRA_FLAGS>` + +CPack invokes both tools as needed. Intermediate ``.wixobj`` files +are considered implementation details. + +WiX extensions must be named with the form ``Wix<Name>Extension``. + +CPack expects the above tools to be available for command-line +use via the ``PATH``. Or, if the ``WIX`` environment variable is set, +CPack looks for the tools in ``%WIX%`` and ``%WIX%\bin``. Variables specific to CPack WIX generator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,6 +105,19 @@ The following variables are specific to the installers built on Windows using WiX. +.. variable:: CPACK_WIX_VERSION + + .. versionadded:: 3.30 + + Specify the version of WiX Toolset for which the configuration + is written. The value must be one of + + ``4`` + Package using `WiX .NET Tools`_. + + ``3`` + Package using `WiX Toolset v3`_. This is the default. + .. variable:: CPACK_WIX_UPGRADE_GUID Upgrade GUID (``Product/@UpgradeCode``) @@ -68,8 +174,13 @@ .. variable:: CPACK_WIX_UI_REF - This variable allows you to override the Id of the ``<UIRef>`` element - in the WiX template. + Specify the WiX ``UI`` extension's dialog set: + + * With `WiX .NET Tools`_, this is the Id of the + ``<ui:WixUI>`` element in the default WiX template. + + * With `WiX Toolset v3`_, this is the Id of the + ``<UIRef>`` element in the default WiX template. The default is ``WixUI_InstallDir`` in case no CPack components have been defined and ``WixUI_FeatureTree`` otherwise. @@ -107,7 +218,7 @@ Language(s) of the installer - Languages are compiled into the WixUI extension library. To use them, + Languages are compiled into the Wix ``UI`` extension library. To use them, simply provide the name of the culture. If you specify more than one culture identifier in a comma or semicolon delimited list, the first one that is found will be used. You can find a list of supported languages at: @@ -196,39 +307,35 @@ Extra WiX source files - This variable provides an optional list of extra WiX source files (.wxs) - that should be compiled and linked. The full path to source files is - required. + This variable provides an optional list of extra WiX source files (``.wxs``) + that should be compiled and linked. The paths must be absolute. .. variable:: CPACK_WIX_EXTRA_OBJECTS - Extra WiX object files or libraries + Extra WiX object files or libraries to use with `WiX Toolset v3`_. - This variable provides an optional list of extra WiX object (.wixobj) - and/or WiX library (.wixlib) files. The full path to objects and libraries - is required. + This variable provides an optional list of extra WiX object (``.wixobj``) + and/or WiX library (``.wixlib``) files. The paths must be absolute. .. variable:: CPACK_WIX_EXTENSIONS - This variable provides a list of additional extensions for the WiX - tools light and candle. + Specify a list of additional extensions for WiX tools. + See `WiX Toolsets`_ for extension naming patterns. .. variable:: CPACK_WIX_<TOOL>_EXTENSIONS - This is the tool specific version of CPACK_WIX_EXTENSIONS. - ``<TOOL>`` can be either LIGHT or CANDLE. + Specify a list of additional extensions for a specific WiX tool. + See `WiX Toolsets`_ for possible ``<TOOL>`` names. .. variable:: CPACK_WIX_<TOOL>_EXTRA_FLAGS - This list variable allows you to pass additional - flags to the WiX tool ``<TOOL>``. + Specify a list of additional command-line flags for a specific WiX tool. + See `WiX Toolsets`_ for possible ``<TOOL>`` names. Use it at your own risk. Future versions of CPack may generate flags which may be in conflict with your own flags. - ``<TOOL>`` can be either LIGHT or CANDLE. - .. variable:: CPACK_WIX_CMAKE_PACKAGE_REGISTRY If this variable is set the generated installer will create @@ -326,9 +433,9 @@ .. versionadded:: 3.23 - If this variable is set then the inclusion of WixUIExtensions is skipped, - i.e. the ``-ext "WixUIExtension"`` command line is not included during - the execution of the WiX light tool. + If this variable is set to true, the default inclusion of the WiX ``UI`` + extension is skipped, i.e., the ``-ext WixUIExtension`` or + ``-ext WixToolset.UI.wixext`` flag is not passed to WiX tools. .. variable:: CPACK_WIX_ARCHITECTURE @@ -351,13 +458,18 @@ administrative privileges. Start menu entries created by the installer are visible to all users. + This is the default if :variable:`CPACK_WIX_VERSION` is set to any + value other than ``3``. + ``perUser`` Not yet supported. This is reserved for future use. ``NONE`` Create an installer without any ``InstallScope`` attribute. - This is the default to preserve compatibility with CPack 3.28 and older. + If :variable:`CPACK_WIX_VERSION` is not set, or is set to ``3``, this + value is the default to preserve compatibility with 3.28 and lower. + Otherwise, this value is not supported. .. deprecated:: 3.29
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index fc4ac8b..35ea34f 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst
@@ -38,3 +38,25 @@ * 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. + +C++ ``import std`` support +========================== + +In order to activate support for ``import std`` in C++23 and newer targets, +set + +* variable ``CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`` to +* value ``0e5b6991-d74f-4b3d-a41c-cf096e0b2508``. + +This UUID may change in future versions of CMake. Be sure to use the value +documented here by the source tree of the version of CMake with which you are +experimenting. It must be set before the ``CXX`` toolchain is discovered by +CMake, usually as part of a :command:`project` call. + +When activated, this experimental feature provides the following: + +* The :prop_tgt:`CXX_MODULE_STD` target property and its initializing variable + :variable:`CMAKE_CXX_MODULE_STD`. + +* Targets with the property set to a true value and at least ``cxx_std_23`` + may use ``import std;`` in any scanned C++ source file.
diff --git a/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst b/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst index 77ead4d..82bd007 100644 --- a/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst +++ b/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst
@@ -4,7 +4,7 @@ .. include:: ENV_VAR.txt Extra PATH locations for custom commands when using -:generator:`Visual Studio 9 2008` (or above) generators. +:generator:`Visual Studio 12 2013` (or above) generators. The ``CMAKE_MSVCIDE_RUN_PATH`` environment variable sets the default value for the :variable:`CMAKE_MSVCIDE_RUN_PATH` variable if not already explicitly set.
diff --git a/Help/envvar/CMAKE_TLS_VERIFY.rst b/Help/envvar/CMAKE_TLS_VERIFY.rst new file mode 100644 index 0000000..9571cb8 --- /dev/null +++ b/Help/envvar/CMAKE_TLS_VERIFY.rst
@@ -0,0 +1,15 @@ +CMAKE_TLS_VERIFY +---------------- + +.. versionadded:: 3.30 + +.. include:: ENV_VAR.txt + +Specify the default value for the :command:`file(DOWNLOAD)` and +:command:`file(UPLOAD)` commands' ``TLS_VERIFY`` option. +This environment variable is used if the option is not given +and the :variable:`CMAKE_TLS_VERIFY` cmake variable is not set. + +This variable is also used by the :module:`ExternalProject` and +:module:`FetchContent` modules for internal calls to +:command:`file(DOWNLOAD)` and ``git clone``.
diff --git a/Help/envvar/CMAKE_TLS_VERSION.rst b/Help/envvar/CMAKE_TLS_VERSION.rst new file mode 100644 index 0000000..c411861 --- /dev/null +++ b/Help/envvar/CMAKE_TLS_VERSION.rst
@@ -0,0 +1,16 @@ +CMAKE_TLS_VERSION +----------------- + +.. versionadded:: 3.30 + +.. include:: ENV_VAR.txt + +Specify the default value for the :command:`file(DOWNLOAD)` and +:command:`file(UPLOAD)` commands' ``TLS_VERSION`` option. +This environment variable is used if the option is not given +and the :variable:`CMAKE_TLS_VERSION` cmake variable is not set. +See that variable for allowed values. + +This variable is also used by the :module:`ExternalProject` and +:module:`FetchContent` modules for internal calls to +:command:`file(DOWNLOAD)` and ``git clone``.
diff --git a/Help/envvar/OBJCFLAGS.rst b/Help/envvar/OBJCFLAGS.rst new file mode 100644 index 0000000..2f30807 --- /dev/null +++ b/Help/envvar/OBJCFLAGS.rst
@@ -0,0 +1,14 @@ +OBJCFLAGS +--------- + +.. versionadded:: 3.16 + +.. include:: ENV_VAR.txt + +Add default compilation flags to be used when compiling ``Objective C`` files. + +.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` +.. |LANG| replace:: ``OBJC`` +.. include:: LANG_FLAGS.txt + +See also :variable:`CMAKE_OBJC_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
diff --git a/Help/envvar/OBJCXXFLAGS.rst b/Help/envvar/OBJCXXFLAGS.rst new file mode 100644 index 0000000..089222a --- /dev/null +++ b/Help/envvar/OBJCXXFLAGS.rst
@@ -0,0 +1,14 @@ +OBJCXXFLAGS +----------- + +.. versionadded:: 3.16 + +.. include:: ENV_VAR.txt + +Add default compilation flags to be used when compiling ``Objective C++`` (.mm) files. + +.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` +.. |LANG| replace:: ``OBJCXX`` +.. include:: LANG_FLAGS.txt + +See also :variable:`CMAKE_OBJCXX_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
diff --git a/Help/generator/Ninja.rst b/Help/generator/Ninja.rst index f3ba222..5f5ffb8 100644 --- a/Help/generator/Ninja.rst +++ b/Help/generator/Ninja.rst
@@ -1,11 +1,35 @@ Ninja ----- -Generates ``build.ninja`` files. +Generates a ``build.ninja`` file into the build tree. -A ``build.ninja`` file is generated into the build tree. Use the ninja -program to build the project through the ``all`` target and install the -project through the ``install`` (or ``install/strip``) target. +Builtin Targets +^^^^^^^^^^^^^^^ + +``all`` + + Depends on all targets required by the project, except those with + :prop_tgt:`EXCLUDE_FROM_ALL` set to true. + +``install`` + + Runs the install step. + +``install/strip`` + + .. versionadded:: 3.7 + + Runs the install followed by a ``CMAKE_STRIP`` command, if any. + + The ``CMAKE_STRIP`` variable will contain the platform's ``strip`` utility, which + removes symbols information from generated binaries. + +``install/parallel`` + + .. versionadded:: 3.30 + + Created only if the :prop_gbl:`INSTALL_PARALLEL` global property is ``ON``. + Runs the install step for each subdirectory independently and in parallel. For each subdirectory ``sub/dir`` of the project, additional targets are generated: @@ -25,12 +49,10 @@ ``sub/dir/install/strip`` .. versionadded:: 3.7 + Runs the install step in the subdirectory followed by a ``CMAKE_STRIP`` command, if any. - The ``CMAKE_STRIP`` variable will contain the platform's ``strip`` utility, which - removes symbols information from generated binaries. - ``sub/dir/test`` .. versionadded:: 3.7
diff --git a/Help/generator/Visual Studio 9 2008.rst b/Help/generator/Visual Studio 9 2008.rst index 1439270..01d5af2 100644 --- a/Help/generator/Visual Studio 9 2008.rst +++ b/Help/generator/Visual Studio 9 2008.rst
@@ -1,41 +1,9 @@ Visual Studio 9 2008 -------------------- -Deprecated. Generates Visual Studio 9 2008 project files. - -.. note:: - This generator is deprecated and will be removed in a future version - of CMake. It will still be possible to build with VS 9 2008 tools - using the :generator:`Visual Studio 14 2015` generator (or above, - and with VS 10 2010 also installed) with - :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v90``, - or by using the :generator:`NMake Makefiles` generator. - -Platform Selection -^^^^^^^^^^^^^^^^^^ - -The default target platform name (architecture) is ``Win32``. - -.. versionadded:: 3.1 - The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps - via the :option:`cmake -A` option, to specify a target platform - name (architecture). For example: - - * ``cmake -G "Visual Studio 9 2008" -A Win32`` - * ``cmake -G "Visual Studio 9 2008" -A x64`` - * ``cmake -G "Visual Studio 9 2008" -A Itanium`` - * ``cmake -G "Visual Studio 9 2008" -A <WinCE-SDK>`` - (Specify a target platform matching a Windows CE SDK name.) - -For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of the generator name. -This is supported only for: - -``Visual Studio 9 2008 Win64`` - Specify target platform ``x64``. - -``Visual Studio 9 2008 IA64`` - Specify target platform ``Itanium``. - -``Visual Studio 9 2008 <WinCE-SDK>`` - Specify target platform matching a Windows CE SDK name. +Removed. This once generated Visual Studio 9 2008 project files, but +the generator has been removed since CMake 3.30. It is still possible +to build with VS 9 2008 tools using the :generator:`Visual Studio 14 2015` +generator (or above, and with VS 10 2010 also installed) with +:variable:`CMAKE_GENERATOR_TOOLSET` set to ``v90``, or by using +the :generator:`NMake Makefiles` generator.
diff --git a/Help/guide/tutorial/Adding Export Configuration.rst b/Help/guide/tutorial/Adding Export Configuration.rst index 6c83276..c4ab476 100644 --- a/Help/guide/tutorial/Adding Export Configuration.rst +++ b/Help/guide/tutorial/Adding Export Configuration.rst
@@ -46,7 +46,7 @@ which is prefixed in the source directory. -What CMake is trying to say is that during generating the export information +CMake is telling you that during the generation of the export information it will export a path that is intrinsically tied to the current machine and will not be valid on other machines. The solution to this is to update the ``MathFunctions`` :command:`target_include_directories` to understand that it
diff --git a/Help/guide/tutorial/Complete/CMakeLists.txt b/Help/guide/tutorial/Complete/CMakeLists.txt index 3cdaaae..548a82d 100644 --- a/Help/guide/tutorial/Complete/CMakeLists.txt +++ b/Help/guide/tutorial/Complete/CMakeLists.txt
@@ -89,6 +89,7 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_GENERATOR "TGZ") set(CPACK_SOURCE_GENERATOR "TGZ") include(CPack)
diff --git a/Help/guide/tutorial/Packaging an Installer.rst b/Help/guide/tutorial/Packaging an Installer.rst index 11a1952..4cca679 100644 --- a/Help/guide/tutorial/Packaging an Installer.rst +++ b/Help/guide/tutorial/Packaging an Installer.rst
@@ -23,8 +23,9 @@ some CPack variables to where we have stored the license and version information for this project. The version information was set earlier in this tutorial and the ``License.txt`` has been included in the top-level source -directory for this step. The :variable:`CPACK_SOURCE_GENERATOR` variable -selects a file format for the source package. +directory for this step. The :variable:`CPACK_GENERATOR` and +:variable:`CPACK_SOURCE_GENERATOR` variables select the generators used for +binary and source installations, respectively. Finally we include the :module:`CPack module <CPack>` which will use these variables and some other properties of the current system to setup an @@ -38,8 +39,9 @@ cpack -To specify the generator, use the :option:`-G <cpack -G>` option. For multi-config builds, -use :option:`-C <cpack -C>` to specify the configuration. For example: +To specify the binary generator, use the :option:`-G <cpack -G>` option. For +multi-config builds, use :option:`-C <cpack -C>` to specify the configuration. +For example: .. code-block:: console
diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt index 2dd6db5..40fee8d 100644 --- a/Help/guide/tutorial/Step10/CMakeLists.txt +++ b/Help/guide/tutorial/Step10/CMakeLists.txt
@@ -72,5 +72,6 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_GENERATOR "TGZ") set(CPACK_SOURCE_GENERATOR "TGZ") include(CPack)
diff --git a/Help/guide/tutorial/Step11/CMakeLists.txt b/Help/guide/tutorial/Step11/CMakeLists.txt index 046bfc9..9214c88 100644 --- a/Help/guide/tutorial/Step11/CMakeLists.txt +++ b/Help/guide/tutorial/Step11/CMakeLists.txt
@@ -80,5 +80,6 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_GENERATOR "TGZ") set(CPACK_SOURCE_GENERATOR "TGZ") include(CPack)
diff --git a/Help/guide/tutorial/Step12/CMakeLists.txt b/Help/guide/tutorial/Step12/CMakeLists.txt index 1ba4e31..a84590f 100644 --- a/Help/guide/tutorial/Step12/CMakeLists.txt +++ b/Help/guide/tutorial/Step12/CMakeLists.txt
@@ -85,6 +85,8 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_GENERATOR "TGZ") +set(CPACK_SOURCE_GENERATOR "TGZ") include(CPack) # install the configuration targets
diff --git a/Help/manual/OPTIONS_BUILD.txt b/Help/manual/OPTIONS_BUILD.txt index fb1e7d4..b8ea1ce 100644 --- a/Help/manual/OPTIONS_BUILD.txt +++ b/Help/manual/OPTIONS_BUILD.txt
@@ -108,7 +108,9 @@ .. versionadded:: 3.21 Specify the cross compiling toolchain file, equivalent to setting - :variable:`CMAKE_TOOLCHAIN_FILE` variable. + :variable:`CMAKE_TOOLCHAIN_FILE` variable. Relative paths are interpreted as + relative to the build directory, and if not found, relative to the source + directory. .. option:: --install-prefix <directory>
diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst index b88b864..eb25a4a 100644 --- a/Help/manual/cmake-buildsystem.7.rst +++ b/Help/manual/cmake-buildsystem.7.rst
@@ -162,103 +162,285 @@ Build Specification and Usage Requirements ========================================== -The :command:`target_include_directories`, :command:`target_compile_definitions` -and :command:`target_compile_options` commands specify the build specifications -and the usage requirements of binary targets. The commands populate the -:prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_DEFINITIONS` and -:prop_tgt:`COMPILE_OPTIONS` target properties respectively, and/or the -:prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`, :prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` -and :prop_tgt:`INTERFACE_COMPILE_OPTIONS` target properties. +Targets build according to their own +`build specification <Target Build Specification_>`_ in combination with +`usage requirements <Target Usage Requirements_>`_ propagated from their +link dependencies. Both may be specified using target-specific +`commands <Target Commands_>`_. -Each of the commands has a ``PRIVATE``, ``PUBLIC`` and ``INTERFACE`` mode. The -``PRIVATE`` mode populates only the non-``INTERFACE_`` variant of the target -property and the ``INTERFACE`` mode populates only the ``INTERFACE_`` variants. -The ``PUBLIC`` mode populates both variants of the respective target property. -Each command may be invoked with multiple uses of each keyword: +For example: .. code-block:: cmake - target_compile_definitions(archive - PRIVATE BUILDING_WITH_LZMA - INTERFACE USING_ARCHIVE_LIB - ) + add_library(archive SHARED archive.cpp zip.cpp) + + if (LZMA_FOUND) + # Add a source implementing support for lzma. + target_sources(archive PRIVATE lzma.cpp) + + # Compile the 'archive' library sources with '-DBUILDING_WITH_LZMA'. + target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA) + endif() + + target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB) + + add_executable(consumer consumer.cpp) + + # Link 'consumer' to 'archive'. This also consumes its usage requirements, + # so 'consumer.cpp' is compiled with '-DUSING_ARCHIVE_LIB'. + target_link_libraries(consumer archive) + + +Target Commands +--------------- + +Target-specific commands populate the +`build specification <Target Build Specification_>`_ of `Binary Targets`_ and +`usage requirements <Target Usage Requirements_>`_ of `Binary Targets`_, +`Interface Libraries`_, and `Imported Targets`_. + +.. _`Target Command Scope`: + +Invocations must specify scope keywords, each affecting the visibility +of arguments following it. The scopes are: + +``PUBLIC`` + Populates both properties for `building <Target Build Specification_>`_ + and properties for `using <Target Usage Requirements_>`_ a target. + +``PRIVATE`` + Populates only properties for `building <Target Build Specification_>`_ + a target. + +``INTERFACE`` + Populates only properties for `using <Target Usage Requirements_>`_ + a target. + +The commands are: + +:command:`target_compile_definitions` + Populates the :prop_tgt:`COMPILE_DEFINITIONS` build specification and + :prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` usage requirement properties. + + For example, the call + + .. code-block:: cmake + + target_compile_definitions(archive + PRIVATE BUILDING_WITH_LZMA + INTERFACE USING_ARCHIVE_LIB + ) + + appends ``BUILDING_WITH_LZMA`` to the target's ``COMPILE_DEFINITIONS`` + property and appends ``USING_ARCHIVE_LIB`` to the target's + ``INTERFACE_COMPILE_DEFINITIONS`` property. + +:command:`target_compile_options` + Populates the :prop_tgt:`COMPILE_OPTIONS` build specification and + :prop_tgt:`INTERFACE_COMPILE_OPTIONS` usage requirement properties. + +:command:`target_compile_features` + .. versionadded:: 3.1 + + Populates the :prop_tgt:`COMPILE_FEATURES` build specification and + :prop_tgt:`INTERFACE_COMPILE_FEATURES` usage requirement properties. + +:command:`target_include_directories` + Populates the :prop_tgt:`INCLUDE_DIRECTORIES` build specification + and :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` usage requirement + properties. With the ``SYSTEM`` option, it also populates the + :prop_tgt:`INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` usage requirement. + + For convenience, the :variable:`CMAKE_INCLUDE_CURRENT_DIR` variable + may be enabled to add the source directory and corresponding build + directory as ``INCLUDE_DIRECTORIES`` on all targets. Similarly, + the :variable:`CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE` variable may + be enabled to add them as ``INTERFACE_INCLUDE_DIRECTORIES`` on all + targets. + +:command:`target_sources` + .. versionadded:: 3.1 + + Populates the :prop_tgt:`SOURCES` build specification and + :prop_tgt:`INTERFACE_SOURCES` usage requirement properties. + + It also supports specifying :ref:`File Sets`, which can add C++ module + sources and headers not listed in the ``SOURCES`` and ``INTERFACE_SOURCES`` + properties. File sets may also populate the :prop_tgt:`INCLUDE_DIRECTORIES` + build specification and :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` usage + requirement properties with the include directories containing the headers. + +:command:`target_precompile_headers` + .. versionadded:: 3.16 + + Populates the :prop_tgt:`PRECOMPILE_HEADERS` build specification and + :prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` usage requirement properties. + +:command:`target_link_libraries` + Populates the :prop_tgt:`LINK_LIBRARIES` build specification + and :prop_tgt:`INTERFACE_LINK_LIBRARIES` usage requirement properties. + + This is the primary mechanism by which link dependencies and their + `usage requirements <Target Usage Requirements_>`_ are transitively + propagated to affect compilation and linking of a target. + +:command:`target_link_directories` + .. versionadded:: 3.13 + + Populates the :prop_tgt:`LINK_DIRECTORIES` build specification and + :prop_tgt:`INTERFACE_LINK_DIRECTORIES` usage requirement properties. + +:command:`target_link_options` + .. versionadded:: 3.13 + + Populates the :prop_tgt:`LINK_OPTIONS` build specification and + :prop_tgt:`INTERFACE_LINK_OPTIONS` usage requirement properties. + +.. _`Target Build Specification`: + +Target Build Specification +-------------------------- + +The build specification of `Binary Targets`_ is represented by target +properties. For each of the following `compile <Target Compile Properties_>`_ +and `link <Target Link Properties_>`_ properties, compilation and linking +of the target is affected both by its own value and by the corresponding +`usage requirement <Target Usage Requirements_>`_ property, named with +an ``INTERFACE_`` prefix, collected from the transitive closure of link +dependencies. + +.. _`Target Compile Properties`: + +Target Compile Properties +^^^^^^^^^^^^^^^^^^^^^^^^^ + +These represent the `build specification <Target Build Specification_>`_ +for compiling a target. + +:prop_tgt:`COMPILE_DEFINITIONS` + List of compile definitions for compiling sources in the target. + These are passed to the compiler with ``-D`` flags, or equivalent, + in an unspecified order. + + The :prop_tgt:`DEFINE_SYMBOL` target property is also used + as a compile definition as a special convenience case for + ``SHARED`` and ``MODULE`` library targets. + +:prop_tgt:`COMPILE_OPTIONS` + List of compile options for compiling sources in the target. + These are passed to the compiler as flags, in the order of appearance. + + Compile options are automatically escaped for the shell. + + Some compile options are best specified via dedicated settings, + such as the :prop_tgt:`POSITION_INDEPENDENT_CODE` target property. + +:prop_tgt:`COMPILE_FEATURES` + .. versionadded:: 3.1 + + List of :manual:`compile features <cmake-compile-features(7)>` needed + for compiling sources in the target. Typically these ensure the + target's sources are compiled using a sufficient language standard level. + +:prop_tgt:`INCLUDE_DIRECTORIES` + List of include directories for compiling sources in the target. + These are passed to the compiler with ``-I`` or ``-isystem`` flags, + or equivalent, in the order of appearance. + + For convenience, the :variable:`CMAKE_INCLUDE_CURRENT_DIR` variable + may be enabled to add the source directory and corresponding build + directory as ``INCLUDE_DIRECTORIES`` on all targets. + +:prop_tgt:`SOURCES` + List of source files associated with the target. This includes sources + specified when the target was created by the :command:`add_executable`, + :command:`add_library`, or :command:`add_custom_target` command. + It also includes sources added by the :command:`target_sources` command, + but does not include :ref:`File Sets`. + +:prop_tgt:`PRECOMPILE_HEADERS` + .. versionadded:: 3.16 + + List of header files to precompile and include when compiling + sources in the target. + +:prop_tgt:`AUTOMOC_MACRO_NAMES` + .. versionadded:: 3.10 + + List of macro names used by :prop_tgt:`AUTOMOC` to determine if a + C++ source in the target needs to be processed by ``moc``. + +:prop_tgt:`AUTOUIC_OPTIONS` + .. versionadded:: 3.0 + + List of options used by :prop_tgt:`AUTOUIC` when invoking ``uic`` + for the target. + +.. _`Target Link Properties`: + +Target Link Properties +^^^^^^^^^^^^^^^^^^^^^^ + +These represent the `build specification <Target Build Specification_>`_ +for linking a target. + +:prop_tgt:`LINK_LIBRARIES` + List of link libraries for linking the target, if it is an executable, + shared library, or module library. Entries for `Normal Libraries`_ are + passed to the linker either via paths to their link artifacts, or + with ``-l`` flags or equivalent. Entries for `Object Libraries`_ are + passed to the linker via paths to their object files. + + Additionally, for compiling and linking the target itself, + `usage requirements <Target Usage Requirements_>`_ are propagated from + ``LINK_LIBRARIES`` entries naming `Normal Libraries`_, + `Interface Libraries`_, `Object Libraries`_, and `Imported Targets`_, + collected over the transitive closure of their + :prop_tgt:`INTERFACE_LINK_LIBRARIES` properties. + +:prop_tgt:`LINK_DIRECTORIES` + .. versionadded:: 3.13 + + List of link directories for linking the target, if it is an executable, + shared library, or module library. The directories are passed to the + linker with ``-L`` flags, or equivalent. + +:prop_tgt:`LINK_OPTIONS` + .. versionadded:: 3.13 + + List of link options for linking the target, if it is an executable, + shared library, or module library. The options are passed to the + linker as flags, in the order of appearance. + + Link options are automatically escaped for the shell. + +:prop_tgt:`LINK_DEPENDS` + List of files on which linking the target depends, if it is an executable, + shared library, or module library. For example, linker scripts specified + via :prop_tgt:`LINK_OPTIONS` may be listed here such that changing them + causes binaries to be linked again. + +.. _`Target Usage Requirements`: + +Target Usage Requirements +------------------------- + +The *usage requirements* of a target are settings that propagate to consumers, +which link to the target via :command:`target_link_libraries`, in order to +correctly compile and link with it. They are represented by transitive +`compile <Transitive Compile Properties_>`_ and +`link <Transitive Link Properties_>`_ properties. Note that usage requirements are not designed as a way to make downstreams -use particular :prop_tgt:`COMPILE_OPTIONS` or -:prop_tgt:`COMPILE_DEFINITIONS` etc for convenience only. The contents of -the properties must be **requirements**, not merely recommendations or -convenience. +use particular :prop_tgt:`COMPILE_OPTIONS`, :prop_tgt:`COMPILE_DEFINITIONS`, +etc. for convenience only. The contents of the properties must be +**requirements**, not merely recommendations. See the :ref:`Creating Relocatable Packages` section of the :manual:`cmake-packages(7)` manual for discussion of additional care that must be taken when specifying usage requirements while creating packages for redistribution. -Target Properties ------------------ - -The contents of the :prop_tgt:`INCLUDE_DIRECTORIES`, -:prop_tgt:`COMPILE_DEFINITIONS` and :prop_tgt:`COMPILE_OPTIONS` target -properties are used appropriately when compiling the source files of a -binary target. - -Entries in the :prop_tgt:`INCLUDE_DIRECTORIES` are added to the compile line -with ``-I`` or ``-isystem`` prefixes and in the order of appearance in the -property value. - -Entries in the :prop_tgt:`COMPILE_DEFINITIONS` are prefixed with ``-D`` or -``/D`` and added to the compile line in an unspecified order. The -:prop_tgt:`DEFINE_SYMBOL` target property is also added as a compile -definition as a special convenience case for ``SHARED`` and ``MODULE`` -library targets. - -Entries in the :prop_tgt:`COMPILE_OPTIONS` are escaped for the shell and added -in the order of appearance in the property value. Several compile options have -special separate handling, such as :prop_tgt:`POSITION_INDEPENDENT_CODE`. - -The contents of the :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`, -:prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` and -:prop_tgt:`INTERFACE_COMPILE_OPTIONS` target properties are -*Usage Requirements* -- they specify content which consumers -must use to correctly compile and link with the target they appear on. -For any binary target, the contents of each ``INTERFACE_`` property on -each target specified in a :command:`target_link_libraries` command is -consumed: - -.. code-block:: cmake - - set(srcs archive.cpp zip.cpp) - if (LZMA_FOUND) - list(APPEND srcs lzma.cpp) - endif() - add_library(archive SHARED ${srcs}) - if (LZMA_FOUND) - # The archive library sources are compiled with -DBUILDING_WITH_LZMA - target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA) - endif() - target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB) - - add_executable(consumer) - # Link consumer to archive and consume its usage requirements. The consumer - # executable sources are compiled with -DUSING_ARCHIVE_LIB. - target_link_libraries(consumer archive) - -Because it is common to require that the source directory and corresponding -build directory are added to the :prop_tgt:`INCLUDE_DIRECTORIES`, the -:variable:`CMAKE_INCLUDE_CURRENT_DIR` variable can be enabled to conveniently -add the corresponding directories to the :prop_tgt:`INCLUDE_DIRECTORIES` of -all targets. The variable :variable:`CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE` -can be enabled to add the corresponding directories to the -:prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` of all targets. This makes use of -targets in multiple different directories convenient through use of the -:command:`target_link_libraries` command. - - -.. _`Target Usage Requirements`: - -Transitive Usage Requirements ------------------------------ - The usage requirements of a target can transitively propagate to the dependents. The :command:`target_link_libraries` command has ``PRIVATE``, ``INTERFACE`` and ``PUBLIC`` keywords to control the propagation. @@ -329,6 +511,156 @@ which will be exported for installation using the :command:`install(EXPORT)` command. See :ref:`Creating Packages` for more. +.. _`Transitive Compile Properties`: + +Transitive Compile Properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These represent `usage requirements <Target Usage Requirements_>`_ for +compiling consumers. + +:prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` + List of compile definitions for compiling sources in the target's consumers. + Typically these are used by the target's header files. + +:prop_tgt:`INTERFACE_COMPILE_OPTIONS` + List of compile options for compiling sources in the target's consumers. + +:prop_tgt:`INTERFACE_COMPILE_FEATURES` + .. versionadded:: 3.1 + + List of :manual:`compile features <cmake-compile-features(7)>` needed + for compiling sources in the target's consumers. Typically these + ensure the target's header files are processed when compiling consumers + using a sufficient language standard level. + +:prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` + List of include directories for compiling sources in the target's consumers. + Typically these are the locations of the target's header files. + +:prop_tgt:`INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` + List of directories that, when specified as include directories, e.g., by + :prop_tgt:`INCLUDE_DIRECTORIES` or :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`, + should be treated as "system" include directories when compiling sources + in the target's consumers. + +:prop_tgt:`INTERFACE_SOURCES` + List of source files to associate with the target's consumers. + +:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` + .. versionadded:: 3.16 + + List of header files to precompile and include when compiling + sources in the target's consumers. + +:prop_tgt:`INTERFACE_AUTOMOC_MACRO_NAMES` + .. versionadded:: 3.27 + + List of macro names used by :prop_tgt:`AUTOMOC` to determine if a + C++ source in the target's consumers needs to be processed by ``moc``. + +:prop_tgt:`INTERFACE_AUTOUIC_OPTIONS` + .. versionadded:: 3.0 + + List of options used by :prop_tgt:`AUTOUIC` when invoking ``uic`` + for the target's consumers. + +.. _`Transitive Link Properties`: + +Transitive Link Properties +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These represent `usage requirements <Target Usage Requirements_>`_ for +linking consumers. + +:prop_tgt:`INTERFACE_LINK_LIBRARIES` + List of link libraries for linking the target's consumers, for + those that are executables, shared libraries, or module libraries. + These are the transitive dependencies of the target. + + Additionally, for compiling and linking the target's consumers, + `usage requirements <Target Usage Requirements_>`_ are collected from + the transitive closure of ``INTERFACE_LINK_LIBRARIES`` entries naming + `Normal Libraries`_, `Interface Libraries`_, `Object Libraries`_, + and `Imported Targets`_, + +:prop_tgt:`INTERFACE_LINK_DIRECTORIES` + .. versionadded:: 3.13 + + List of link directories for linking the target's consumers, for + those that are executables, shared libraries, or module libraries. + +:prop_tgt:`INTERFACE_LINK_OPTIONS` + .. versionadded:: 3.13 + + List of link options for linking the target's consumers, for + those that are executables, shared libraries, or module libraries. + +:prop_tgt:`INTERFACE_LINK_DEPENDS` + .. versionadded:: 3.13 + + List of files on which linking the target's consumers depends, for + those that are executables, shared libraries, or module libraries. + +.. _`Custom Transitive Properties`: + +Custom Transitive Properties +---------------------------- + +.. versionadded:: 3.30 + +The :genex:`TARGET_PROPERTY` generator expression evaluates the above +`build specification <Target Build Specification_>`_ and +`usage requirement <Target Usage Requirements_>`_ properties +as builtin transitive properties. It also supports custom transitive +properties defined by the :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` +and :prop_tgt:`TRANSITIVE_LINK_PROPERTIES` properties on the target +and its link dependencies. + +For example: + +.. code-block:: cmake + + add_library(example INTERFACE) + set_target_properties(example PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_C" + TRANSITIVE_LINK_PROPERTIES "CUSTOM_L" + + INTERFACE_CUSTOM_C "EXAMPLE_CUSTOM_C" + INTERFACE_CUSTOM_L "EXAMPLE_CUSTOM_L" + ) + + add_library(mylib STATIC mylib.c) + target_link_libraries(mylib PRIVATE example) + set_target_properties(mylib PROPERTIES + CUSTOM_C "MYLIB_PRIVATE_CUSTOM_C" + CUSTOM_L "MYLIB_PRIVATE_CUSTOM_L" + INTERFACE_CUSTOM_C "MYLIB_IFACE_CUSTOM_C" + INTERFACE_CUSTOM_L "MYLIB_IFACE_CUSTOM_L" + ) + + add_executable(myexe myexe.c) + target_link_libraries(myexe PRIVATE mylib) + set_target_properties(myexe PROPERTIES + CUSTOM_C "MYEXE_CUSTOM_C" + CUSTOM_L "MYEXE_CUSTOM_L" + ) + + add_custom_target(print ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -E echo + # Prints "MYLIB_PRIVATE_CUSTOM_C;EXAMPLE_CUSTOM_C" + "$<TARGET_PROPERTY:mylib,CUSTOM_C>" + + # Prints "MYLIB_PRIVATE_CUSTOM_L;EXAMPLE_CUSTOM_L" + "$<TARGET_PROPERTY:mylib,CUSTOM_L>" + + # Prints "MYEXE_CUSTOM_C" + "$<TARGET_PROPERTY:myexe,CUSTOM_C>" + + # Prints "MYEXE_CUSTOM_L;MYLIB_IFACE_CUSTOM_L;EXAMPLE_CUSTOM_L" + "$<TARGET_PROPERTY:myexe,CUSTOM_L>" + ) + .. _`Compatible Interface Properties`: Compatible Interface Properties
diff --git a/Help/manual/cmake-cxxmodules.7.rst b/Help/manual/cmake-cxxmodules.7.rst index 3ee6645..201a415 100644 --- a/Help/manual/cmake-cxxmodules.7.rst +++ b/Help/manual/cmake-cxxmodules.7.rst
@@ -12,6 +12,49 @@ scanning results to infer ordering constraints, and tells the build tool how to dynamically update the build graph. +Compilation Strategy +==================== + +With C++ modules, compiling a set of C++ sources is no longer embarrassingly +parallel. That is, any given source may first require the compilation of +another source file first in order to provide a "CMI" (compiled module +interface) or "BMI" (binary module interface) that C++ compilers use to +satisfy ``import`` statements in other sources. With headers, sources could +share their declarations so that any consumers could compile independently. +With modules, declarations are now generated into these BMI files by the +compiler during compilation based on the contents of the source file and its +``export`` statements. + +The order necessary for compilation requires build-time resolution of the +ordering because the order is controlled by the contents of the sources. This +means that the ordering needs extracted from the source during the build to +avoid regenerating the build graph via a configure and generate phase for +every source change to get a correct build. + +The general strategy is to use a "scanner" to extract the ordering dependency +information and update the build graph with new edges between existing edges +by taking the per-source scan results (represented by `P1689R5`_ files) and +"collating" the dependencies within a target and to modules produced by +targets visible to the target. The primary task is to generate "module map" +files to pass to each compile rule with the paths to the BMIs needed to +satisfy ``import`` statements. The collator also has tasks to use the +build-time information to fill out information including ``install`` rules for +the module interface units, their BMIs, and properties for any exported +targets with C++ modules. + +.. _`P1689R5`: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html + +.. note:: + + CMake is focusing on correct builds before looking at performance + improvements. There are known tactics within the chosen strategy which may + offer build performance improvements. However, they are being deferred + until we have a working model against which to compare them. It is also + important to note that a tactic useful in one situation (e.g., clean + builds) may not be performant in a different situation (e.g., incremental + builds). Finding a balance and offering controls to select the tactics is + future work. + Scanning Control ================ @@ -43,6 +86,25 @@ * LLVM/Clang 16.0 and newer * GCC 14 (for the in-development branch, after 2023-09-20) and newer +``import std`` Support +====================== + +Support for ``import std`` is limited to the following toolchain and standard +library combinations: + +* Clang 18.1.2 and newer with ``-stdlib=libc++`` +* MSVC toolset 14.36 and newer (provided with Visual Studio 17.6 Preview 2 and + newer) + +The :variable:`CMAKE_CXX_COMPILER_IMPORT_STD` variable may be used to detect +support for a standard level with the active C++ toolchain. + +.. note :: + + This support is provided only when experimental support for + ``import std;`` has been enabled by the + ``CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`` gate. + Generator Support ================= @@ -73,6 +135,7 @@ - Only Visual Studio 2022 and MSVC toolsets 14.34 (Visual Studio 17.4) and newer. - No support for exporting or installing BMI or module information. -- No support for compiling BMIs from ``IMPORTED`` targets with C++ modules. +- No support for compiling BMIs from ``IMPORTED`` targets with C++ modules + (including ``import std``). - No diagnosis of using modules provided by ``PRIVATE`` sources from ``PUBLIC`` module sources.
diff --git a/Help/manual/cmake-developer.7.rst b/Help/manual/cmake-developer.7.rst index a09bd14..cebe8f9 100644 --- a/Help/manual/cmake-developer.7.rst +++ b/Help/manual/cmake-developer.7.rst
@@ -119,7 +119,8 @@ The more modern approach is to behave as much like :ref:`config file packages <Config File Packages>` files as possible, by providing :ref:`imported target <Imported targets>`. This has the advantage -of propagating :ref:`Target Usage Requirements` to consumers. +of propagating :ref:`usage requirements <Target Usage Requirements>` +to consumers. In either case (or even when providing both variables and imported targets), find modules should provide backwards compatibility with old
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst index 3ab5935..a69ace6 100644 --- a/Help/manual/cmake-env-variables.7.rst +++ b/Help/manual/cmake-env-variables.7.rst
@@ -27,6 +27,8 @@ /envvar/CMAKE_MAXIMUM_RECURSION_DEPTH /envvar/CMAKE_PREFIX_PATH /envvar/CMAKE_PROGRAM_PATH + /envvar/CMAKE_TLS_VERIFY + /envvar/CMAKE_TLS_VERSION /envvar/SSL_CERT_DIR /envvar/SSL_CERT_FILE @@ -90,7 +92,9 @@ /envvar/ISPC /envvar/ISPCFLAGS /envvar/OBJC + /envvar/OBJCFLAGS /envvar/OBJCXX + /envvar/OBJCXXFLAGS /envvar/RC /envvar/RCFLAGS /envvar/SWIFTC
diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index c249ee2..260030e 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst
@@ -1489,7 +1489,7 @@ { "kind": "cmakeFiles", - "version": { "major": 1, "minor": 0 }, + "version": { "major": 1, "minor": 1 }, "paths": { "build": "/path/to/top-level-build-dir", "source": "/path/to/top-level-source-dir" @@ -1511,6 +1511,16 @@ "isExternal": true, "path": "/path/to/cmake/Modules/CMakeGenericSystem.cmake" } + ], + "globsDependent": [ + { + "expression": "src/*.cxx", + "recurse": true, + "files": [ + "src/foo.cxx", + "src/bar.cxx" + ] + } ] } @@ -1553,6 +1563,44 @@ Optional member that is present with boolean value ``true`` if the path specifies a file in the CMake installation. +``globsDependent`` + 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 + expression and the list of paths it matched. If the globbing expression + 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`` + A string specifying the globbing expression. + + ``recurse`` + Optional member that is present with boolean value ``true`` + if the entry corresponds to a :command:`file(GLOB_RECURSE)` call. + Otherwise the entry corresponds to a :command:`file(GLOB)` call. + + ``listDirectories`` + Optional member that is present with boolean value ``true`` if + :command:`file(GLOB)` was called without ``LIST_DIRECTORIES false`` or + :command:`file(GLOB_RECURSE)` was called with ``LIST_DIRECTORIES true``. + + ``followSymlinks`` + Optional member that is present with boolean value ``true`` if + :command:`file(GLOB)` was called with the ``FOLLOW_SYMLINKS`` option. + + ``relative`` + Optional member that is present if :command:`file(GLOB)` was called + with the ``RELATIVE <path>`` option. The value is a string containing + the ``<path>`` given. + + ``paths`` + A JSON array of strings specifying the paths matched by the call + to :command:`file(GLOB)` or :command:`file(GLOB_RECURSE)`. + Object Kind "toolchains" ------------------------
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index a0b5b66..26a4a60 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -967,7 +967,7 @@ .. genex:: $<CXX_COMPILER_VERSION:version> - ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``. + ``1`` if the version of the C++ compiler matches ``version``, otherwise ``0``. .. genex:: $<CUDA_COMPILER_VERSION> @@ -979,31 +979,31 @@ .. versionadded:: 3.15 - ``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``. + ``1`` if the version of the C++ compiler matches ``version``, otherwise ``0``. .. genex:: $<OBJC_COMPILER_VERSION> .. versionadded:: 3.16 - The version of the OBJC compiler used. + The version of the Objective-C compiler used. .. genex:: $<OBJC_COMPILER_VERSION:version> .. versionadded:: 3.16 - ``1`` if the version of the OBJC compiler matches ``version``, otherwise ``0``. + ``1`` if the version of the Objective-C compiler matches ``version``, otherwise ``0``. .. genex:: $<OBJCXX_COMPILER_VERSION> .. versionadded:: 3.16 - The version of the OBJCXX compiler used. + The version of the Objective-C++ compiler used. .. genex:: $<OBJCXX_COMPILER_VERSION:version> .. versionadded:: 3.16 - ``1`` if the version of the OBJCXX compiler matches ``version``, otherwise ``0``. + ``1`` if the version of the Objective-C++ compiler matches ``version``, otherwise ``0``. .. genex:: $<Fortran_COMPILER_VERSION> @@ -1037,10 +1037,11 @@ ``1`` if the version of the ISPC compiler matches ``version``, otherwise ``0``. -Compiler Language And ID -^^^^^^^^^^^^^^^^^^^^^^^^ +Compiler Language, ID, and Frontend-Variant +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable, which is closely +See also the :variable:`CMAKE_<LANG>_COMPILER_ID` and +:variable:`CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT` variables, which are closely related to most of the expressions in this sub-section. .. genex:: $<C_COMPILER_ID> @@ -1059,12 +1060,12 @@ .. genex:: $<CXX_COMPILER_ID> - CMake's compiler id of the CXX compiler used. + CMake's compiler id of the C++ compiler used. .. genex:: $<CXX_COMPILER_ID:compiler_ids> where ``compiler_ids`` is a comma-separated list. - ``1`` if CMake's compiler id of the CXX compiler matches any one + ``1`` if CMake's compiler id of the C++ compiler matches any one of the entries in ``compiler_ids``, otherwise ``0``. .. versionchanged:: 3.15 @@ -1089,7 +1090,7 @@ .. versionadded:: 3.16 - CMake's compiler id of the OBJC compiler used. + CMake's compiler id of the Objective-C compiler used. .. genex:: $<OBJC_COMPILER_ID:compiler_ids> @@ -1103,7 +1104,7 @@ .. versionadded:: 3.16 - CMake's compiler id of the OBJCXX compiler used. + CMake's compiler id of the Objective-C++ compiler used. .. genex:: $<OBJCXX_COMPILER_ID:compiler_ids> @@ -1155,6 +1156,118 @@ ``1`` if CMake's compiler id of the ISPC compiler matches any one of the entries in ``compiler_ids``, otherwise ``0``. +.. genex:: $<C_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler frontend variant of the C compiler used. + +.. genex:: $<C_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the C compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<CXX_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler frontend variant of the C++ compiler used. + +.. genex:: $<CXX_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the C++ compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<CUDA_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler id of the CUDA compiler used. + +.. genex:: $<CUDA_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the CUDA compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<OBJC_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler frontend variant of the Objective-C compiler used. + +.. genex:: $<OBJC_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the Objective-C compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<OBJCXX_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler frontend variant of the Objective-C++ compiler used. + +.. genex:: $<OBJCXX_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the Objective-C++ compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<Fortran_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler id of the Fortran compiler used. + +.. genex:: $<Fortran_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the Fortran compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<HIP_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler id of the HIP compiler used. + +.. genex:: $<HIP_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the HIP compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + +.. genex:: $<ISPC_COMPILER_FRONTEND_VARIANT> + + .. versionadded:: 3.30 + + CMake's compiler id of the ISPC compiler used. + +.. genex:: $<ISPC_COMPILER_FRONTEND_VARIANT:compiler_ids> + + .. versionadded:: 3.30 + + where ``compiler_ids`` is a comma-separated list. + ``1`` if CMake's compiler frontend variant of the ISPC compiler matches any one + of the entries in ``compiler_ids``, otherwise ``0``. + .. genex:: $<COMPILE_LANGUAGE> .. versionadded:: 3.3 @@ -1281,7 +1394,8 @@ .. versionadded:: 3.27 - Content of ``...``, when collecting :ref:`Target Usage Requirements`, + Content of ``...``, when collecting + :ref:`transitive compile properties <Transitive Compile Properties>`, otherwise it is the empty string. This is intended for use in an :prop_tgt:`INTERFACE_LINK_LIBRARIES` and :prop_tgt:`LINK_LIBRARIES` target properties, typically populated via the :command:`target_link_libraries` command. @@ -1669,7 +1783,8 @@ .. versionadded:: 3.1 - Content of ``...``, except while collecting :ref:`Target Usage Requirements`, + Content of ``...``, except while collecting usage requirements from + :ref:`transitive compile properties <Transitive Compile Properties>`, in which case it is the empty string. This is intended for use in an :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated via the :command:`target_link_libraries` command, to specify private link @@ -1700,22 +1815,15 @@ only be used to specify link options. -.. _`Target-Dependent Queries`: +.. _`Target-Dependent Expressions`: Target-Dependent Expressions ---------------------------- -These queries refer to a target ``tgt``. Unless otherwise stated, this can -be any runtime artifact, namely: +Target Meta-Data +^^^^^^^^^^^^^^^^ -* An executable target created by :command:`add_executable`. -* A shared library target (``.so``, ``.dll`` but not their ``.lib`` import - library) created by :command:`add_library`. -* A static library target created by :command:`add_library`. - -In the following, the phrase "the ``tgt`` filename" means the name of the -``tgt`` binary file. This has to be distinguished from the phrase -"the target name", which is just the string ``tgt``. +These expressions look up information about a target. .. genex:: $<TARGET_EXISTS:tgt> @@ -1732,15 +1840,32 @@ Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on. -.. genex:: $<TARGET_NAME:...> +.. genex:: $<TARGET_NAME:tgt> - Marks ``...`` as being the name of a target. This is required if exporting - targets to multiple dependent export sets. The ``...`` must be a literal - name of a target, it may not contain generator expressions. + The target name ``tgt`` as written. This marks ``tgt`` as being the name + of a target inside a larger expression, which is required if exporting + targets to multiple dependent export sets. The ``tgt`` text must be a + literal name of a target; it may not contain generator expressions. + The target does not have to exist. + +.. genex:: $<TARGET_POLICY:policy> + + ``1`` if the ``policy`` was ``NEW`` when the 'head' target was created, + else ``0``. If the ``policy`` was not set, the warning message for the policy + will be emitted. This generator expression only works for a subset of + policies. + + +Target Properties +^^^^^^^^^^^^^^^^^ + +These expressions look up the values of +:ref:`target properties <Target Properties>`. .. genex:: $<TARGET_PROPERTY:tgt,prop> - Value of the property ``prop`` on the target ``tgt``. + Value of the property ``prop`` on the target ``tgt``, or empty if + the property is not set. Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on. @@ -1756,23 +1881,129 @@ :target: TARGET_PROPERTY:prop Value of the property ``prop`` on the target for which the expression - is being evaluated. Note that for generator expressions in - :ref:`Target Usage Requirements` this is the consuming target rather - than the target specifying the requirement. + is being evaluated, or empty if the property is not set. + Note that for generator expressions in :ref:`Target Usage Requirements` + this is the consuming target rather than the target specifying the + requirement. -.. genex:: $<TARGET_OBJECTS:tgt> +The expressions have special evaluation rules for some properties: - .. versionadded:: 3.1 +:ref:`Target Build Specification Properties <Target Build Specification>` + These evaluate as a :ref:`semicolon-separated list <CMake Language Lists>` + representing the union of the value on the target itself with the values + of the corresponding :ref:`Target Usage Requirements` on targets named by + the target's :prop_tgt:`LINK_LIBRARIES`: - List of objects resulting from building ``tgt``. This would typically be - used on :ref:`object library <Object Libraries>` targets. + * For :ref:`Target Compile Properties`, evaluation of corresponding usage + requirements is transitive over the closure of the linked targets' + :prop_tgt:`INTERFACE_LINK_LIBRARIES` *excluding* entries guarded by the + :genex:`LINK_ONLY` generator expression. -.. genex:: $<TARGET_POLICY:policy> + * For :ref:`Target Link Properties`, evaluation of corresponding usage + requirements is transitive over the closure of the linked targets' + :prop_tgt:`INTERFACE_LINK_LIBRARIES` *including* entries guarded by the + :genex:`LINK_ONLY` generator expression. See policy :policy:`CMP0166`. - ``1`` if the ``policy`` was ``NEW`` when the 'head' target was created, - else ``0``. If the ``policy`` was not set, the warning message for the policy - will be emitted. This generator expression only works for a subset of - policies. + Evaluation of :prop_tgt:`LINK_LIBRARIES` itself is not transitive. + +:ref:`Target Usage Requirement Properties <Target Usage Requirements>` + These evaluate as a :ref:`semicolon-separated list <CMake Language Lists>` + representing the union of the value on the target itself with the values + of the same properties on targets named by the target's + :prop_tgt:`INTERFACE_LINK_LIBRARIES`: + + * For :ref:`Transitive Compile Properties`, evaluation is transitive over + the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES` + *excluding* entries guarded by the :genex:`LINK_ONLY` generator expression. + + * For :ref:`Transitive Link Properties`, evaluation is transitive over + the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES` + *including* entries guarded by the :genex:`LINK_ONLY` generator expression. + See policy :policy:`CMP0166`. + + Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is not transitive. + +:ref:`Custom Transitive Properties` + .. versionadded:: 3.30 + + These are processed during evaluation as follows: + + * Evaluation of :genex:`$<TARGET_PROPERTY:tgt,PROP>` for some property + ``PROP``, named without an ``INTERFACE_`` prefix, + checks the :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` + and :prop_tgt:`TRANSITIVE_LINK_PROPERTIES` properties on target ``tgt``, + on targets named by its :prop_tgt:`LINK_LIBRARIES`, and on the + transitive closure of targets named by the linked targets' + :prop_tgt:`INTERFACE_LINK_LIBRARIES`. + + If ``PROP`` is listed by one of those properties, then it evaluates as + a :ref:`semicolon-separated list <CMake Language Lists>` representing + the union of the value on the target itself with the values of the + corresponding ``INTERFACE_PROP`` on targets named by the target's + :prop_tgt:`LINK_LIBRARIES`: + + * If ``PROP`` is named by :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES`, + evaluation of the corresponding ``INTERFACE_PROP`` is transitive over + the closure of the linked targets' :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + excluding entries guarded by the :genex:`LINK_ONLY` generator expression. + + * If ``PROP`` is named by :prop_tgt:`TRANSITIVE_LINK_PROPERTIES`, + evaluation of the corresponding ``INTERFACE_PROP`` is transitive over + the closure of the linked targets' :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + including entries guarded by the :genex:`LINK_ONLY` generator expression. + + * Evaluation of :genex:`$<TARGET_PROPERTY:tgt,INTERFACE_PROP>` for some + property ``INTERFACE_PROP``, named with an ``INTERFACE_`` prefix, + checks the :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` + and :prop_tgt:`TRANSITIVE_LINK_PROPERTIES` properties on target ``tgt``, + and on the transitive closure of targets named by its + :prop_tgt:`INTERFACE_LINK_LIBRARIES`. + + If the corresponding ``PROP`` is listed by one of those properties, + then ``INTERFACE_PROP`` evaluates as a + :ref:`semicolon-separated list <CMake Language Lists>` representing the + union of the value on the target itself with the value of the same + property on targets named by the target's + :prop_tgt:`INTERFACE_LINK_LIBRARIES`: + + * If ``PROP`` is named by :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES`, + evaluation of the corresponding ``INTERFACE_PROP`` is transitive over + the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + excluding entries guarded by the :genex:`LINK_ONLY` generator expression. + + * If ``PROP`` is named by :prop_tgt:`TRANSITIVE_LINK_PROPERTIES`, + evaluation of the corresponding ``INTERFACE_PROP`` is transitive over + the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`, + including entries guarded by the :genex:`LINK_ONLY` generator expression. + + If a ``PROP`` is named by both :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` + and :prop_tgt:`TRANSITIVE_LINK_PROPERTIES`, the latter takes precedence. + +:ref:`Compatible Interface Properties` + These evaluate as a single value combined from the target itself, + from targets named by the target's :prop_tgt:`LINK_LIBRARIES`, and + from the transitive closure of the linked targets' + :prop_tgt:`INTERFACE_LINK_LIBRARIES`. Values of a compatible + interface property from multiple targets combine based on the type + of compatibility required by the ``COMPATIBLE_INTERFACE_*`` property + defining it. + + +Target Artifacts +^^^^^^^^^^^^^^^^ + +These expressions look up information about artifacts associated with +a given target ``tgt``. Unless otherwise stated, this can be any +runtime artifact, namely: + +* An executable target created by :command:`add_executable`. +* A shared library target (``.so``, ``.dll`` but not their ``.lib`` import + library) created by :command:`add_library`. +* A static library target created by :command:`add_library`. + +In the following, the phrase "the ``tgt`` filename" means the name of the +``tgt`` binary file. This has to be distinguished from the phrase +"the target name", which is just the string ``tgt``. .. genex:: $<TARGET_FILE:tgt> @@ -2254,6 +2485,13 @@ 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> + + .. versionadded:: 3.1 + + List of objects resulting from building ``tgt``. This would typically be + used on :ref:`object library <Object Libraries>` targets. + .. genex:: $<TARGET_RUNTIME_DLLS:tgt> .. versionadded:: 3.21 @@ -2410,6 +2648,13 @@ A literal ``;``. Used to prevent list expansion on an argument with ``;``. +.. genex:: $<QUOTE> + + .. versionadded:: 3.30 + + A literal ``"``. Used to allow string literal quotes inside a generator expression. + + Deprecated Expressions ----------------------
diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst index 9ed570a..82e11ef 100644 --- a/Help/manual/cmake-modules.7.rst +++ b/Help/manual/cmake-modules.7.rst
@@ -112,7 +112,6 @@ /module/FindBacktrace /module/FindBISON /module/FindBLAS - /module/FindBoost /module/FindBullet /module/FindBZip2 /module/FindCABLE @@ -291,6 +290,7 @@ .. toctree:: :maxdepth: 1 + /module/FindBoost /module/FindCUDA /module/FindDart /module/FindITK
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index bb6cfd4..7155404 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,22 @@ to determine whether to report an error on use of deprecated macros or functions. +Policies Introduced by CMake 3.30 +================================= + +.. toctree:: + :maxdepth: 1 + + CMP0170: FETCHCONTENT_FULLY_DISCONNECTED requirements are enforced. </policy/CMP0170> + CMP0169: FetchContent_Populate(depName) single-argument signature is deprecated. </policy/CMP0169> + CMP0168: FetchContent implements steps directly instead of through a sub-build. </policy/CMP0168> + CMP0167: The FindBoost module is removed. </policy/CMP0167> + CMP0166: TARGET_PROPERTY evaluates link properties transitively over private dependencies of static libraries. </policy/CMP0166> + CMP0165: enable_language() must not be called before project(). </policy/CMP0165> + CMP0164: add_library() rejects SHARED libraries when not supported by the platform. </policy/CMP0164> + CMP0163: The GENERATED source file property is now visible in all directories. </policy/CMP0163> + CMP0162: Visual Studio generators add UseDebugLibraries indicators by default. </policy/CMP0162> + Policies Introduced by CMake 3.29 ================================= @@ -118,7 +134,7 @@ CMP0138: CheckIPOSupported uses flags from calling project. </policy/CMP0138> CMP0137: try_compile() passes platform variables in project mode. </policy/CMP0137> CMP0136: Watcom runtime library flags are selected by an abstraction. </policy/CMP0136> - CMP0135: ExternalProject ignores timestamps in archives by default for the URL download method. </policy/CMP0135> + CMP0135: ExternalProject and FetchContent ignore timestamps in archives by default for the URL download method. </policy/CMP0135> CMP0134: Fallback to "HOST" Windows registry view when "TARGET" view is not usable. </policy/CMP0134> CMP0133: The CPack module disables SLA by default in the CPack DragNDrop Generator. </policy/CMP0133> CMP0132: Do not set compiler environment variables on first run. </policy/CMP0132> @@ -163,7 +179,7 @@ CMP0120: The WriteCompilerDetectionHeader module is removed. </policy/CMP0120> CMP0119: LANGUAGE source file property explicitly compiles as language. </policy/CMP0119> - CMP0118: The GENERATED source file property is now visible in all directories. </policy/CMP0118> + CMP0118: GENERATED sources may be used across directories without manual marking. </policy/CMP0118> CMP0117: MSVC RTTI flag /GR is not added to CMAKE_CXX_FLAGS by default. </policy/CMP0117> CMP0116: Ninja generators transform DEPFILEs from add_custom_command(). </policy/CMP0116> CMP0115: Source file extensions must be explicit. </policy/CMP0115> @@ -203,7 +219,7 @@ CMP0102: mark_as_advanced() does nothing if a cache entry does not exist. </policy/CMP0102> CMP0101: target_compile_options honors BEFORE keyword in all scopes. </policy/CMP0101> CMP0100: Let AUTOMOC and AUTOUIC process .hh header files. </policy/CMP0100> - CMP0099: Link properties are transitive over private dependency on static libraries. </policy/CMP0099> + CMP0099: Link properties are transitive over private dependencies of static libraries. </policy/CMP0099> CMP0098: FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing. </policy/CMP0098> Policies Introduced by CMake 3.16
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index fc0e1f9..59b34c6 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst
@@ -78,6 +78,9 @@ ``8`` .. versionadded:: 3.28 + ``9`` + .. versionadded:: 3.30 + ``cmakeMinimumRequired`` An optional object representing the minimum version of CMake needed to build this project. This object consists of the following fields: @@ -146,7 +149,10 @@ include files from anywhere. Starting from version ``7``, the ``include`` field supports -`macro expansion`_, but only ``$penv{}`` macro expansion. +`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``. Configure Preset ^^^^^^^^^^^^^^^^ @@ -1212,11 +1218,15 @@ ``${presetName}`` Name specified in the preset's ``name`` field. + This is a preset-specific macro. + ``${generator}`` Generator specified in the preset's ``generator`` field. For build and test presets, this will evaluate to the generator specified by ``configurePreset``. + This is a preset-specific macro. + ``${hostSystemName}`` The name of the host operating system. Contains the same value as :variable:`CMAKE_HOST_SYSTEM_NAME`. This is allowed in preset files
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 0d1046a..673bc7c 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst
@@ -25,6 +25,7 @@ /prop_gbl/CMAKE_C_KNOWN_FEATURES /prop_gbl/CMAKE_CUDA_KNOWN_FEATURES /prop_gbl/CMAKE_CXX_KNOWN_FEATURES + /prop_gbl/CMAKE_HIP_KNOWN_FEATURES /prop_gbl/CMAKE_ROLE /prop_gbl/DEBUG_CONFIGURATIONS /prop_gbl/DISABLED_FEATURES @@ -39,11 +40,13 @@ /prop_gbl/GENERATOR_IS_MULTI_CONFIG /prop_gbl/GLOBAL_DEPENDS_DEBUG_MODE /prop_gbl/GLOBAL_DEPENDS_NO_CYCLES + /prop_gbl/INSTALL_PARALLEL /prop_gbl/IN_TRY_COMPILE /prop_gbl/JOB_POOLS /prop_gbl/PACKAGES_FOUND /prop_gbl/PACKAGES_NOT_FOUND /prop_gbl/PREDEFINED_TARGETS_FOLDER + /prop_gbl/PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE /prop_gbl/REPORT_UNDEFINED_PROPERTIES /prop_gbl/RULE_LAUNCH_COMPILE /prop_gbl/RULE_LAUNCH_CUSTOM @@ -194,6 +197,7 @@ /prop_tgt/CXX_MODULE_SET /prop_tgt/CXX_MODULE_SET_NAME /prop_tgt/CXX_MODULE_SETS + /prop_tgt/CXX_MODULE_STD /prop_tgt/CXX_SCAN_FOR_MODULES /prop_tgt/CXX_STANDARD /prop_tgt/CXX_STANDARD_REQUIRED @@ -400,6 +404,8 @@ /prop_tgt/Swift_MODULE_NAME /prop_tgt/SYSTEM /prop_tgt/TEST_LAUNCHER + /prop_tgt/TRANSITIVE_COMPILE_PROPERTIES + /prop_tgt/TRANSITIVE_LINK_PROPERTIES /prop_tgt/TYPE /prop_tgt/UNITY_BUILD /prop_tgt/UNITY_BUILD_BATCH_SIZE @@ -445,7 +451,9 @@ /prop_tgt/VS_SDK_REFERENCES /prop_tgt/VS_SOLUTION_DEPLOY /prop_tgt/VS_SOURCE_SETTINGS_tool + /prop_tgt/VS_USE_DEBUG_LIBRARIES /prop_tgt/VS_USER_PROPS + /prop_tgt/VS_FILTER_PROPS /prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION /prop_tgt/VS_WINRT_COMPONENT /prop_tgt/VS_WINRT_REFERENCES
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 8a5ab30..315c73d 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst
@@ -137,6 +137,7 @@ /variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER /variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION /variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION + /variable/CMAKE_VS_USE_DEBUG_LIBRARIES /variable/CMAKE_VS_VERSION_BUILD_NUMBER /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION @@ -271,6 +272,7 @@ /variable/CMAKE_SYSTEM_PROGRAM_PATH /variable/CMAKE_TLS_CAINFO /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 @@ -422,6 +424,7 @@ /variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS /variable/CMAKE_CUDA_RUNTIME_LIBRARY /variable/CMAKE_CUDA_SEPARABLE_COMPILATION + /variable/CMAKE_CXX_MODULE_STD /variable/CMAKE_CXX_SCAN_FOR_MODULES /variable/CMAKE_DEBUG_POSTFIX /variable/CMAKE_DEFAULT_BUILD_TYPE @@ -464,6 +467,7 @@ /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE_SUPPORTED + /variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_ATTRIBUTES /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG /variable/CMAKE_LANG_LINK_LIBRARY_FLAG /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE @@ -482,6 +486,7 @@ /variable/CMAKE_LINK_GROUP_USING_FEATURE /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED /variable/CMAKE_LINK_INTERFACE_LIBRARIES + /variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES /variable/CMAKE_LINK_LIBRARY_FILE_FLAG /variable/CMAKE_LINK_LIBRARY_FLAG /variable/CMAKE_LINK_LIBRARY_USING_FEATURE @@ -577,6 +582,7 @@ /variable/CMAKE_CUDA_STANDARD_REQUIRED /variable/CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES /variable/CMAKE_CXX_COMPILE_FEATURES + /variable/CMAKE_CXX_COMPILER_IMPORT_STD /variable/CMAKE_CXX_EXTENSIONS /variable/CMAKE_CXX_STANDARD /variable/CMAKE_CXX_STANDARD_REQUIRED @@ -584,6 +590,7 @@ /variable/CMAKE_Fortran_MODDIR_FLAG /variable/CMAKE_Fortran_MODOUT_FLAG /variable/CMAKE_HIP_ARCHITECTURES + /variable/CMAKE_HIP_COMPILE_FEATURES /variable/CMAKE_HIP_EXTENSIONS /variable/CMAKE_HIP_PLATFORM /variable/CMAKE_HIP_STANDARD @@ -641,6 +648,7 @@ /variable/CMAKE_LANG_STANDARD /variable/CMAKE_LANG_STANDARD_DEFAULT /variable/CMAKE_LANG_STANDARD_INCLUDE_DIRECTORIES + /variable/CMAKE_LANG_STANDARD_LATEST /variable/CMAKE_LANG_STANDARD_LIBRARIES /variable/CMAKE_LANG_STANDARD_REQUIRED /variable/CMAKE_OBJC_EXTENSIONS @@ -669,7 +677,6 @@ /variable/CTEST_CONFIGURE_COMMAND /variable/CTEST_COVERAGE_COMMAND /variable/CTEST_COVERAGE_EXTRA_FLAGS - /variable/CTEST_CURL_OPTIONS /variable/CTEST_CUSTOM_COVERAGE_EXCLUDE /variable/CTEST_CUSTOM_ERROR_EXCEPTION /variable/CTEST_CUSTOM_ERROR_MATCH @@ -726,6 +733,8 @@ /variable/CTEST_SVN_UPDATE_OPTIONS /variable/CTEST_TEST_LOAD /variable/CTEST_TEST_TIMEOUT + /variable/CTEST_TLS_VERIFY + /variable/CTEST_TLS_VERSION /variable/CTEST_UPDATE_COMMAND /variable/CTEST_UPDATE_OPTIONS /variable/CTEST_UPDATE_VERSION_ONLY @@ -839,6 +848,7 @@ .. toctree:: :maxdepth: 1 + /variable/CTEST_CURL_OPTIONS /variable/CTEST_CVS_CHECKOUT /variable/CTEST_SCP_COMMAND /variable/CTEST_TRIGGER_SITE
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 621c005..48a9219 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst
@@ -213,6 +213,13 @@ This removes any existing ``CMakeCache.txt`` file and associated ``CMakeFiles/`` directory, and recreates them from scratch. + .. versionchanged:: 3.30 + + For dependencies previously populated by :module:`FetchContent` with the + ``NEW`` setting for policy :policy:`CMP0168`, their stamp and script files + from any previous run will be removed. The download, update, and patch + steps will therefore be forced to re-execute. + .. option:: -L[A][H] List non-advanced cached variables. @@ -507,27 +514,33 @@ .. option:: --preset <preset>, --preset=<preset> - Reads a :manual:`preset <cmake-presets(7)>` from - ``<path-to-source>/CMakePresets.json`` and - ``<path-to-source>/CMakeUserPresets.json``. The preset may specify the - generator and the build directory, and a list of variables and other - arguments to pass to CMake. The current working directory must contain - CMake preset files. The :manual:`CMake GUI <cmake-gui(1)>` can - also recognize ``CMakePresets.json`` and ``CMakeUserPresets.json`` files. For - full details on these files, see :manual:`cmake-presets(7)`. + 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. + 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. The options - specified by the 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. + 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 + 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. .. 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. + directory must contain CMake preset files unless the :option:`-S <cmake -S>` + option is used to specify a different top level source directory. .. option:: --debugger @@ -1375,9 +1388,8 @@ .. option:: --fresh - Perform a fresh configuration of the build tree. - This removes any existing ``CMakeCache.txt`` file and associated - ``CMakeFiles/`` directory, and recreates them from scratch. + Perform a fresh configuration of the build tree, which has the same effect + as :option:`cmake --fresh`. View Help =========
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index 0e90ab6..c9ab31e 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst
@@ -1452,14 +1452,25 @@ * :module:`CTest` module variable: ``CTEST_SUBMIT_RETRY_DELAY`` ``CurlOptions`` + .. deprecated:: 3.30 + + Use ``TLSVerify`` instead. + Specify a semicolon-separated list of options to control the Curl library that CTest uses internally to connect to the - server. Possible options are ``CURLOPT_SSL_VERIFYPEER_OFF`` - and ``CURLOPT_SSL_VERIFYHOST_OFF``. + server. * `CTest Script`_ variable: :variable:`CTEST_CURL_OPTIONS` * :module:`CTest` module variable: ``CTEST_CURL_OPTIONS`` + Possible options are: + + ``CURLOPT_SSL_VERIFYPEER_OFF`` + Disable the ``CURLOPT_SSL_VERIFYPEER`` curl option. + + ``CURLOPT_SSL_VERIFYHOST_OFF`` + Disable the ``CURLOPT_SSL_VERIFYHOST`` curl option. + ``DropLocation`` Legacy option. When ``SubmitURL`` is not set, it is constructed from ``DropMethod``, ``DropSiteUser``, ``DropSitePassword``, ``DropSite``, and @@ -1540,6 +1551,24 @@ * `CTest Script`_ variable: :variable:`CTEST_SUBMIT_INACTIVITY_TIMEOUT` * :module:`CTest` module variable: ``CTEST_SUBMIT_INACTIVITY_TIMEOUT`` +``TLSVersion`` + .. versionadded:: 3.30 + + Specify a minimum TLS version allowed when submitting to a dashboard + via ``https://`` URLs. + + * `CTest Script`_ variable: :variable:`CTEST_TLS_VERSION` + * :module:`CTest` module variable: ``CTEST_TLS_VERSION`` + +``TLSVerify`` + .. versionadded:: 3.30 + + Specify a boolean value indicating whether to verify the server + certificate when submitting to a dashboard via ``https://`` URLs. + + * `CTest Script`_ variable: :variable:`CTEST_TLS_VERIFY` + * :module:`CTest` module variable: ``CTEST_TLS_VERIFY`` + ``TriggerSite`` Legacy option. Not used.
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json index d27faa1..f80685c 100644 --- a/Help/manual/presets/schema.json +++ b/Help/manual/presets/schema.json
@@ -124,6 +124,24 @@ "include": { "$ref": "#/definitions/include" } }, "additionalProperties": false + }, + { + "properties": { + "$schema": { "$ref": "#/definitions/$schema" }, + "version": { + "const": 9, + "description": "A required integer representing the version of the JSON schema." + }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "vendor": { "$ref": "#/definitions/vendor" }, + "configurePresets": { "$ref": "#/definitions/configurePresetsV7" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, + "testPresets": { "$ref": "#/definitions/testPresetsV6" }, + "packagePresets": { "$ref": "#/definitions/packagePresetsV6" }, + "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" }, + "include": { "$ref": "#/definitions/include" } + }, + "additionalProperties": false } ], "required": [
diff --git a/Help/policy/CMP0066.rst b/Help/policy/CMP0066.rst index fa4bc5c..947a186 100644 --- a/Help/policy/CMP0066.rst +++ b/Help/policy/CMP0066.rst
@@ -18,7 +18,7 @@ built-in defaults for the current compiler and platform. The ``NEW`` behavior of this policy is to honor config-specific flag -variabldes like :variable:`CMAKE_<LANG>_FLAGS_DEBUG`. +variables like :variable:`CMAKE_<LANG>_FLAGS_DEBUG`. .. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.7 .. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
diff --git a/Help/policy/CMP0097.rst b/Help/policy/CMP0097.rst index 66a7b96..d9141f5 100644 --- a/Help/policy/CMP0097.rst +++ b/Help/policy/CMP0097.rst
@@ -4,9 +4,7 @@ .. versionadded:: 3.16 :command:`ExternalProject_Add` with ``GIT_SUBMODULES ""`` initializes no -submodules. The policy also applies to :command:`FetchContent_Declare`, -which uses the same download and update features as -:command:`ExternalProject_Add`. +submodules. The commands provide a ``GIT_SUBMODULES`` option which controls what submodules to initialize and update. Starting with CMake 3.16, explicitly setting @@ -25,4 +23,15 @@ .. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn .. include:: STANDARD_ADVICE.txt +.. note:: + + This policy also applies to :command:`FetchContent_Declare`, + which uses the same download and update features as + :command:`ExternalProject_Add`. However, due to an implementation deficiency + present since the policy was first introduced, CMake 3.16 and later always + uses the ``NEW`` behavior for :command:`FetchContent_Declare`, regardless of + the policy setting. Formally, this forcing of ``NEW`` behavior for + :command:`FetchContent_Declare` will continue to apply in future CMake + releases. + .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0099.rst b/Help/policy/CMP0099.rst index c0db99d..0a2b786 100644 --- a/Help/policy/CMP0099.rst +++ b/Help/policy/CMP0099.rst
@@ -3,13 +3,16 @@ .. versionadded:: 3.17 -Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`, -:prop_tgt:`INTERFACE_LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DEPENDS` -are now transitive over private dependencies of static libraries. +Link properties are transitive over private dependencies of static libraries. -In CMake 3.16 and below the interface link properties attached to libraries -are not propagated for private dependencies of static libraries. +In CMake 3.16 and below, evaluation of target properties +:prop_tgt:`INTERFACE_LINK_OPTIONS`, :prop_tgt:`INTERFACE_LINK_DIRECTORIES`, +and :prop_tgt:`INTERFACE_LINK_DEPENDS` during buildsystem generation does not +follow private dependencies of static libraries, which appear in their +:prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded by :genex:`LINK_ONLY` generator +expressions. Only the libraries themselves are propagated to link the dependent binary. + CMake 3.17 and later prefer to propagate all interface link properties. This policy provides compatibility for projects that have not been updated to expect the new behavior. @@ -18,6 +21,12 @@ properties. The ``NEW`` behavior of this policy is to propagate interface link properties. +.. versionadded:: 3.30 + + Policy :policy:`CMP0166` makes :genex:`TARGET_PROPERTY` evaluation of + these three transitive link properties follow private dependencies of + static libraries too. + .. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17 .. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn .. include:: STANDARD_ADVICE.txt
diff --git a/Help/policy/CMP0118.rst b/Help/policy/CMP0118.rst index 00afadf..5c04927 100644 --- a/Help/policy/CMP0118.rst +++ b/Help/policy/CMP0118.rst
@@ -3,25 +3,36 @@ .. versionadded:: 3.20 -The :prop_sf:`GENERATED` source file property is now visible in all directories. +:prop_sf:`GENERATED` sources may be used across directories without manual marking. + +In CMake 3.19 and below, the :prop_sf:`GENERATED` source file property, +like other source file properties, was scoped in every directory separately. +If a source file was generated in one directory, projects had to manually +set the ``GENERATED`` property in another directory in order to use the file. Whether or not a source file is generated is an all-or-nothing global -property of the source. Consequently, the associated ``GENERATED`` -property is now visible from any directory scope, not only from the scope -for which it was set. - +property of the source: a source is either generated or it is not. +CMake 3.20 and above prefer to allow source files generated in one directory +to be used in other directories without manually marking them as ``GENERATED``. Additionally, the ``GENERATED`` property may now be set only to boolean -values, and may not be turned off once turned on. +values, and may not be turned off once turned on. This policy provides +compatibility for projects that have not been updated for this behavior. -The ``OLD`` behavior of this policy is to only allow ``GENERATED`` to be -visible from the directory scope for which it was set. The ``NEW`` -behavior on the other hand allows it to be visible from any scope. +The ``OLD`` behavior of this policy is to allow generated files to be used +only in directories in which their ``GENERATED`` property has been turned on. +The ``NEW`` behavior of this policy is to allow generated files to be used +in other directories without explicitly turning on the ``GENERATED`` property +for those directories. + +.. versionadded:: 3.30 + + Policy :policy:`CMP0163` additionally makes the :prop_sf:`GENERATED` source + file property visible to :command:`get_property` and + :command:`get_source_file_property` calls in other directories. .. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20 .. |WARNS_OR_DOES_NOT_WARN| replace:: - does *not* warn with regard to visibility of the ``GENERATED`` - property, but does warn about setting the ``GENERATED`` property - to a non-boolean value, + warns about setting the ``GENERATED`` property to a non-boolean value .. include:: STANDARD_ADVICE.txt .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0135.rst b/Help/policy/CMP0135.rst index c8d9205..09c688d 100644 --- a/Help/policy/CMP0135.rst +++ b/Help/policy/CMP0135.rst
@@ -4,20 +4,20 @@ .. versionadded:: 3.24 When using the ``URL`` download method with the :command:`ExternalProject_Add` -command, CMake 3.23 and below sets the timestamps of the extracted contents -to the same as the timestamps in the archive. When the ``URL`` changes, the -new archive is downloaded and extracted, but the timestamps of the extracted -contents might not be newer than the previous contents. Anything that depends -on the extracted contents might not be rebuilt, even though the contents may -change. +or :command:`FetchContent_Declare` commands, CMake 3.23 and below sets the +timestamps of the extracted contents to the same as the timestamps in the +archive. When the ``URL`` changes, the new archive is downloaded and extracted, +but the timestamps of the extracted contents might not be newer than the +previous contents. Anything that depends on the extracted contents might not +be rebuilt, even though the contents may change. CMake 3.24 and above prefers to set the timestamps of all extracted contents to the time of the extraction. This ensures that anything that depends on the extracted contents will be rebuilt whenever the ``URL`` changes. -The ``DOWNLOAD_EXTRACT_TIMESTAMP`` option to the -:command:`ExternalProject_Add` command can be used to explicitly specify how -timestamps should be handled. When ``DOWNLOAD_EXTRACT_TIMESTAMP`` is not +The ``DOWNLOAD_EXTRACT_TIMESTAMP`` option to the :command:`ExternalProject_Add` +and :command:`FetchContent_Declare` commands can be used to explicitly specify +how timestamps should be handled. When ``DOWNLOAD_EXTRACT_TIMESTAMP`` is not given, this policy controls the default behavior. The ``OLD`` behavior for this policy is to restore the timestamps from the archive. The ``NEW`` behavior sets the timestamps of extracted contents to the time of extraction.
diff --git a/Help/policy/CMP0162.rst b/Help/policy/CMP0162.rst new file mode 100644 index 0000000..df46d96 --- /dev/null +++ b/Help/policy/CMP0162.rst
@@ -0,0 +1,47 @@ +CMP0162 +------- + +.. versionadded:: 3.30 + +:ref:`Visual Studio Generators` add ``UseDebugLibraries`` indicators by default. + +The "Use Debug Libraries" setting in Visual Studio projects indicates what +configurations are considered debug configurations. In standalone projects, +this may affect MSBuild's default selection of MSVC runtime library, +optimization flags, runtime checks, and similar settings. CMake typically +generates all those settings explicitly based on the project's specification, +so CMake 3.29 and below do not write any ``UseDebugLibraries`` indicators to +``.vcxproj`` files. + +CMake 3.30 and above prefer to write ``UseDebugLibraries`` indicators because +they are useful for reference by both humans and tools, and may also affect +the behavior of platform-specific SDKs. The indicator for each configuration +of a target is determined as follows: + +* If the target compiles sources for a known MSVC runtime library + (such as that specified by :prop_tgt:`MSVC_RUNTIME_LIBRARY`), + then ``UseDebugLibraries`` is ``true`` for configurations that + compile for a "Debug" runtime library, and ``false`` for others. + +* Otherwise, such as in targets created by :command:`add_custom_target`, + ``UseDebugLibraries`` is ``true`` for the ``Debug`` configuration, + and ``false`` for others. + +This policy provides compatibility for projects that have not been updated to +expect the indicators. The policy setting is recorded by each target as it is +created and used to determine the default behavior for that target's +``.vcxproj`` file. + +The ``OLD`` behavior for this policy is to not generate ``UseDebugLibraries`` +indicators by default. The ``NEW`` behavior for this policy is to generate +``UseDebugLibraries`` indicators by default. + +If the :variable:`CMAKE_VS_USE_DEBUG_LIBRARIES` variable and/or +:prop_tgt:`VS_USE_DEBUG_LIBRARIES` target property is set, it explicitly +controls ``UseDebugLibraries`` generation regardless of this policy. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0163.rst b/Help/policy/CMP0163.rst new file mode 100644 index 0000000..40b3535 --- /dev/null +++ b/Help/policy/CMP0163.rst
@@ -0,0 +1,37 @@ +CMP0163 +------- + +.. versionadded:: 3.30 + +The :prop_sf:`GENERATED` source file property is now visible in all directories. + +In CMake 3.29 and below, the :prop_sf:`GENERATED` source file property, +like other source file properties, was scoped in every directory separately. +Although policy :policy:`CMP0118` allowed sources marked ``GENERATED`` in one +directory to be used in other directories without manually marking them as +``GENERATED`` again, the ``GENERATED`` property was still not visible to +:command:`get_property` and :command:`get_source_file_property` calls. + +Whether or not a source file is generated is an all-or-nothing global +property of the source: a source is either generated or it is not. +CMake 3.30 and above prefer to treat the :prop_sf:`GENERATED` source file +property as globally scoped. Once it is set in one directory, it is +immediately visible to :command:`get_property` and +:command:`get_source_file_property` calls in other directories. +This policy provides compatibility for projects that have not been +updated for this behavior. + +The ``OLD`` behavior of this policy is for the ``GENERATED`` source file +property to be visible only in the directories in which it is set. The +``NEW`` behavior of this policy is to allow the ``GENERATED`` source file +property to be visible in all directories once set in any directory. +Furthermore, the ``NEW`` behavior of this policy implies the ``NEW`` +behavior of policy :policy:`CMP0118`: the ``GENERATED`` property may +be set only to boolean values, and may not be turned off once turned on. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: + does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0164.rst b/Help/policy/CMP0164.rst new file mode 100644 index 0000000..9702cc2 --- /dev/null +++ b/Help/policy/CMP0164.rst
@@ -0,0 +1,31 @@ +CMP0164 +------- + +.. versionadded:: 3.30 + +:command:`add_library` rejects ``SHARED`` libraries when not supported by +the platform. + +In CMake 3.29 and below, on platforms that do not support shared libraries +(:prop_gbl:`TARGET_SUPPORTS_SHARED_LIBS` is false), the +:command:`add_library` command automatically converts ``SHARED`` libraries to +``STATIC`` libraries to help users build projects on such platforms. However, +the semantics of shared and static libraries are different enough that such +automatic conversion cannot work in general. Projects using shared libraries +need to be ported to such platforms on a case-by-case basis. + +In CMake 3.30 and above, :command:`add_library` prefers to reject creation +of shared libraries on platforms that do not support them, and fail with a +fatal error message. This policy provides compatibility for projects that +happened to work with the automatic conversion to static libraries and have +not been updated with an explicit port. + +The ``OLD`` behavior for this policy is to implicitly create a static +library with a developer warning. The ``NEW`` behavior for this policy is +to fail. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn about the behavior change +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0165.rst b/Help/policy/CMP0165.rst new file mode 100644 index 0000000..7a79c44 --- /dev/null +++ b/Help/policy/CMP0165.rst
@@ -0,0 +1,26 @@ +CMP0165 +------- + +.. versionadded:: 3.30 + +:command:`enable_language` must not be called before :command:`project`. + +In CMake 3.29 and below, if a project called :command:`enable_language` +before the first call to :command:`project`, the language would be enabled +but possibly using unset details that were expected to be set. +In CMake 3.30 and above, :command:`enable_language` prefers to reject this +case and stop with a fatal error instead if it detects that :command:`project` +has not yet been called. This policy provides compatibility for projects that +happened to work when :command:`enable_language` was called before +:command:`project` and have not been updated to call these commands in the +required order. + +The ``OLD`` behavior for this policy is to allow :command:`enable_language` +to be called before :command:`project`. The ``NEW`` behavior for this policy +is to fail with a fatal error in such cases. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0166.rst b/Help/policy/CMP0166.rst new file mode 100644 index 0000000..0534559 --- /dev/null +++ b/Help/policy/CMP0166.rst
@@ -0,0 +1,40 @@ +CMP0166 +------- + +.. versionadded:: 3.30 + +:genex:`TARGET_PROPERTY` evaluates link properties transitively over private +dependencies of static libraries. + +In CMake 3.29 and below, the :genex:`TARGET_PROPERTY` generator expression +evaluates properties :prop_tgt:`INTERFACE_LINK_OPTIONS`, +:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS` +as if they were :ref:`Transitive Compile Properties` rather than +:ref:`Transitive Link Properties`, even when policy :policy:`CMP0099` is +set to ``NEW``. Private dependencies of static libraries, which appear in +their :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded by :genex:`LINK_ONLY` +generator expressions, are not followed. This is inconsistent with +evaluation of the same target properties during buildsystem generation. + +CMake 3.30 and above prefer that :genex:`TARGET_PROPERTY` evaluates +properties :prop_tgt:`INTERFACE_LINK_OPTIONS`, +:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS` +as :ref:`Transitive Link Properties` such that private dependencies of static +libraries, which appear in their :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded +by :genex:`LINK_ONLY` generator expressions, are followed. +This policy provides compatibility for projects that have not been updated +to expect the new behavior. + +The ``OLD`` behavior for this policy is for :genex:`TARGET_PROPERTY` to +evaluate properties :prop_tgt:`INTERFACE_LINK_OPTIONS`, +:prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and :prop_tgt:`INTERFACE_LINK_DEPENDS` +as if they were :ref:`Transitive Compile Properties` by not following private +dependencies of static libraries. The ``NEW`` behavior for this policy is +to evaluate them as :ref:`Transitive Link Properties` by following private +dependencies of static libraries. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0167.rst b/Help/policy/CMP0167.rst new file mode 100644 index 0000000..763ef69 --- /dev/null +++ b/Help/policy/CMP0167.rst
@@ -0,0 +1,28 @@ +CMP0167 +------- + +.. versionadded:: 3.30 + +The :module:`FindBoost` module is removed. + +CMake 3.29 and below provide a ``FindBoost`` module, but it needs constant +updates to keep up with upstream Boost releases. Upstream Boost 1.70 +and above provide a ``BoostConfig.cmake`` package configuration file. +``find_package(Boost CONFIG)`` finds the upstream package directly, +without the find module. + +CMake 3.30 and above prefer to not provide the ``FindBoost`` module +so that ``find_package(Boost)`` calls, without the ``CONFIG`` or +``NO_MODULE`` options, find the upstream ``BoostConfig.cmake`` directly. +This policy provides compatibility for projects that have not been ported +to use the upstream Boost package. + +The ``OLD`` behavior of this policy is for ``find_package(Boost)`` to +load CMake's :module:`FindBoost` module. The ``NEW`` behavior is for +``find_package(Boost)`` to search for the upstream ``BoostConfig.cmake``. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0168.rst b/Help/policy/CMP0168.rst new file mode 100644 index 0000000..cab40b9 --- /dev/null +++ b/Help/policy/CMP0168.rst
@@ -0,0 +1,72 @@ +CMP0168 +------- + +.. versionadded:: 3.30 + +The :module:`FetchContent` module implements steps directly instead of through +a sub-build. + +CMake 3.29 and below implement FetchContent as a separate sub-build. +This required configuring that separate project and using a build tool. +This approach can be very slow with some generators and operating systems. +CMake 3.30 and above prefer to implement the download, update, and patch +steps directly as part of the main project. + +The ``NEW`` behavior has the following characteristics: + +* No sub-build is used. All operations are implemented directly from the + main project's CMake configure step. When running in CMake script mode, + no build tool needs to be available. +* Generator expressions and GNU Make variables of the form ``$(SOMEVAR)`` are + not supported. They should not be used in any argument to + :command:`FetchContent_Declare` or :command:`FetchContent_Populate`. +* All ``LOG_...`` and ``USES_TERMINAL_...`` options, the ``QUIET`` option, and + the :variable:`FETCHCONTENT_QUIET` variable are ignored. + :module:`FetchContent` output is always part of the main project's configure + output. This also means it now respects the message logging level (see + :variable:`CMAKE_MESSAGE_LOG_LEVEL` and + :option:`--log-level <cmake --log-level>`). The default message log level + should be comparable to using ``QUIET`` with the ``OLD`` policy setting, + except that warnings will now be shown. +* The ``PREFIX``, ``TMP_DIR``, ``STAMP_DIR``, ``LOG_DIR``, and ``DOWNLOAD_DIR`` + options and their associated directory properties are ignored. The + :module:`FetchContent` module controls those locations internally. +* :option:`cmake --fresh` will remove the stamp and script files used for + tracking and populating the dependency. This will force the dependency's + download, update, and patch steps to be re-executed. The directory used for + downloads is not affected by :option:`cmake --fresh`, so any previously + downloaded files for the ``URL`` download method can still be re-used. + +The ``OLD`` behavior has the following characteristics: + +* A sub-build is always used to implement the download, update, and patch + steps. A build tool must be available, even when using + :command:`FetchContent_Populate` in CMake script mode. +* Generator expressions and GNU Make variables of the form ``$(SOMEVAR)`` can + be used, although such use is almost always inappropriate. They are evaluated + in the sub-build, so they do not see any information from the main build. +* All logging, terminal control, and directory options related to the download, + update, or patch steps are supported. +* If the ``QUIET`` option is used, or the :variable:`FETCHCONTENT_QUIET` + variable is set to true, warnings will not be shown in the output. +* :option:`cmake --fresh` has no effect on the dependency's stamp or script + files. Previously executed steps will only re-run if details about the + dependency have changed. + +There's a reasonably good chance that users can set the +:variable:`CMAKE_POLICY_DEFAULT_CMP0168 <CMAKE_POLICY_DEFAULT_CMP<NNNN>>` +variable to ``NEW`` to globally switch to the ``NEW`` behavior while waiting +for the project and its dependencies to be updated use the ``NEW`` policy +setting by default. Projects don't typically make use of the features that the +``NEW`` behavior no longer supports, and even those projects that do will often +still work fine when those options are ignored. Before setting this behavior +globally, check whether any :command:`FetchContent_Declare` or +:command:`FetchContent_Populate` calls use the ignored options in a way that +would change observable behavior, other than putting temporary or +internally-generated files in different locations. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0169.rst b/Help/policy/CMP0169.rst new file mode 100644 index 0000000..38b8dcb --- /dev/null +++ b/Help/policy/CMP0169.rst
@@ -0,0 +1,49 @@ +CMP0169 +------- + +.. versionadded:: 3.30 + +Calling :command:`FetchContent_Populate` with a single argument (the name of +a declared dependency) is deprecated. + +Prior to the introduction of :command:`FetchContent_MakeAvailable`, projects +populated previously declared content (with :command:`FetchContent_Declare`) +using the following pattern: + +.. code-block:: cmake + + FetchContent_GetProperties(depname) + if(NOT depname_POPULATED) + FetchContent_Populate(depname) + add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR}) + endif() + +The above pattern does not support a number of features that have been added +to :module:`FetchContent` over time. It ignores options like ``SYSTEM`` and +``EXCLUDE_FROM_ALL`` which may be given to :command:`FetchContent_Declare`, +but can't be made known to the above project code. It also does not support +:ref:`dependency providers <dependency_providers_overview>`. +Projects should call :command:`FetchContent_MakeAvailable` instead of using +the above pattern. + +CMake 3.30 and above prefers to reject calls to +:command:`FetchContent_Populate` with the name of a declared dependency. +This policy provides compatibility for projects that have not been updated +to call :command:`FetchContent_MakeAvailable` instead. + +The ``OLD`` behavior of this policy allows :command:`FetchContent_Populate` +to be called with the name of a declared dependency. +The ``NEW`` behavior halts with a fatal error in such cases. + +.. note:: + Calling :command:`FetchContent_Populate` with the full population details + as command arguments rather than just a dependency name remains fully + supported. Only the form calling :command:`FetchContent_Populate` with a + single argument (the name of a previously declared dependency) is deprecated + with this policy. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0170.rst b/Help/policy/CMP0170.rst new file mode 100644 index 0000000..9d7860a --- /dev/null +++ b/Help/policy/CMP0170.rst
@@ -0,0 +1,30 @@ +CMP0170 +------- + +.. versionadded:: 3.30 + +When ``FETCHCONTENT_FULLY_DISCONNECTED`` is set to true, +:command:`FetchContent_MakeAvailable` and :command:`FetchContent_Populate` +enforce the constraint that their source directory must already be populated. +The requirement has always been documented, but it was not checked or enforced +with CMake 3.29 or older. This sometimes led to hard-to-trace errors when a +project expected a dependency to have been populated, but its population was +silently skipped. + +CMake 3.30 and above prefers to check and enforce the constraint. +This policy provides compatibility for situations where the user cannot easily +prevent ``FETCHCONTENT_FULLY_DISCONNECTED`` from being inappropriately set +to true. + +The ``OLD`` behavior of this policy allows ``FETCHCONTENT_FULLY_DISCONNECTED`` +to be set to true even if a dependency's source directory has not been +populated. +The ``NEW`` behavior halts with a fatal error if +``FETCHCONTENT_FULLY_DISCONNECTED`` is set to true and a dependency population +would be skipped, but that dependency's source directory doesn't exist. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt
diff --git a/Help/prop_dir/EXCLUDE_FROM_ALL.rst b/Help/prop_dir/EXCLUDE_FROM_ALL.rst index 8e3cca0..1adb50e 100644 --- a/Help/prop_dir/EXCLUDE_FROM_ALL.rst +++ b/Help/prop_dir/EXCLUDE_FROM_ALL.rst
@@ -8,6 +8,27 @@ subdirectory itself. Running e.g. ``make`` inside the subdirectory will still build its targets. -If the :prop_tgt:`EXCLUDE_FROM_ALL` target property is set on a target -then its value determines whether the target is included in the "all" -target of this directory and its ancestors. +``EXCLUDE_FROM_ALL`` is meant for when the subdirectory contains +a separate part of the project that is useful, but not necessary, +such as a set of examples, or e.g. an integrated 3rd party library. +Typically the subdirectory should contain its own :command:`project` +command invocation so that a full build system will be generated in the +subdirectory (such as a Visual Studio IDE solution file). Note that +inter-target dependencies supersede this exclusion. If a target built by +the parent project depends on a target in the subdirectory, the dependee +target will be included in the parent project build system to satisfy +the dependency. + +If the ``EXCLUDE_FROM_ALL`` argument is provided, it has the following effects: + +* Targets defined in the subdirectory or below will not be + included in the ``ALL`` target of the parent directory. + Those targets must be built explicitly by the user, + or be a dependency of another target that will be built. +* Targets defined in the subdirectory or below will be + excluded from IDE project files. +* Any install rules defined in the subdirectory or below will + be ignored when installing the parent directory. + +Note that these effects are not the same as those for the +:prop_tgt:`EXCLUDE_FROM_ALL` target property.
diff --git a/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst b/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst index b65db99..c36306d 100644 --- a/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst +++ b/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst
@@ -17,7 +17,7 @@ solution global section. Whitespace around key and value is ignored. List elements which do not contain an equal sign are skipped. -This property only works for Visual Studio 9 and above; it is ignored +This property only works for Visual Studio 12 and above; it is ignored on other generators. The property only applies when set on a directory whose ``CMakeLists.txt`` contains a :command:`project` command.
diff --git a/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst b/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst index 7f8bf61..c775ad5 100644 --- a/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst +++ b/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst
@@ -17,6 +17,6 @@ solution global section. Whitespace around key and value is ignored. List elements which do not contain an equal sign are skipped. -This property only works for Visual Studio 9 and above; it is ignored +This property only works for Visual Studio 12 and above; it is ignored on other generators. The property only applies when set on a directory whose ``CMakeLists.txt`` contains a :command:`project` command.
diff --git a/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst b/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst index a31ee3a..fd2f941 100644 --- a/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst +++ b/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst
@@ -37,7 +37,7 @@ Compiler mode is at least CUDA/C++ 23. ``cuda_std_26`` - .. versionadded:: 3.25 + .. versionadded:: 3.30 Compiler mode is at least CUDA/C++ 26.
diff --git a/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst b/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst index e54b927..177659a 100644 --- a/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst +++ b/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst
@@ -47,7 +47,7 @@ Compiler mode is at least C++ 23. ``cxx_std_26`` - .. versionadded:: 3.25 + .. versionadded:: 3.30 Compiler mode is at least C++ 26.
diff --git a/Help/prop_gbl/CMAKE_HIP_KNOWN_FEATURES.rst b/Help/prop_gbl/CMAKE_HIP_KNOWN_FEATURES.rst new file mode 100644 index 0000000..9d13c81 --- /dev/null +++ b/Help/prop_gbl/CMAKE_HIP_KNOWN_FEATURES.rst
@@ -0,0 +1,42 @@ +CMAKE_HIP_KNOWN_FEATURES +------------------------ + +.. versionadded:: 3.30 + +List of HIP features known to this version of CMake. + +The features listed in this global property may be known to be available to the +HIP compiler. If the feature is available with the HIP compiler, it will +be listed in the :variable:`CMAKE_HIP_COMPILE_FEATURES` variable. + +The features listed here may be used with the :command:`target_compile_features` +command. See the :manual:`cmake-compile-features(7)` manual for information on +compile features and a list of supported compilers. + + +The features known to this version of CMake are: + +``hip_std_98`` + Compiler mode is at least HIP/C++ 98. + +``hip_std_11`` + Compiler mode is at least HIP/C++ 11. + +``hip_std_14`` + Compiler mode is at least HIP/C++ 14. + +``hip_std_17`` + Compiler mode is at least HIP/C++ 17. + +``hip_std_20`` + Compiler mode is at least HIP/C++ 20. + +``hip_std_23`` + Compiler mode is at least HIP/C++ 23. + +``hip_std_26`` + .. versionadded:: 3.30 + + Compiler mode is at least HIP/C++ 26. + +.. include:: CMAKE_LANG_STD_FLAGS.txt
diff --git a/Help/prop_gbl/INSTALL_PARALLEL.rst b/Help/prop_gbl/INSTALL_PARALLEL.rst new file mode 100644 index 0000000..7b6632e --- /dev/null +++ b/Help/prop_gbl/INSTALL_PARALLEL.rst
@@ -0,0 +1,23 @@ +INSTALL_PARALLEL +---------------- + +.. versionadded:: 3.30 + +Enables parallel installation option for the Ninja generator. + +When this property is ``ON``, ``install/local`` targets have the +console pool disabled, allowing them to run concurrently. + +This property also provides the target ``install/parallel``, which has an +explicit dependency on the ``install/local`` target for each subdirectory, +recursing down the project. + +Setting this property has no affect on the behavior of ``cmake --install``. +The install must be invoked by building the ``install/parallel`` target +directly. + +Calls to :command:`install(CODE)` or :command:`install(SCRIPT)` might depend +on actions performed by an earlier :command:`install` command in a different +directory such as files installed or variable settings. If the project has +such order-dependent installation logic, parallel installation should be +not be enabled, in order to prevent possible race conditions.
diff --git a/Help/prop_gbl/PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE.rst b/Help/prop_gbl/PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE.rst new file mode 100644 index 0000000..edfee17 --- /dev/null +++ b/Help/prop_gbl/PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE.rst
@@ -0,0 +1,19 @@ +PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE +------------------------------------------- + +.. versionadded:: 3.30 + +When this global property is set to true, the +:variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable is propagated into +:command:`try_compile` calls that use the +:ref:`whole-project signature <Try Compiling Whole Projects>`. +Calls to the :ref:`source file signature <Try Compiling Source Files>` are not +affected by this property. +``PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE`` is unset by default. + +For :ref:`dependency providers <dependency_providers_overview>` that want to +be enabled in whole-project :command:`try_compile` calls, set this global +property to true just before or after registering the provider. +Note that all files listed in :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` +will need to be able to handle being included in such :command:`try_compile` +calls, and it is the user's responsibility to ensure this.
diff --git a/Help/prop_gbl/TARGET_SUPPORTS_SHARED_LIBS.rst b/Help/prop_gbl/TARGET_SUPPORTS_SHARED_LIBS.rst index f6e89fb..6846b63 100644 --- a/Help/prop_gbl/TARGET_SUPPORTS_SHARED_LIBS.rst +++ b/Help/prop_gbl/TARGET_SUPPORTS_SHARED_LIBS.rst
@@ -3,7 +3,7 @@ Does the target platform support shared libraries. -TARGET_SUPPORTS_SHARED_LIBS is a boolean specifying whether the target +``TARGET_SUPPORTS_SHARED_LIBS`` is a boolean specifying whether the target platform supports shared libraries. Basically all current general -general purpose OS do so, the exception are usually embedded systems +purpose OS do so, the exceptions are usually embedded systems with no or special OSs.
diff --git a/Help/prop_sf/COMPILE_FLAGS.rst b/Help/prop_sf/COMPILE_FLAGS.rst index eefe7bf..1d3def4 100644 --- a/Help/prop_sf/COMPILE_FLAGS.rst +++ b/Help/prop_sf/COMPILE_FLAGS.rst
@@ -5,8 +5,7 @@ The ``COMPILE_FLAGS`` property, managed as a string, sets additional compiler flags used that will be added to the list of compile flags when this source -file builds. The flags will be added after target-wide flags (except in -some cases not supported by the :generator:`Visual Studio 9 2008` generator). +file builds. The flags will be added after target-wide flags. Use :prop_sf:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions.
diff --git a/Help/prop_sf/COMPILE_OPTIONS.rst b/Help/prop_sf/COMPILE_OPTIONS.rst index 84c543a..9b00e0e 100644 --- a/Help/prop_sf/COMPILE_OPTIONS.rst +++ b/Help/prop_sf/COMPILE_OPTIONS.rst
@@ -7,8 +7,7 @@ This property holds a :ref:`semicolon-separated list <CMake Language Lists>` of options and will be added to the list of compile flags when this source -file builds. The options will be added after target-wide options (except in -some cases not supported by the :generator:`Visual Studio 9 2008` generator). +file builds. The options will be added after target-wide options. Contents of ``COMPILE_OPTIONS`` may use "generator expressions" with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual
diff --git a/Help/prop_sf/GENERATED.rst b/Help/prop_sf/GENERATED.rst index 5ecc86c..566b58a 100644 --- a/Help/prop_sf/GENERATED.rst +++ b/Help/prop_sf/GENERATED.rst
@@ -4,7 +4,16 @@ Is this source file generated as part of the build or CMake process. .. versionchanged:: 3.20 - The GENERATED source file property is now visible in all directories. + Turning on the ``GENERATED`` source file property in one directory allows + the associated source file to be used across directories without the need + to manually setting that property for other directory scopes, too. + Additionally, it may now be set only to boolean values, and may not be + turned off once turned on. See policy :policy:`CMP0118`. + +.. versionchanged:: 3.30 + Whether or not a source file is generated is an all-or-nothing global + property of the source. Consequently, the ``GENERATED`` source file + property is now visible in all directories. See policy :policy:`CMP0163`. Tells the internal CMake engine that a source file is generated by an outside process such as another build step, or the execution of CMake itself. @@ -38,11 +47,3 @@ :prop_gbl:`AUTORCC_SOURCE_GROUP` and :prop_gbl:`AUTOUIC_SOURCE_GROUP` target properties may influence where the generated sources are grouped in the project's file lists. - -.. note:: - - Starting with CMake 3.20 the ``GENERATED`` source file property can be set - and retrieved from any directory scope. It is an all-or-nothing property. - It also can no longer be removed or unset if it was set to ``TRUE``. Policy - :policy:`CMP0118` was introduced to allow supporting the ``OLD`` behavior - for some time.
diff --git a/Help/prop_tgt/AUTOUIC_OPTIONS.rst b/Help/prop_tgt/AUTOUIC_OPTIONS.rst index 425ea1c..d601740 100644 --- a/Help/prop_tgt/AUTOUIC_OPTIONS.rst +++ b/Help/prop_tgt/AUTOUIC_OPTIONS.rst
@@ -1,6 +1,8 @@ AUTOUIC_OPTIONS --------------- +.. versionadded:: 3.0 + Additional options for ``uic`` when using :prop_tgt:`AUTOUIC` This property holds additional command line options which will be used when
diff --git a/Help/prop_tgt/CXX_MODULE_STD.rst b/Help/prop_tgt/CXX_MODULE_STD.rst new file mode 100644 index 0000000..d42699d --- /dev/null +++ b/Help/prop_tgt/CXX_MODULE_STD.rst
@@ -0,0 +1,41 @@ +CXX_MODULE_STD +-------------- + +.. versionadded:: 3.30 + +``CXX_MODULE_STD`` is a boolean specifying whether the target may use +``import std;`` its C++ sources or not. + +.. note :: + + This setting is meaningful only when experimental support for ``import + std;`` has been enabled by the ``CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`` gate. + +When this property is explicitly set to ``ON``, CMake will add a dependency to +a target which provides the C++ standard library's modules for the C++ +standard applied to the target. This target is only applicable within the +current build and will not appear in the exported interfaces of the targets. +When consumed, these targets will be reapplied as necessary. + +.. note: + + Similar to the introduction of :prop:`CXX_SCAN_FOR_MODULES`, this property + defaults to _not_ adding ``import std`` support to targets using + ``cxx_std_23`` without an explicit request in order to preserve existing + behavior for projects using C++23 without ``import std``. A future policy + to change the default behavior is expected once the feature sees wider + usage. + +This property's value is not relevant for targets which disable scanning (see +:prop_tgt:`CXX_SCAN_FOR_MODULES`). Additionally, this property only applies to +targets utilizing C++23 (``cxx_std_23``) or newer. + +The property supports +:manual:`generator expressions <cmake-generator-expressions(7)>`, however +expressions that depend upon the configuration, the consuming target, or the +linker language are not allowed. Whether a target uses ``import std`` should +not depend upon such things as it is a static property of the target's source +code. + +Targets which are exported with C++ module sources will have this property's +resolved value exported.
diff --git a/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst b/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst index f11fe7c..afbee83 100644 --- a/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst +++ b/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst
@@ -4,7 +4,7 @@ .. versionadded:: 3.13 Set the WinCE project ``AdditionalFiles`` in ``DeploymentTool`` in ``.vcproj`` -files generated by the :generator:`Visual Studio 9 2008` generator. +files generated by the :ref:`Visual Studio Generators`. This is useful when you want to debug on remote WinCE device. Specify additional files that will be copied to the device. For example:
diff --git a/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst b/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst index 0680238..3f691b1 100644 --- a/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst +++ b/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst
@@ -5,7 +5,7 @@ Set the WinCE project ``RemoteDirectory`` in ``DeploymentTool`` and ``RemoteExecutable`` in ``DebuggerTool`` in ``.vcproj`` files generated -by the :generator:`Visual Studio 9 2008` generator. +by the :ref:`Visual Studio Generators`. This is useful when you want to debug on remote WinCE device. For example:
diff --git a/Help/prop_tgt/INTERFACE_AUTOUIC_OPTIONS.rst b/Help/prop_tgt/INTERFACE_AUTOUIC_OPTIONS.rst index e97d293..2fe011a 100644 --- a/Help/prop_tgt/INTERFACE_AUTOUIC_OPTIONS.rst +++ b/Help/prop_tgt/INTERFACE_AUTOUIC_OPTIONS.rst
@@ -1,6 +1,8 @@ INTERFACE_AUTOUIC_OPTIONS ------------------------- +.. versionadded:: 3.0 + List of interface options to pass to uic. Targets may populate this property to publish the options
diff --git a/Help/prop_tgt/LANG_STANDARD.rst b/Help/prop_tgt/LANG_STANDARD.rst index c83da01..4b59af7 100644 --- a/Help/prop_tgt/LANG_STANDARD.rst +++ b/Help/prop_tgt/LANG_STANDARD.rst
@@ -18,7 +18,7 @@ Note that the actual language standard used may be higher than that specified by ``<LANG>_STANDARD``, regardless of the value of :prop_tgt:`<LANG>_STANDARD_REQUIRED`. In particular, -:ref:`transitive usage requirements <Target Usage Requirements>` or the use of +:ref:`usage requirements <Target Usage Requirements>` or the use of :manual:`compile features <cmake-compile-features(7)>` can raise the required language standard above what ``<LANG>_STANDARD`` specifies.
diff --git a/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst b/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst index e61125b..532057b 100644 --- a/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst +++ b/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst
@@ -20,7 +20,7 @@ Note that the actual language standard used may be higher than that specified by :prop_tgt:`<LANG>_STANDARD`, regardless of the value of ``<LANG>_STANDARD_REQUIRED``. In particular, -:ref:`transitive usage requirements <Target Usage Requirements>` or the use of +:ref:`usage requirements <Target Usage Requirements>` or the use of :manual:`compile features <cmake-compile-features(7)>` can raise the required language standard above what :prop_tgt:`<LANG>_STANDARD` specifies.
diff --git a/Help/prop_tgt/LINKER_TYPE.rst b/Help/prop_tgt/LINKER_TYPE.rst index 0a016fe..23e1e4c 100644 --- a/Help/prop_tgt/LINKER_TYPE.rst +++ b/Help/prop_tgt/LINKER_TYPE.rst
@@ -8,8 +8,7 @@ .. include:: ../variable/LINKER_PREDEFINED_TYPES.txt -This property is not supported on :generator:`Green Hills MULTI` and -:generator:`Visual Studio 9 2008` generators. +This property is not supported on :generator:`Green Hills MULTI` generator. The implementation details for the selected linker will be provided by the :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` variable. For example:
diff --git a/Help/prop_tgt/SOURCES.rst b/Help/prop_tgt/SOURCES.rst index 1ebfa14..1688ef7 100644 --- a/Help/prop_tgt/SOURCES.rst +++ b/Help/prop_tgt/SOURCES.rst
@@ -18,7 +18,7 @@ Paths that are for files generated by the build will be treated as relative to the build directory of the target, if the path is not already specified as an absolute path. Note that whether a file is seen as -generated may be affected by policy :policy:`CMP0118`. +generated may be affected by policies :policy:`CMP0118` and :policy:`CMP0163`. If a path does not start with a generator expression, is not an absolute path and is not a generated file, it will be treated as relative to
diff --git a/Help/prop_tgt/Swift_MODULE_DIRECTORY.rst b/Help/prop_tgt/Swift_MODULE_DIRECTORY.rst index 619377a..77f8311 100644 --- a/Help/prop_tgt/Swift_MODULE_DIRECTORY.rst +++ b/Help/prop_tgt/Swift_MODULE_DIRECTORY.rst
@@ -10,3 +10,15 @@ placed in the build directory corresponding to the target's source directory. If the variable :variable:`CMAKE_Swift_MODULE_DIRECTORY` is set when a target is created its value is used to initialize this property. + +.. warning:: + + This property does not currently provide a way to express per-config + module directories, so use with multi-config generators is problematic: + + * The :generator:`Xcode` generator does not implement the property at all. + + * The :generator:`Ninja Multi-Config` generator implements this property, + but module files generated for different build configurations have the + same path, which can lead to subtle problems when building more than + one configuration.
diff --git a/Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst b/Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst new file mode 100644 index 0000000..b190f3b --- /dev/null +++ b/Help/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.rst
@@ -0,0 +1,19 @@ +TRANSITIVE_COMPILE_PROPERTIES +----------------------------- + +.. versionadded:: 3.30 + +Properties that the :genex:`TARGET_PROPERTY` generator expression, on the +target and its dependents, evaluates as the union of values collected from +the transitive closure of link dependencies, excluding entries guarded by +:genex:`LINK_ONLY`. + +The value is a :ref:`semicolon-separated list <CMake Language Lists>` +of :ref:`custom transitive property <Custom Transitive Properties>` names. +Any leading ``INTERFACE_`` prefix is ignored, e.g., ``INTERFACE_PROP`` is +treated as just ``PROP``. + +See documentation of the :genex:`TARGET_PROPERTY` generator expression +for details of custom transitive property evaluation. See also the +:prop_tgt:`TRANSITIVE_LINK_PROPERTIES` target property, which includes +entries guarded by :genex:`LINK_ONLY`.
diff --git a/Help/prop_tgt/TRANSITIVE_LINK_PROPERTIES.rst b/Help/prop_tgt/TRANSITIVE_LINK_PROPERTIES.rst new file mode 100644 index 0000000..adb5808 --- /dev/null +++ b/Help/prop_tgt/TRANSITIVE_LINK_PROPERTIES.rst
@@ -0,0 +1,19 @@ +TRANSITIVE_LINK_PROPERTIES +-------------------------- + +.. versionadded:: 3.30 + +Properties that the :genex:`TARGET_PROPERTY` generator expression, on the +target and its dependents, evaluates as the union of values collected from +the transitive closure of link dependencies, including entries guarded by +:genex:`LINK_ONLY`. + +The value is a :ref:`semicolon-separated list <CMake Language Lists>` +of :ref:`custom transitive property <Custom Transitive Properties>` names. +Any leading ``INTERFACE_`` prefix is ignored, e.g., ``INTERFACE_PROP`` is +treated as just ``PROP``. + +See documentation of the :genex:`TARGET_PROPERTY` generator expression +for details of custom transitive property evaluation. See also the +:prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` target property, which excludes +entries guarded by :genex:`LINK_ONLY`..
diff --git a/Help/prop_tgt/VS_FILTER_PROPS.rst b/Help/prop_tgt/VS_FILTER_PROPS.rst new file mode 100644 index 0000000..3de0a16 --- /dev/null +++ b/Help/prop_tgt/VS_FILTER_PROPS.rst
@@ -0,0 +1,10 @@ +VS_FILTER_PROPS +--------------- + +.. versionadded:: 3.30 + +Sets the filter props file to be included in the visual studio +C++ project filter file. + +The ``*.filter.props`` files can be used for Visual Studio wide +configuration which is independent from cmake.
diff --git a/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst b/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst index ce49316..072475f 100644 --- a/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst +++ b/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst
@@ -1,12 +1,9 @@ VS_GLOBAL_KEYWORD ----------------- -Visual Studio project keyword for VS 10 (2010) and newer. +Visual Studio project keyword. Sets the "keyword" attribute for a generated Visual Studio project. Defaults to "Win32Proj". You may wish to override this value with "ManagedCProj", for example, in a Visual Studio managed C++ unit test project. - -Use the :prop_tgt:`VS_KEYWORD` target property to set the -keyword for Visual Studio 9 (2008) and older.
diff --git a/Help/prop_tgt/VS_KEYWORD.rst b/Help/prop_tgt/VS_KEYWORD.rst index f04d109..b2ce78a 100644 --- a/Help/prop_tgt/VS_KEYWORD.rst +++ b/Help/prop_tgt/VS_KEYWORD.rst
@@ -1,10 +1,9 @@ VS_KEYWORD ---------- -Visual Studio project keyword for VS 9 (2008) and older. - -Can be set to change the visual studio keyword, for example Qt -integration works better if this is set to Qt4VSv1.0. +Removed. This once specified the Visual Studio project keyword +for the :generator:`Visual Studio 9 2008` generator, and older, +but all of those generators have been removed. Use the :prop_tgt:`VS_GLOBAL_KEYWORD` target property to set the keyword for Visual Studio 12 (2013) and newer.
diff --git a/Help/prop_tgt/VS_USE_DEBUG_LIBRARIES-PURPOSE.txt b/Help/prop_tgt/VS_USE_DEBUG_LIBRARIES-PURPOSE.txt new file mode 100644 index 0000000..8d3714e --- /dev/null +++ b/Help/prop_tgt/VS_USE_DEBUG_LIBRARIES-PURPOSE.txt
@@ -0,0 +1,18 @@ +Indicate to :ref:`Visual Studio Generators` what configurations are considered +debug configurations. This controls the ``UseDebugLibraries`` setting in +each configuration of a ``.vcxproj`` file. + +The "Use Debug Libraries" setting in Visual Studio projects, despite its +specific-sounding name, is a general-purpose indicator of what configurations +are considered debug configurations. In standalone projects, this may affect +MSBuild's default selection of MSVC runtime library, optimization flags, +runtime checks, and similar settings. In CMake projects those settings are +typically generated explicitly based on the project's specification, e.g., +the MSVC runtime library is controlled by |MSVC_RUNTIME_LIBRARY|. However, +the ``UseDebugLibraries`` indicator is useful for reference by both humans +and tools, and may also affect the behavior of platform-specific SDKs. + +Set |VS_USE_DEBUG_LIBRARIES| to a true or false value to indicate whether +each configuration is considered a debug configuration. The value may also +be the empty string (``""``) in which case no ``UseDebugLibraries`` will be +added explicitly by CMake, and MSBuild will use its default value, ``false``.
diff --git a/Help/prop_tgt/VS_USE_DEBUG_LIBRARIES.rst b/Help/prop_tgt/VS_USE_DEBUG_LIBRARIES.rst new file mode 100644 index 0000000..3d5ada4 --- /dev/null +++ b/Help/prop_tgt/VS_USE_DEBUG_LIBRARIES.rst
@@ -0,0 +1,28 @@ +VS_USE_DEBUG_LIBRARIES +---------------------- + +.. versionadded:: 3.30 + +.. |VS_USE_DEBUG_LIBRARIES| replace:: ``VS_USE_DEBUG_LIBRARIES`` +.. |MSVC_RUNTIME_LIBRARY| replace:: :prop_tgt:`MSVC_RUNTIME_LIBRARY` + +.. include:: VS_USE_DEBUG_LIBRARIES-PURPOSE.txt + +Use :manual:`generator expressions <cmake-generator-expressions(7)>` +for per-configuration specification. For example, the code: + +.. code-block:: cmake + + add_executable(foo foo.c) + set_property(TARGET foo PROPERTY + VS_USE_DEBUG_LIBRARIES "$<CONFIG:Debug,Custom>") + +indicates that target ``foo`` considers its "Debug" and "Custom" +configurations to be debug configurations, and its other configurations +to be non-debug configurations. + +The property is initialized from the value of the +:variable:`CMAKE_VS_USE_DEBUG_LIBRARIES` variable, if it is set. +If the property is not set then CMake generates ``UseDebugLibraries`` using +heuristics to determine which configurations are debug configurations. +See policy :policy:`CMP0162`.
diff --git a/Help/release/3.20.rst b/Help/release/3.20.rst index ebd0f91..51e4594 100644 --- a/Help/release/3.20.rst +++ b/Help/release/3.20.rst
@@ -147,8 +147,9 @@ for the associated :variable:`CMAKE_EXPORT_COMPILE_COMMANDS` variable to allow for configuration of exporting compile commands per target. -* The :prop_sf:`GENERATED` source-file property is now visible - from any directory scope, regardless of the scope in which it is set. +* Generated sources may be used across directories without manual marking. + Additionally, the :prop_sf:`GENERATED` source file property can no longer be + turned off once turned on, nor can it be set to other than boolean values. See policy :policy:`CMP0118`. * The :prop_tgt:`UNITY_BUILD_UNIQUE_ID` target property
diff --git a/Help/release/3.25.rst b/Help/release/3.25.rst index cb6984c..646c5ba 100644 --- a/Help/release/3.25.rst +++ b/Help/release/3.25.rst
@@ -27,9 +27,7 @@ Languages --------- -* The :manual:`Compile Features <cmake-compile-features(7)>` functionality - is now aware of C++26, and defines a ``cxx_std_26`` meta-feature. - C++26 compiler modes may also be specified via the :prop_tgt:`CXX_STANDARD`, +* C++26 compiler modes may now be specified via the :prop_tgt:`CXX_STANDARD`, :prop_tgt:`CUDA_STANDARD`, :prop_tgt:`HIP_STANDARD`, or :prop_tgt:`OBJCXX_STANDARD` target properties.
diff --git a/Help/release/3.30.rst b/Help/release/3.30.rst new file mode 100644 index 0000000..32345be --- /dev/null +++ b/Help/release/3.30.rst
@@ -0,0 +1,300 @@ +CMake 3.30 Release Notes +************************ + +.. only:: html + + .. contents:: + +Changes made since CMake 3.29 include the following. + +New Features +============ + +Presets +------- + +* :manual:`cmake-presets(7)` files now support schema version ``9``. + ``include`` fields now expand all macros except ``$env{}`` and + preset-specific macros, i.e., those derived from the fields + inside a preset's definition. + +File-Based API +-------------- + +* The :manual:`cmake-file-api(7)` "cmakeFiles" version 1 object's ``version`` + field has been updated to 1.1. It gained a ``globsDependent`` field to + report :command:`file(GLOB)` calls using ``CONFIGURE_DEPENDS``. + +Generators +---------- + +* :ref:`Visual Studio Generators` now add ``UseDebugLibraries`` indicators to + ``.vcxproj`` files to denote which configurations are debug configurations. + See policy :policy:`CMP0162`. + +Languages +--------- + +* The :manual:`Compile Features <cmake-compile-features(7)>` functionality + now implements support for the ``cxx_std_26`` and ``cuda_std_26`` + meta-features to indicate that the compiler mode must be at least C++26. + These meta-features were first documented by CMake 3.25, but were not fully + implemented. + +Commands +-------- + +* The :command:`add_library` command, on platforms that do not support shared + libraries, now rejects creation of shared libraries instead of automatically + converting them to static libraries. See policy :policy:`CMP0164`. + +* The :command:`enable_language` command now fails with an error + if it is called before the first :command:`project` call. + See policy :policy:`CMP0165`. + +* The :command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands + gained a ``TLS_VERSION <min>`` option to specify the minimum TLS + version for connections to ``https://`` URLs. + +Variables +--------- + +* The :variable:`CMAKE_<LANG>_STANDARD_LATEST` variable was added to + describe the latest ``<LANG>`` language standard CMake supports for + the selected compiler. + +* The :envvar:`CMAKE_TLS_VERIFY` environment variable was added as a fallback + to the existing :variable:`CMAKE_TLS_VERIFY` variable. It specifies + whether to verify the server certificate for ``https://`` URLs by default. + +* The :variable:`CMAKE_TLS_VERSION` variable and :envvar:`CMAKE_TLS_VERSION` + environment variable were added to specify a default minimum TLS version + for connections to ``https://`` URLs by the :command:`file(DOWNLOAD)` + and :command:`file(UPLOAD)` commands. + +* The :variable:`CMAKE_VS_USE_DEBUG_LIBRARIES` variable and corresponding + :prop_tgt:`VS_USE_DEBUG_LIBRARIES` target property were added to explicitly + control ``UseDebugLibraries`` indicators in ``.vcxproj`` files. + +Properties +---------- + +* The :prop_sf:`GENERATED` source file property is now visible in all + directories. See policy :policy:`CMP0163`. Policy :policy:`CMP0118`'s + documentation has been revised to describe its actual effects. + +* The :prop_gbl:`PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE` global property + can be used to propagate :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` into + :command:`try_compile` calls that use the + :ref:`whole-project signature <Try Compiling Whole Projects>`. + This is primarily intended as a way for dependency providers to be enabled + in such :command:`try_compile` calls. + +* A :prop_tgt:`VS_FILTER_PROPS` target property was added to tell + :ref:`Visual Studio Generators` to use a custom MSBuild filter + ``.props`` file. + +Modules +------- + +* The :module:`ExternalProject` module's :command:`ExternalProject_Add` + command gained a ``TLS_VERSION <min>`` option, and support for the + :variable:`CMAKE_TLS_VERSION` variable and :envvar:`CMAKE_TLS_VERSION` + environment variable, to specify the minimum TLS version for connections + to ``https://`` URLs. + +* The :module:`FindBacktrace` module now provides an imported target. + +* The :module:`FindBLAS` and :module:`FindLAPACK` modules gained + support for ``libblastrampoline``. + +* The :module:`FindCUDAToolkit` module now provides a target for + ``libnvfatbin`` and ``libnvfatbin_static``, if found. + +* The :module:`FindCUDAToolkit` module now searches the + :variable:`CMAKE_CUDA_COMPILER <CMAKE_<LANG>_COMPILER>` + variable and the :envvar:`CUDACXX` environment variable + even when the ``CUDA`` language isn't enabled. + +* The :module:`FindOpenMP` module gained an ``OpenMP_RUNTIME_MSVC`` + option to control the OpenMP runtime used with MSVC. + +* The :module:`FindPython` and :module:`FindPython3` modules gained + support for the free threaded Python version. + +* The :module:`FindPython`, :module:`FindPython2`, and :module:`FindPython3` + modules, on Windows, now offer better support for the Python debug variant: + + * new variables: + + * ``Python_EXECUTABLE_DEBUG`` + * ``Python_INTERPRETER`` + * ``Python_DEBUG_POSTFIX`` + + * new targets: + + * ``Python::InterpreterDebug`` + * ``Python::InterpreterMultiConfig`` + + The ``python_add_library()`` command now manages the + :prop_tgt:`DEBUG_POSTFIX` target property based on the value + of the ``Python_DEBUG_POSTFIX`` variable. + +Generator Expressions +--------------------- + +* The :genex:`<LANG>_COMPILER_FRONTEND_VARIANT <C_COMPILER_FRONTEND_VARIANT>` + family of generator expressions were added to access the value of the + associated :variable:`CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT` variables. + +* Link features, as used with the :genex:`LINK_LIBRARY` generator expression, + gained the ability to have attributes that describe their behavior by + specifying the :variable:`CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` or + :variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` variables. + +* The :genex:`QUOTE` generator expression was added to evaluate to ``"``. + +* The :genex:`TARGET_PROPERTY` generator expression learned to evaluate + :ref:`custom transitive properties <Custom Transitive Properties>` + defined by new :prop_tgt:`TRANSITIVE_COMPILE_PROPERTIES` and + :prop_tgt:`TRANSITIVE_LINK_PROPERTIES` target properties. + +* The :genex:`TARGET_PROPERTY` generator expression now evaluates target + properties :prop_tgt:`INTERFACE_LINK_OPTIONS`, + :prop_tgt:`INTERFACE_LINK_DIRECTORIES`, and + :prop_tgt:`INTERFACE_LINK_DEPENDS` correctly by following private + dependencies of static libraries. See policy :policy:`CMP0166`. + +CTest +----- + +* The :command:`ctest_submit` command and :option:`ctest -T Submit <ctest -T>` + step gained ``TLSVersion`` and ``TLSVerify`` options to control negotiation + with ``https://`` URLs. See the :variable:`CTEST_TLS_VERSION` and + :variable:`CTEST_TLS_VERIFY` variables. + +CPack +----- + +* The :cpack_gen:`CPack Inno Setup Generator` is now available + on non-Windows hosts. + +* The :cpack_gen:`CPack NuGet Generator` gained the + :variable:`CPACK_NUGET_PACKAGE_README`, + :variable:`CPACK_NUGET_PACKAGE_REPOSITORY_URL`, + :variable:`CPACK_NUGET_PACKAGE_REPOSITORY_TYPE`, + :variable:`CPACK_NUGET_PACKAGE_REPOSITORY_BRANCH`, and + :variable:`CPACK_NUGET_PACKAGE_REPOSITORY_COMMIT` variables. + +* The :cpack_gen:`CPack NuGet Generator` can now generate dependency groups + for framework-specific dependencies. The :variable:`CPACK_NUGET_PACKAGE_TFMS` + variable was added to specify a list of target framework monikers (TFMs) + for which groups should be generated. + +* The :cpack_gen:`CPack WIX Generator` gained support for WiX Toolset v4. + See the :variable:`CPACK_WIX_VERSION` variable. + +Deprecated and Removed Features +=============================== + +* The :module:`FindBoost` module has been removed by policy :policy:`CMP0167`. + Port projects to upstream Boost's ``BoostConfig.cmake`` package + configuration file, for which ``find_package(Boost)`` now searches. + +* Calling :command:`FetchContent_Populate` with just the name of a + dependency is now deprecated. Projects should call + :command:`FetchContent_MakeAvailable` instead. See policy :policy:`CMP0169`. + Calling :command:`FetchContent_Populate` with full population details + rather than just a dependency name remains fully supported. + +* The :generator:`Visual Studio 9 2008` generator has been removed. + +Other Changes +============= + +* The precompiled Windows ``.msi`` installers provided on + `cmake.org <https://cmake.org/download/>`_, when performing a fresh + installation, now modify the system-wide ``PATH`` by default. + When replacing an existing installation of 3.30 or later, the ``PATH`` + modification preference is preserved by default. + +* The official ``.zip`` source archive provided on + `cmake.org <https://cmake.org/download/>`_ now uses LF newlines, + instead of CRLF newlines, for consistency with modern conventions. + +* The durations printed after "Configuring done" and "Generating done" + messages now reflect time spent in generator-specific steps, and + in a code model evaluation step at the beginning of generation that + was not previously captured. Printed durations may appear longer + than in previous versions of CMake, but are more accurate. + +* :module:`FetchContent` now prefers to populate content directly rather + than using a separate sub-build. This may significantly improve configure + times on some systems (Windows especially, but also on macOS when using + the Xcode generator). :option:`cmake --fresh` also forces the download, + update, and patch steps of directly populated dependencies to be re-executed. + Policy :policy:`CMP0168` provides backward compatibility for those projects + that still rely on using a sub-build for content population. + +* When :variable:`FETCHCONTENT_FULLY_DISCONNECTED` is set to true, + :command:`FetchContent_MakeAvailable` and the single-argument form of + :command:`FetchContent_Populate` require that the dependency's source + directory has already been populated. CMake 3.29 and earlier did not + check this requirement, but it is now enforced, subject to policy + :policy:`CMP0170`. + +Updates +======= + +Changes made since CMake 3.30.0 include the following. + +3.30.1, 3.30.2 +-------------- + +* These versions made no changes to documented features or interfaces. + Some implementation updates were made to support ecosystem changes + and/or fix regressions. + +3.30.3 +------ + +* The :command:`project(<PROJECT-NAME>)` command now sets + :variable:`<PROJECT-NAME>_SOURCE_DIR`, :variable:`<PROJECT-NAME>_BINARY_DIR`, + and :variable:`<PROJECT-NAME>_IS_TOP_LEVEL` as normal variables in addition + to setting them as cache entries. This is needed to preserve support for + some :module:`FetchContent` use cases under policy :policy:`CMP0169`'s + NEW behavior. + +* The :module:`FindPython` and :module:`FindPython3` modules now define, + respectively, the ``Python_DEFINITIONS`` and ``Python3_DEFINITIONS`` + variables on Windows to support development with the free threaded + version of Python. The :prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` target + property is also defined for the various targets provided by these modules. + +3.30.4 +------ + +* The :command:`project(<PROJECT-NAME>)` command now sets + :variable:`<PROJECT-NAME>_SOURCE_DIR`, :variable:`<PROJECT-NAME>_BINARY_DIR`, + and :variable:`<PROJECT-NAME>_IS_TOP_LEVEL` as normal variables only if they + are already set as cache or non-cache variables when :command:`project` is + invoked. Cache entries by the same names are always set as before. + This refines 3.30.3's behavior change to restore behavior of nested + directories that call :command:`project` with the same project name, + but the implementation in this release is flawed (this release note has + been retoractively updated). It can result in different behavior between + the first and subsequent runs. Do not use CMake 3.30.4 if your project + contains nested calls to :command:`project` with the same project name + and you use these variables. + +3.30.5 +------ + +* The :command:`project(<PROJECT-NAME>)` command now sets + :variable:`<PROJECT-NAME>_SOURCE_DIR`, :variable:`<PROJECT-NAME>_BINARY_DIR`, + and :variable:`<PROJECT-NAME>_IS_TOP_LEVEL` as non-cache variables only if + they are already set as non-cache variables when :command:`project` is + invoked. Cache entries by the same names are always set as before. + This refines 3.30.3's behavior change to restore behavior of nested + directories that call :command:`project` with the same project name, + and it addresses the bug in the implementation introduced in 3.30.4.
diff --git a/Help/release/index.rst b/Help/release/index.rst index 76adcac..56bbf50 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst
@@ -13,6 +13,7 @@ .. toctree:: :maxdepth: 1 + 3.30 <3.30> 3.29 <3.29> 3.28 <3.28> 3.27 <3.27>
diff --git a/Help/variable/CMAKE_CFG_INTDIR.rst b/Help/variable/CMAKE_CFG_INTDIR.rst index 3045d91..5a1f9e8 100644 --- a/Help/variable/CMAKE_CFG_INTDIR.rst +++ b/Help/variable/CMAKE_CFG_INTDIR.rst
@@ -18,7 +18,6 @@ :: - $(ConfigurationName) = Visual Studio 9 $(Configuration) = Visual Studio 12 and above $(CONFIGURATION) = Xcode . = Make-based tools
diff --git a/Help/variable/CMAKE_CXX_COMPILER_IMPORT_STD.rst b/Help/variable/CMAKE_CXX_COMPILER_IMPORT_STD.rst new file mode 100644 index 0000000..384b5eb --- /dev/null +++ b/Help/variable/CMAKE_CXX_COMPILER_IMPORT_STD.rst
@@ -0,0 +1,14 @@ +CMAKE_CXX_COMPILER_IMPORT_STD +----------------------------- + +.. versionadded:: 3.30 + +A list of C++ standard levels for which ``import std`` support exists for the +current C++ toolchain. Support for C++\<NN\> may be detected using a +``<NN> IN_LIST CMAKE_CXX_COMPILER_IMPORT_STD`` predicate with the +:command:`if` command. + +.. note :: + + This variable is meaningful only when experimental support for ``import + std;`` has been enabled by the ``CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`` gate.
diff --git a/Help/variable/CMAKE_CXX_MODULE_STD.rst b/Help/variable/CMAKE_CXX_MODULE_STD.rst new file mode 100644 index 0000000..e2b27a0 --- /dev/null +++ b/Help/variable/CMAKE_CXX_MODULE_STD.rst
@@ -0,0 +1,15 @@ +CMAKE_CXX_MODULE_STD +-------------------- + +.. versionadded:: 3.30 + +Whether to add utility targets as dependencies to targets with at least +``cxx_std_23`` or not. + +.. note :: + + This setting is meaningful only when experimental support for ``import + std;`` has been enabled by the ``CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`` gate. + +This variable is used to initialize the :prop_tgt:`CXX_MODULE_STD` property on +all targets. See that target property for additional information.
diff --git a/Help/variable/CMAKE_HIP_COMPILE_FEATURES.rst b/Help/variable/CMAKE_HIP_COMPILE_FEATURES.rst new file mode 100644 index 0000000..d46d8c2 --- /dev/null +++ b/Help/variable/CMAKE_HIP_COMPILE_FEATURES.rst
@@ -0,0 +1,13 @@ +CMAKE_HIP_COMPILE_FEATURES +-------------------------- + +.. versionadded:: 3.21 + +List of features known to the HIP compiler + +These features are known to be available for use with the HIP compiler. This +list is a subset of the features listed in the +:prop_gbl:`CMAKE_HIP_KNOWN_FEATURES` global property. + +See the :manual:`cmake-compile-features(7)` manual for information on +compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst b/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst index 316fea2..9d34170 100644 --- a/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst +++ b/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst
@@ -13,5 +13,5 @@ .. code-block:: cmake if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "/my/default" CACHE PATH "..." FORCE) + set_property(CACHE CMAKE_INSTALL_PREFIX PROPERTY VALUE "/my/default") endif()
diff --git a/Help/variable/CMAKE_LANG_FLAGS.rst b/Help/variable/CMAKE_LANG_FLAGS.rst index 909a001..4f8f469 100644 --- a/Help/variable/CMAKE_LANG_FLAGS.rst +++ b/Help/variable/CMAKE_LANG_FLAGS.rst
@@ -24,6 +24,10 @@ Initialized by the :envvar:`HIPFLAGS` environment variable. * ``CMAKE_ISPC_FLAGS``: Initialized by the :envvar:`ISPCFLAGS` environment variable. +* ``CMAKE_OBJC_FLAGS``: + Initialized by the :envvar:`OBJCFLAGS` environment variable. +* ``CMAKE_OBJCXX_FLAGS``: + Initialized by the :envvar:`OBJCXXFLAGS` environment variable. This value is a command-line string fragment. Therefore, multiple options should be separated by spaces, and options with spaces should be quoted.
diff --git a/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_ATTRIBUTES.rst b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_ATTRIBUTES.rst new file mode 100644 index 0000000..8769d60 --- /dev/null +++ b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_ATTRIBUTES.rst
@@ -0,0 +1,10 @@ +CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES +---------------------------------------------- + +.. versionadded:: 3.30 + +This variable defines the semantics of the specified link library ``<FEATURE>`` +when linking with the link language ``<LANG>``. It takes precedence over +:variable:`CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` if that variable is also +defined for the same ``<FEATURE>``, but otherwise has similar effects. +See :variable:`CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` for further details.
diff --git a/Help/variable/CMAKE_LANG_STANDARD_LATEST.rst b/Help/variable/CMAKE_LANG_STANDARD_LATEST.rst new file mode 100644 index 0000000..3a759cf --- /dev/null +++ b/Help/variable/CMAKE_LANG_STANDARD_LATEST.rst
@@ -0,0 +1,64 @@ +CMAKE_<LANG>_STANDARD_LATEST +----------------------------- + +.. versionadded:: 3.30 + +This variable represents the minimum between the latest version of the +standard for language ``<LANG>`` which is supported by the current compiler +and the latest version which is supported by CMake. Its value will be set to +one of the supported values of the corresponding :prop_tgt:`<LANG>_STANDARD` +target property; see the documentation of that property for a list of +supported languages. + +See the :manual:`cmake-compile-features(7)` manual for information on compile +features and a list of supported compilers. + +.. note:: + + ``CMAKE_<LANG>_STANDARD_LATEST`` will never be set to a language standard + which CMake recognizes but provides no support for. Unless explicitly + stated otherwise, every value which is supported by the corresponding + :prop_tgt:`<LANG>_STANDARD` target property represents a standard of + language ``<LANG>`` which is both recognized and supported by CMake. + +Checking for Language Standard Support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is possible to use the value of the ``CMAKE_<LANG>_STANDARD_LATEST`` +variable to check for language standard support. This can be used to, e.g., +conditionally enable optional features for a distributed library. + +When doing so, one should be careful to **not** rely on integer value +comparisons between standard levels. This is because some older standards of +a given language which are supported by CMake (e.g., C++98, represented as +``98``) will have a higher numerical value than newer standards of that same +language. + +The following code sample demonstrates how one might correctly check for +C++17 support: + +.. code-block:: cmake + + # Careful! We cannot do direct integer comparisons with + # CMAKE_CXX_STANDARD_LATEST because some earlier C++ standards (e.g., + # C++98) will have a higher numerical value than our requirement (C++17). + # + # Instead, we keep a list of unsupported C++ standards and check if + # CMAKE_CXX_STANDARD_LATEST appears in that list. + set(UNSUPPORTED_CXX_STANDARDS + 98 + 11 + 14 + ) + + list(FIND UNSUPPORTED_CXX_STANDARDS ${CMAKE_CXX_STANDARD_LATEST} UNSUPPORTED_CXX_STANDARDS_INDEX) + + if(UNSUPPORTED_CXX_STANDARDS_INDEX EQUAL -1) + # We know that the current compiler supports at least C++17. Enabling + # some optional feature... + else() + message(STATUS + "Feature X is disabled because it requires C++17, but the current " + "compiler only supports C++${CMAKE_CXX_STANDARD_LATEST}." + ) + endif()
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES.rst b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES.rst new file mode 100644 index 0000000..c9966ff --- /dev/null +++ b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES.rst
@@ -0,0 +1,116 @@ +CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES +--------------------------------------- + +.. versionadded:: 3.30 + +This variable defines the behavior of the specified link library +``<FEATURE>``. It specifies how the ``<FEATURE>`` interacts with other +features, when the ``<FEATURE>`` should be applied, and aspects of how the +``<FEATURE>`` should be handled when CMake assembles the final linker +command line (e.g. de-duplication). + +The syntax of the linker flags for the ``<FEATURE>`` are controlled by the +:variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` and +:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variables. +The :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` and +:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variables +control whether the ``<FEATURE>`` is available at all. + +When linking for a particular language ``<LANG>``, +``CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES`` is ignored if the +:variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` variable is also +defined for the same ``<FEATURE>``. + +The value of ``CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES`` and +:variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` at the end of the +directory scope in which a target is defined is what matters. + +Feature Attributes Definition +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A feature attributes definition is a +:ref:`semicolon-separated list <CMake Language Lists>` of +``attribute=value(s)`` items. If an attribute has multiple values, those values +must be comma-separated. + +The following attributes are supported: + +``LIBRARY_TYPE=<library-type-list>`` + Specify the library types supported by the feature. Supported values are: + ``STATIC``, ``SHARED``, ``MODULE``, and ``EXECUTABLE``. + + If this attribute is not specified, the default is + ``LIBRARY_TYPE=STATIC,SHARED,MODULE,EXECUTABLE``. + + If the feature is used with an unsupported library type, CMake will emit a + developer warning and the feature will be ignored. + +``OVERRIDE=<feature-list>`` + Specify which features this one replaces in the event of a conflict. + This override mechanism is superseded by + :prop_tgt:`LINK_LIBRARY_OVERRIDE` or + :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target property definitions, + if defined. + + If this attribute is not specified, the default is an empty list. + +``DEDUPLICATION=YES|NO|DEFAULT`` + Specify the de-duplication strategy for a library using this feature. + + ``YES`` + The library is always de-duplicated. The default strategy CMake would + normally apply is ignored. + + ``NO`` + The library is never de-duplicated. The default strategy CMake would + normally apply is ignored. + + ``DEFAULT`` + Let CMake determine a de-duplication strategy automatically. + + If this attribute is not specified, ``DEFAULT`` will be used. + +Example +^^^^^^^ + +A common need is the loading of a full archive as part of the creation of a +shared library. The built-in ``WHOLE_ARCHIVE`` feature can be used for that +purpose. The implementation of that built-in feature sets the following +link library feature attributes: + +.. code-block:: cmake + + set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES + LIBRARY_TYPE=STATIC + OVERRIDE=DEFAULT + DEDUPLICATION=YES + ) + +``LIBRARY_TYPE=STATIC`` + This feature is only meaningful for static libraries. +``OVERRIDE=DEFAULT`` + The ``DEFAULT`` feature will be overridden by the ``WHOLE_ARCHIVE`` feature + because they are compatible and enhance the user's experience: standard + library specification and ``$<LINK_LIBRARY:WHOLE_ARCHIVE>`` can be used + freely. +``DEDUPLICATION=YES`` + When this feature is used, the linker loads all symbols from the static + library, so there is no need to repeat the library on the linker + command line. + +The ``WHOLE_ARCHIVE`` feature can be used like so: + +.. code-block:: cmake + + add_library(A STATIC ...) + add_library(B STATIC ...) + + target_link_libraries(B PUBLIC A) + target_link_libraries(A PUBLIC B) + + add_library(global SHARED ...) + target_link_libraries(global PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,A>) + +The resulting link command will only have one instance of the ``A`` library +specified, and the linker flags will ensure that all symbols are loaded from +the ``A`` library.
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt b/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt index 4b13b7c..f5c19e0 100644 --- a/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt +++ b/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
@@ -2,6 +2,9 @@ and underscores. Feature names defined in all uppercase are reserved for CMake's own built-in features (see `Predefined Features`_ further below). +Some aspects of feature behavior can be defined by the +:variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` and +:variable:`CMAKE_LINK_LIBRARY_<FEATURE>_ATTRIBUTES` variables. Feature Definitions ^^^^^^^^^^^^^^^^^^^
diff --git a/Help/variable/CMAKE_MODULE_PATH.rst b/Help/variable/CMAKE_MODULE_PATH.rst index 3021b49..213b75c 100644 --- a/Help/variable/CMAKE_MODULE_PATH.rst +++ b/Help/variable/CMAKE_MODULE_PATH.rst
@@ -5,4 +5,17 @@ represented using forward slashes, specifying a search path for CMake modules to be loaded by the :command:`include` or :command:`find_package` commands before checking the default modules that come with CMake. By default it is -empty. It is intended to be set by the project. +empty. It is intended to be set by the project. + +It's fairly common for a project to have a directory containing various +``*.cmake`` files to assist in development. Adding the directory to the +:variable:`CMAKE_MODULE_PATH` simplifies loading them. For example, a +project's top-level ``CMakeLists.txt`` file may contain: + +.. code-block:: cmake + + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + + include(Foo) # Loads ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Foo.cmake + + find_package(Bar) # Loads ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindBar.cmake
diff --git a/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst b/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst index 721ceaa..d4b256a 100644 --- a/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst +++ b/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst
@@ -5,7 +5,7 @@ Extra PATH locations that should be used when executing :command:`add_custom_command` or :command:`add_custom_target` when using the -:generator:`Visual Studio 9 2008` (or above) generator. This allows +:generator:`Visual Studio 12 2013` (or above) generator. This allows for running commands and using dll's that the IDE environment is not aware of. If not set explicitly the value is initialized by the ``CMAKE_MSVCIDE_RUN_PATH``
diff --git a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst index 54f530e..805b7ea 100644 --- a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst +++ b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst
@@ -25,7 +25,10 @@ By default, this variable is empty. It is intended to be set by the user. -See also the :variable:`CMAKE_PROJECT_INCLUDE`, -:variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, -:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, and -:variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` variables. +See also: + +* :variable:`CMAKE_PROJECT_INCLUDE` +* :variable:`CMAKE_PROJECT_INCLUDE_BEFORE` +* :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE` +* :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` +* :prop_gbl:`PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE`
diff --git a/Help/variable/CMAKE_TLS_VERIFY.rst b/Help/variable/CMAKE_TLS_VERIFY.rst index b22f1ce..5871ac7 100644 --- a/Help/variable/CMAKE_TLS_VERIFY.rst +++ b/Help/variable/CMAKE_TLS_VERIFY.rst
@@ -3,7 +3,9 @@ Specify the default value for the :command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands' ``TLS_VERIFY`` options. -If not set, the default is *off*. +If this variable is not set, the commands check the +:envvar:`CMAKE_TLS_VERIFY` environment variable. +If neither is set, the default is *off*. This variable is also used by the :module:`ExternalProject` and :module:`FetchContent` modules for internal calls to :command:`file(DOWNLOAD)`.
diff --git a/Help/variable/CMAKE_TLS_VERSION-VALUES.txt b/Help/variable/CMAKE_TLS_VERSION-VALUES.txt new file mode 100644 index 0000000..47fd2bc --- /dev/null +++ b/Help/variable/CMAKE_TLS_VERSION-VALUES.txt
@@ -0,0 +1,7 @@ +* ``1.0`` + +* ``1.1`` + +* ``1.2`` + +* ``1.3``
diff --git a/Help/variable/CMAKE_TLS_VERSION.rst b/Help/variable/CMAKE_TLS_VERSION.rst new file mode 100644 index 0000000..3e7f2ce --- /dev/null +++ b/Help/variable/CMAKE_TLS_VERSION.rst
@@ -0,0 +1,17 @@ +CMAKE_TLS_VERSION +----------------- + +.. versionadded:: 3.30 + +Specify the default value for the :command:`file(DOWNLOAD)` and +:command:`file(UPLOAD)` commands' ``TLS_VERSION`` option. +If this variable is not set, the commands check the +:envvar:`CMAKE_TLS_VERSION` environment variable. + +The value may be one of: + +.. include:: CMAKE_TLS_VERSION-VALUES.txt + +This variable is also used by the :module:`ExternalProject` and +:module:`FetchContent` modules for internal calls to +:command:`file(DOWNLOAD)` and ``git clone``.
diff --git a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst index 155931f..95e09b1 100644 --- a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst +++ b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
@@ -1,7 +1,7 @@ CMAKE_VS_DEVENV_COMMAND ----------------------- -The generators for :generator:`Visual Studio 9 2008` and above set this +The generators for :generator:`Visual Studio 12 2013` and above set this variable to the ``devenv.com`` command installed with the corresponding Visual Studio version. Note that this variable may be empty on Visual Studio Express editions because they do not provide this tool.
diff --git a/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst b/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst index ceedf28..4857269 100644 --- a/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst +++ b/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst
@@ -1,7 +1,7 @@ CMAKE_VS_INTEL_Fortran_PROJECT_VERSION -------------------------------------- -When generating for :generator:`Visual Studio 9 2008` or greater with the Intel +When generating for :generator:`Visual Studio 12 2013` or greater with the Intel Fortran plugin installed, this specifies the ``.vfproj`` project file format version. This is intended for internal use by CMake and should not be used by project code.
diff --git a/Help/variable/CMAKE_VS_USE_DEBUG_LIBRARIES.rst b/Help/variable/CMAKE_VS_USE_DEBUG_LIBRARIES.rst new file mode 100644 index 0000000..35d94bc --- /dev/null +++ b/Help/variable/CMAKE_VS_USE_DEBUG_LIBRARIES.rst
@@ -0,0 +1,29 @@ +CMAKE_VS_USE_DEBUG_LIBRARIES +---------------------------- + +.. versionadded:: 3.30 + +.. |VS_USE_DEBUG_LIBRARIES| replace:: ``CMAKE_VS_USE_DEBUG_LIBRARIES`` +.. |MSVC_RUNTIME_LIBRARY| replace:: :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` + +.. include:: ../prop_tgt/VS_USE_DEBUG_LIBRARIES-PURPOSE.txt + +Use :manual:`generator expressions <cmake-generator-expressions(7)>` +for per-configuration specification. For example, the code: + +.. code-block:: cmake + + set(CMAKE_VS_USE_DEBUG_LIBRARIES "$<CONFIG:Debug,Custom>") + +indicates that all following targets consider their "Debug" and "Custom" +configurations to be debug configurations, and their other configurations +to be non-debug configurations. + +This variable is used to initialize the :prop_tgt:`VS_USE_DEBUG_LIBRARIES` +property on all targets as they are created. It is also propagated by +calls to the :command:`try_compile` command into its test project. + +If this variable is not set then the :prop_tgt:`VS_USE_DEBUG_LIBRARIES` +property will not be set automatically. If that property is not set then +CMake generates ``UseDebugLibraries`` using heuristics to determine which +configurations are debug configurations. See policy :policy:`CMP0162`.
diff --git a/Help/variable/CTEST_CURL_OPTIONS.rst b/Help/variable/CTEST_CURL_OPTIONS.rst index 14af4e4..45e84ed 100644 --- a/Help/variable/CTEST_CURL_OPTIONS.rst +++ b/Help/variable/CTEST_CURL_OPTIONS.rst
@@ -1,6 +1,10 @@ CTEST_CURL_OPTIONS ------------------ +.. deprecated:: 3.30 + + Use the :variable:`CTEST_TLS_VERIFY` variable instead. + .. versionadded:: 3.1 Specify the CTest ``CurlOptions`` setting
diff --git a/Help/variable/CTEST_TLS_VERIFY.rst b/Help/variable/CTEST_TLS_VERIFY.rst new file mode 100644 index 0000000..9b3d96c --- /dev/null +++ b/Help/variable/CTEST_TLS_VERIFY.rst
@@ -0,0 +1,13 @@ +CTEST_TLS_VERIFY +---------------- + +.. versionadded:: 3.30 + +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 +indicating whether to verify the server certificate when submitting +to a dashboard via ``https://`` URLs. + +If ``CTEST_TLS_VERIFY`` is not set, the :variable:`CMAKE_TLS_VERIFY` variable +or :envvar:`CMAKE_TLS_VERIFY` environment variable is used instead.
diff --git a/Help/variable/CTEST_TLS_VERSION.rst b/Help/variable/CTEST_TLS_VERSION.rst new file mode 100644 index 0000000..f8123df --- /dev/null +++ b/Help/variable/CTEST_TLS_VERSION.rst
@@ -0,0 +1,16 @@ +CTEST_TLS_VERSION +----------------- + +.. versionadded:: 3.30 + +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 +TLS version allowed when submitting to a dashboard via ``https://`` URLs. + +The value may be one of: + +.. include:: CMAKE_TLS_VERSION-VALUES.txt + +If ``CTEST_TLS_VERSION`` is not set, the :variable:`CMAKE_TLS_VERSION` variable +or :envvar:`CMAKE_TLS_VERSION` environment variable is used instead.
diff --git a/Modules/CMakeASM_NASMInformation.cmake b/Modules/CMakeASM_NASMInformation.cmake index a72575b..898b823 100644 --- a/Modules/CMakeASM_NASMInformation.cmake +++ b/Modules/CMakeASM_NASMInformation.cmake
@@ -12,6 +12,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_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(CMAKE_ASM_NASM_OBJECT_FORMAT win64) else() set(CMAKE_ASM_NASM_OBJECT_FORMAT win32) endif() @@ -20,6 +22,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_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(CMAKE_ASM_NASM_OBJECT_FORMAT macho64) else() set(CMAKE_ASM_NASM_OBJECT_FORMAT macho) endif() @@ -28,6 +32,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_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64) else() set(CMAKE_ASM_NASM_OBJECT_FORMAT elf) endif() @@ -38,6 +44,11 @@ set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>") endif() +if(NOT CMAKE_ASM_NASM_LINK_EXECUTABLE) + set(CMAKE_ASM_NASM_LINK_EXECUTABLE + "<CMAKE_LINKER> <CMAKE_ASM_NASM_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>") +endif() + if(CMAKE_ASM_NASM_COMPILER_ID STREQUAL "NASM") set(CMAKE_DEPFILE_FLAGS_ASM_NASM "-MD <DEP_FILE> -MT <DEP_TARGET>")
diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in index df2a060..1726fe9 100644 --- a/Modules/CMakeCCompiler.cmake.in +++ b/Modules/CMakeCCompiler.cmake.in
@@ -6,6 +6,7 @@ set(CMAKE_C_COMPILER_WRAPPER "@CMAKE_C_COMPILER_WRAPPER@") set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "@CMAKE_C_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_C_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_C_EXTENSIONS_COMPUTED_DEFAULT@") +set(CMAKE_C_STANDARD_LATEST "@CMAKE_C_STANDARD_LATEST@") set(CMAKE_C_COMPILE_FEATURES "@CMAKE_C_COMPILE_FEATURES@") set(CMAKE_C90_COMPILE_FEATURES "@CMAKE_C90_COMPILE_FEATURES@") set(CMAKE_C99_COMPILE_FEATURES "@CMAKE_C99_COMPILE_FEATURES@")
diff --git a/Modules/CMakeCCompilerId.c.in b/Modules/CMakeCCompilerId.c.in index 82d56cf..759cdf4 100644 --- a/Modules/CMakeCCompilerId.c.in +++ b/Modules/CMakeCCompilerId.c.in
@@ -39,19 +39,28 @@ @CMAKE_C_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_C_COMPILER_ID_ERROR_FOR_TEST@ +#define C_STD_99 199901L +#define C_STD_11 201112L +#define C_STD_17 201710L +#define C_STD_23 202311L + +#ifdef __STDC_VERSION__ +# define C_STD __STDC_VERSION__ +#endif + #if !defined(__STDC__) && !defined(__clang__) # if defined(_MSC_VER) || defined(__ibmxl__) || defined(__IBMC__) # define C_VERSION "90" # else # define C_VERSION # endif -#elif __STDC_VERSION__ > 201710L +#elif C_STD > C_STD_17 # define C_VERSION "23" -#elif __STDC_VERSION__ >= 201710L +#elif C_STD > C_STD_11 # define C_VERSION "17" -#elif __STDC_VERSION__ >= 201000L +#elif C_STD > C_STD_99 # define C_VERSION "11" -#elif __STDC_VERSION__ >= 199901L +#elif C_STD >= C_STD_99 # define C_VERSION "99" #else # define C_VERSION "90"
diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake index 998e476..72fb801 100644 --- a/Modules/CMakeCInformation.cmake +++ b/Modules/CMakeCInformation.cmake
@@ -91,23 +91,6 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE_C "${_override}") endif() -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_C_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_C_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_C_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) -endif() - set(CMAKE_C_FLAGS_INIT "$ENV{CFLAGS} ${CMAKE_C_FLAGS_INIT}") cmake_initialize_per_config_variable(CMAKE_C_FLAGS "Flags used by the C compiler") @@ -129,6 +112,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(C) # now define the following rule variables @@ -191,16 +175,4 @@ "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>") endif() -if(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG) - set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP) - set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) -endif() - -if(NOT CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG) - set(CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) -endif() - set(CMAKE_C_INFORMATION_LOADED 1)
diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in index 3af02a4..c054e5c 100644 --- a/Modules/CMakeCUDACompiler.cmake.in +++ b/Modules/CMakeCUDACompiler.cmake.in
@@ -7,6 +7,7 @@ set(CMAKE_CUDA_FATBINARY "@CMAKE_CUDA_FATBINARY@") set(CMAKE_CUDA_STANDARD_COMPUTED_DEFAULT "@CMAKE_CUDA_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_CUDA_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_CUDA_EXTENSIONS_COMPUTED_DEFAULT@") +set(CMAKE_CUDA_STANDARD_LATEST "@CMAKE_CUDA_STANDARD_LATEST@") set(CMAKE_CUDA_COMPILE_FEATURES "@CMAKE_CUDA_COMPILE_FEATURES@") set(CMAKE_CUDA03_COMPILE_FEATURES "@CMAKE_CUDA03_COMPILE_FEATURES@") set(CMAKE_CUDA11_COMPILE_FEATURES "@CMAKE_CUDA11_COMPILE_FEATURES@") @@ -14,6 +15,7 @@ set(CMAKE_CUDA17_COMPILE_FEATURES "@CMAKE_CUDA17_COMPILE_FEATURES@") set(CMAKE_CUDA20_COMPILE_FEATURES "@CMAKE_CUDA20_COMPILE_FEATURES@") set(CMAKE_CUDA23_COMPILE_FEATURES "@CMAKE_CUDA23_COMPILE_FEATURES@") +set(CMAKE_CUDA26_COMPILE_FEATURES "@CMAKE_CUDA26_COMPILE_FEATURES@") set(CMAKE_CUDA_PLATFORM_ID "@CMAKE_CUDA_PLATFORM_ID@") set(CMAKE_CUDA_SIMULATE_ID "@CMAKE_CUDA_SIMULATE_ID@")
diff --git a/Modules/CMakeCUDACompilerId.cu.in b/Modules/CMakeCUDACompilerId.cu.in index d5a3b03..cecb948 100644 --- a/Modules/CMakeCUDACompilerId.cu.in +++ b/Modules/CMakeCUDACompilerId.cu.in
@@ -16,16 +16,34 @@ @CMAKE_CUDA_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_CUDA_COMPILER_ID_ERROR_FOR_TEST@ +#define CXX_STD_11 201103L +#define CXX_STD_14 201402L +#define CXX_STD_17 201703L +#define CXX_STD_20 202002L +#define CXX_STD_23 202302L + +#if defined(_MSC_VER) && defined(_MSVC_LANG) +# if _MSVC_LANG > __cplusplus +# define CXX_STD _MSVC_LANG +# else +# define CXX_STD __cplusplus +# endif +#else +# define CXX_STD __cplusplus +#endif + const char* info_language_standard_default = "INFO" ":" "standard_default[" -#if __cplusplus > 202002L +#if CXX_STD > CXX_STD_23 + "26" +#elif CXX_STD > CXX_STD_20 "23" -#elif __cplusplus > 201703L +#elif CXX_STD > CXX_STD_17 "20" -#elif __cplusplus >= 201703L +#elif CXX_STD > CXX_STD_14 "17" -#elif __cplusplus >= 201402L +#elif CXX_STD > CXX_STD_11 "14" -#elif __cplusplus >= 201103L +#elif CXX_STD >= CXX_STD_11 "11" #else "03"
diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake index e774088..66a5faa 100644 --- a/Modules/CMakeCUDAInformation.cmake +++ b/Modules/CMakeCUDAInformation.cmake
@@ -55,59 +55,6 @@ endif() -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG) - set(CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG_SEP) - set(CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_CUDA_FLAG) - set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_EXE_EXPORTS_CUDA_FLAG) - set(CMAKE_EXE_EXPORTS_CUDA_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_CUDA_FLAG) - set(CMAKE_SHARED_LIBRARY_SONAME_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG) - set(CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG_SEP) - set(CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG_SEP}) -endif() - -if(NOT CMAKE_EXECUTABLE_RPATH_LINK_CUDA_FLAG) - set(CMAKE_EXECUTABLE_RPATH_LINK_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_CUDA_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CUDA_WITH_RUNTIME_PATH) - set(CMAKE_SHARED_LIBRARY_LINK_CUDA_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) -endif() - - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_CUDA_FLAGS ${CMAKE_SHARED_LIBRARY_CUDA_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_CUDA_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS}) -endif() - -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() # add the flags to the cache based # on the initial values computed in the platform/*.cmake files @@ -129,6 +76,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(CUDA) # now define the following rules: # CMAKE_CUDA_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 55f1113..2f7c3e4 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -6,6 +6,7 @@ set(CMAKE_CXX_COMPILER_WRAPPER "@CMAKE_CXX_COMPILER_WRAPPER@") set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "@CMAKE_CXX_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT@") +set(CMAKE_CXX_STANDARD_LATEST "@CMAKE_CXX_STANDARD_LATEST@") set(CMAKE_CXX_COMPILE_FEATURES "@CMAKE_CXX_COMPILE_FEATURES@") set(CMAKE_CXX98_COMPILE_FEATURES "@CMAKE_CXX98_COMPILE_FEATURES@") set(CMAKE_CXX11_COMPILE_FEATURES "@CMAKE_CXX11_COMPILE_FEATURES@") @@ -13,6 +14,7 @@ set(CMAKE_CXX17_COMPILE_FEATURES "@CMAKE_CXX17_COMPILE_FEATURES@") set(CMAKE_CXX20_COMPILE_FEATURES "@CMAKE_CXX20_COMPILE_FEATURES@") set(CMAKE_CXX23_COMPILE_FEATURES "@CMAKE_CXX23_COMPILE_FEATURES@") +set(CMAKE_CXX26_COMPILE_FEATURES "@CMAKE_CXX26_COMPILE_FEATURES@") set(CMAKE_CXX_PLATFORM_ID "@CMAKE_CXX_PLATFORM_ID@") set(CMAKE_CXX_SIMULATE_ID "@CMAKE_CXX_SIMULATE_ID@") @@ -90,3 +92,6 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES@") set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@") set(CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR "@CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR@") + +set(CMAKE_CXX_COMPILER_IMPORT_STD "") +@CMAKE_CXX_IMPORT_STD@
diff --git a/Modules/CMakeCXXCompilerId.cpp.in b/Modules/CMakeCXXCompilerId.cpp.in index 2643326..1cd8006 100644 --- a/Modules/CMakeCXXCompilerId.cpp.in +++ b/Modules/CMakeCXXCompilerId.cpp.in
@@ -33,32 +33,73 @@ @CMAKE_CXX_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_CXX_COMPILER_ID_ERROR_FOR_TEST@ -#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) && _MSVC_LANG < 201403L -# if defined(__INTEL_CXX11_MODE__) -# if defined(__cpp_aggregate_nsdmi) -# define CXX_STD 201402L -# else -# define CXX_STD 201103L -# endif +#define CXX_STD_98 199711L +#define CXX_STD_11 201103L +#define CXX_STD_14 201402L +#define CXX_STD_17 201703L +#define CXX_STD_20 202002L +#define CXX_STD_23 202302L + +#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) +# if _MSVC_LANG > CXX_STD_17 +# define CXX_STD _MSVC_LANG +# elif _MSVC_LANG == CXX_STD_17 && defined(__cpp_aggregate_paren_init) +# define CXX_STD CXX_STD_20 +# elif _MSVC_LANG > CXX_STD_14 && __cplusplus > CXX_STD_17 +# define CXX_STD CXX_STD_20 +# elif _MSVC_LANG > CXX_STD_14 +# define CXX_STD CXX_STD_17 +# elif defined(__INTEL_CXX11_MODE__) && defined(__cpp_aggregate_nsdmi) +# define CXX_STD CXX_STD_14 +# elif defined(__INTEL_CXX11_MODE__) +# define CXX_STD CXX_STD_11 # else -# define CXX_STD 199711L +# define CXX_STD CXX_STD_98 # endif #elif defined(_MSC_VER) && defined(_MSVC_LANG) -# define CXX_STD _MSVC_LANG +# if _MSVC_LANG > __cplusplus +# define CXX_STD _MSVC_LANG +# else +# define CXX_STD __cplusplus +# endif +#elif defined(__NVCOMPILER) +# if __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init) +# define CXX_STD CXX_STD_20 +# else +# define CXX_STD __cplusplus +# endif +#elif defined(__INTEL_COMPILER) || defined(__PGI) +# if __cplusplus == CXX_STD_11 && defined(__cpp_namespace_attributes) +# define CXX_STD CXX_STD_17 +# elif __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi) +# define CXX_STD CXX_STD_14 +# else +# define CXX_STD __cplusplus +# endif +#elif (defined(__IBMCPP__) || defined(__ibmxl__)) && defined(__linux__) +# if __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi) +# define CXX_STD CXX_STD_14 +# else +# define CXX_STD __cplusplus +# endif +#elif __cplusplus == 1 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CXX_STD CXX_STD_11 #else # define CXX_STD __cplusplus #endif const char* info_language_standard_default = "INFO" ":" "standard_default[" -#if CXX_STD > 202002L +#if CXX_STD > CXX_STD_23 + "26" +#elif CXX_STD > CXX_STD_20 "23" -#elif CXX_STD > 201703L +#elif CXX_STD > CXX_STD_17 "20" -#elif CXX_STD >= 201703L +#elif CXX_STD > CXX_STD_14 "17" -#elif CXX_STD >= 201402L +#elif CXX_STD > CXX_STD_11 "14" -#elif CXX_STD >= 201103L +#elif CXX_STD >= CXX_STD_11 "11" #else "98"
diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake index 3753d18..e521fb8 100644 --- a/Modules/CMakeCXXInformation.cmake +++ b/Modules/CMakeCXXInformation.cmake
@@ -91,117 +91,6 @@ endif() -# Create a set of shared library variable specific to C++ -# For 90% of the systems, these are the same flags as the C versions -# so if these are not set just copy the flags from the c version -if(NOT CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS) - set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) -endif() - -if(NOT CMAKE_CXX_COMPILE_OPTIONS_PIC) - set(CMAKE_CXX_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC}) -endif() - -if(NOT CMAKE_CXX_COMPILE_OPTIONS_PIE) - set(CMAKE_CXX_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE}) -endif() -if(NOT CMAKE_CXX_LINK_OPTIONS_PIE) - set(CMAKE_CXX_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE}) -endif() -if(NOT CMAKE_CXX_LINK_OPTIONS_NO_PIE) - set(CMAKE_CXX_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE}) -endif() - -if(NOT CMAKE_CXX_COMPILE_OPTIONS_DLL) - set(CMAKE_CXX_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_CXX_FLAGS) - set(CMAKE_SHARED_LIBRARY_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) - set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG) - set(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP) - set(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG) - set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_EXE_EXPORTS_CXX_FLAG) - set(CMAKE_EXE_EXPORTS_CXX_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG) - set(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG) - set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP) - set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP}) -endif() - -if(NOT CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG) - set(CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH) - set(CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) -endif() - -if(NOT CMAKE_INCLUDE_FLAG_CXX) - set(CMAKE_INCLUDE_FLAG_CXX ${CMAKE_INCLUDE_FLAG_C}) -endif() - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS}) -endif() - -# repeat for modules -if(NOT CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS) - set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS}) -endif() - -if(NOT CMAKE_SHARED_MODULE_CXX_FLAGS) - set(CMAKE_SHARED_MODULE_CXX_FLAGS ${CMAKE_SHARED_MODULE_C_FLAGS}) -endif() - -# Initialize CXX link type selection flags from C versions. -foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE) - if(NOT CMAKE_${type}_LINK_STATIC_CXX_FLAGS) - set(CMAKE_${type}_LINK_STATIC_CXX_FLAGS - ${CMAKE_${type}_LINK_STATIC_C_FLAGS}) - endif() - if(NOT CMAKE_${type}_LINK_DYNAMIC_CXX_FLAGS) - set(CMAKE_${type}_LINK_DYNAMIC_CXX_FLAGS - ${CMAKE_${type}_LINK_DYNAMIC_C_FLAGS}) - endif() -endforeach() - -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - # add the flags to the cache based # on the initial values computed in the platform/*.cmake files # use _INIT variables so that this only happens the first time @@ -227,6 +116,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(CXX) # now define the following rules: # CMAKE_CXX_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeCommonLanguageInclude.cmake b/Modules/CMakeCommonLanguageInclude.cmake index b043e18..5511930 100644 --- a/Modules/CMakeCommonLanguageInclude.cmake +++ b/Modules/CMakeCommonLanguageInclude.cmake
@@ -21,3 +21,112 @@ mark_as_advanced( CMAKE_VERBOSE_MAKEFILE ) + +# The Platform/* modules set a bunch of platform-specific flags expressed +# for the C toolchain. Other languages call this to copy them as defaults. +macro(_cmake_common_language_platform_flags lang) + if(NOT DEFINED CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS) + set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) + endif() + + if(NOT DEFINED CMAKE_${lang}_COMPILE_OPTIONS_PIC) + set(CMAKE_${lang}_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC}) + endif() + + if(NOT DEFINED CMAKE_${lang}_COMPILE_OPTIONS_PIE) + set(CMAKE_${lang}_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE}) + endif() + if(NOT DEFINED CMAKE_${lang}_LINK_OPTIONS_PIE) + set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE}) + endif() + if(NOT DEFINED CMAKE_${lang}_LINK_OPTIONS_NO_PIE) + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE}) + endif() + + if(NOT DEFINED CMAKE_${lang}_COMPILE_OPTIONS_DLL) + set(CMAKE_${lang}_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_${lang}_FLAGS) + set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS) + set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG) + set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP) + set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG) + set(CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) + endif() + + if(NOT DEFINED CMAKE_EXE_EXPORTS_${lang}_FLAG) + set(CMAKE_EXE_EXPORTS_${lang}_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG) + set(CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG}) + endif() + + if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG) + set(CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG}) + endif() + + if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG_SEP) + set(CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP}) + endif() + + if(NOT DEFINED CMAKE_EXECUTABLE_RPATH_LINK_${lang}_FLAG) + set(CMAKE_EXECUTABLE_RPATH_LINK_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG}) + endif() + + if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_${lang}_WITH_RUNTIME_PATH) + set(CMAKE_SHARED_LIBRARY_LINK_${lang}_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) + endif() + + if(NOT DEFINED CMAKE_INCLUDE_FLAG_${lang}) + set(CMAKE_INCLUDE_FLAG_${lang} ${CMAKE_INCLUDE_FLAG_C}) + endif() + + # for most systems a module is the same as a shared library + # so unless the variable CMAKE_MODULE_EXISTS is set just + # copy the values from the LIBRARY variables + if(NOT CMAKE_MODULE_EXISTS) + set(CMAKE_SHARED_MODULE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_${lang}_FLAGS}) + set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS}) + endif() + + if(NOT DEFINED CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS) + set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) + endif() + if(NOT DEFINED CMAKE_SHARED_MODULE_${lang}_FLAGS) + set(CMAKE_SHARED_MODULE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) + endif() + + foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE) + if(NOT DEFINED CMAKE_${type}_LINK_STATIC_${lang}_FLAGS) + set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS + ${CMAKE_${type}_LINK_STATIC_C_FLAGS}) + endif() + if(NOT DEFINED CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS) + set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS + ${CMAKE_${type}_LINK_DYNAMIC_C_FLAGS}) + endif() + endforeach() + + if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") + if(NOT DEFINED CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG) + set(CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") + endif() + if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) + set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) + endif() + endif() +endmacro()
diff --git a/Modules/CMakeDetermineCompileFeatures.cmake b/Modules/CMakeDetermineCompileFeatures.cmake deleted file mode 100644 index 09de7b1..0000000 --- a/Modules/CMakeDetermineCompileFeatures.cmake +++ /dev/null
@@ -1,246 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - - -function(cmake_determine_compile_features lang) - - if("x${lang}" STREQUAL "xC" AND COMMAND cmake_record_c_compile_features) - message(CHECK_START "Detecting ${lang} compile features") - - set(CMAKE_C90_COMPILE_FEATURES) - set(CMAKE_C99_COMPILE_FEATURES) - set(CMAKE_C11_COMPILE_FEATURES) - set(CMAKE_C17_COMPILE_FEATURES) - set(CMAKE_C23_COMPILE_FEATURES) - - include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") - - cmake_record_c_compile_features() - - if(NOT _result EQUAL 0) - message(CHECK_FAIL "failed") - return() - endif() - - if (CMAKE_C17_COMPILE_FEATURES AND CMAKE_C23_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_C23_COMPILE_FEATURES ${CMAKE_C17_COMPILE_FEATURES}) - endif() - if (CMAKE_C11_COMPILE_FEATURES AND CMAKE_C17_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_C17_COMPILE_FEATURES ${CMAKE_C11_COMPILE_FEATURES}) - endif() - if (CMAKE_C99_COMPILE_FEATURES AND CMAKE_C11_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_C11_COMPILE_FEATURES ${CMAKE_C99_COMPILE_FEATURES}) - endif() - if (CMAKE_C90_COMPILE_FEATURES AND CMAKE_C99_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_C99_COMPILE_FEATURES ${CMAKE_C90_COMPILE_FEATURES}) - endif() - - if(NOT CMAKE_C_COMPILE_FEATURES) - set(CMAKE_C_COMPILE_FEATURES - ${CMAKE_C90_COMPILE_FEATURES} - ${CMAKE_C99_COMPILE_FEATURES} - ${CMAKE_C11_COMPILE_FEATURES} - ${CMAKE_C17_COMPILE_FEATURES} - ${CMAKE_C23_COMPILE_FEATURES} - ) - endif() - - set(CMAKE_C_COMPILE_FEATURES ${CMAKE_C_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_C90_COMPILE_FEATURES ${CMAKE_C90_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_C99_COMPILE_FEATURES ${CMAKE_C99_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_C11_COMPILE_FEATURES ${CMAKE_C11_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_C17_COMPILE_FEATURES ${CMAKE_C17_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_C23_COMPILE_FEATURES ${CMAKE_C23_COMPILE_FEATURES} PARENT_SCOPE) - - message(CHECK_PASS "done") - - elseif("x${lang}" STREQUAL "xCXX" AND COMMAND cmake_record_cxx_compile_features) - message(CHECK_START "Detecting ${lang} compile features") - - 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) - - include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") - - cmake_record_cxx_compile_features() - - if(NOT _result EQUAL 0) - message(CHECK_FAIL "failed") - return() - endif() - - if (CMAKE_CXX23_COMPILE_FEATURES AND CMAKE_CXX26_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES}) - endif() - if (CMAKE_CXX20_COMPILE_FEATURES AND CMAKE_CXX23_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES}) - endif() - 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() - - 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") - - elseif("x${lang}" STREQUAL "xCUDA" AND COMMAND cmake_record_cuda_compile_features) - message(CHECK_START "Detecting ${lang} compile features") - - set(CMAKE_CUDA03_COMPILE_FEATURES) - set(CMAKE_CUDA11_COMPILE_FEATURES) - set(CMAKE_CUDA14_COMPILE_FEATURES) - set(CMAKE_CUDA17_COMPILE_FEATURES) - set(CMAKE_CUDA20_COMPILE_FEATURES) - set(CMAKE_CUDA23_COMPILE_FEATURES) - set(CMAKE_CUDA26_COMPILE_FEATURES) - - include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") - - cmake_record_cuda_compile_features() - - if(NOT _result EQUAL 0) - message(CHECK_FAIL "failed") - return() - endif() - - if (CMAKE_CUDA23_COMPILE_FEATURES AND CMAKE_CUDA26_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CUDA26_COMPILE_FEATURES ${CMAKE_CUDA23_COMPILE_FEATURES}) - endif() - if (CMAKE_CUDA20_COMPILE_FEATURES AND CMAKE_CUDA23_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CUDA23_COMPILE_FEATURES ${CMAKE_CUDA20_COMPILE_FEATURES}) - endif() - if (CMAKE_CUDA17_COMPILE_FEATURES AND CMAKE_CUDA20_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CUDA20_COMPILE_FEATURES ${CMAKE_CUDA17_COMPILE_FEATURES}) - endif() - if (CMAKE_CUDA14_COMPILE_FEATURES AND CMAKE_CUDA17_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CUDA17_COMPILE_FEATURES ${CMAKE_CUDA14_COMPILE_FEATURES}) - endif() - if (CMAKE_CUDA11_COMPILE_FEATURES AND CMAKE_CUDA14_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CUDA14_COMPILE_FEATURES ${CMAKE_CUDA11_COMPILE_FEATURES}) - endif() - if (CMAKE_CUDA03_COMPILE_FEATURES AND CMAKE_CUDA11_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_CUDA11_COMPILE_FEATURES ${CMAKE_CUDA03_COMPILE_FEATURES}) - endif() - - if(NOT CMAKE_CUDA_COMPILE_FEATURES) - set(CMAKE_CUDA_COMPILE_FEATURES - ${CMAKE_CUDA03_COMPILE_FEATURES} - ${CMAKE_CUDA11_COMPILE_FEATURES} - ${CMAKE_CUDA14_COMPILE_FEATURES} - ${CMAKE_CUDA17_COMPILE_FEATURES} - ${CMAKE_CUDA20_COMPILE_FEATURES} - ${CMAKE_CUDA23_COMPILE_FEATURES} - ${CMAKE_CUDA26_COMPILE_FEATURES} - ) - endif() - - set(CMAKE_CUDA_COMPILE_FEATURES ${CMAKE_CUDA_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA03_COMPILE_FEATURES ${CMAKE_CUDA03_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA11_COMPILE_FEATURES ${CMAKE_CUDA11_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA14_COMPILE_FEATURES ${CMAKE_CUDA14_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA17_COMPILE_FEATURES ${CMAKE_CUDA17_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA20_COMPILE_FEATURES ${CMAKE_CUDA20_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA23_COMPILE_FEATURES ${CMAKE_CUDA23_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_CUDA26_COMPILE_FEATURES ${CMAKE_CUDA26_COMPILE_FEATURES} PARENT_SCOPE) - - message(CHECK_PASS "done") - - elseif(lang STREQUAL HIP AND COMMAND cmake_record_hip_compile_features) - message(CHECK_START "Detecting ${lang} compile features") - - set(CMAKE_HIP98_COMPILE_FEATURES) - set(CMAKE_HIP11_COMPILE_FEATURES) - set(CMAKE_HIP14_COMPILE_FEATURES) - set(CMAKE_HIP17_COMPILE_FEATURES) - set(CMAKE_HIP20_COMPILE_FEATURES) - set(CMAKE_HIP23_COMPILE_FEATURES) - set(CMAKE_HIP26_COMPILE_FEATURES) - - - include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") - - cmake_record_hip_compile_features() - - if(NOT _result EQUAL 0) - message(CHECK_FAIL "failed") - return() - endif() - - if (CMAKE_HIP23_COMPILE_FEATURES AND CMAKE_HIP26_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_HIP26_COMPILE_FEATURES ${CMAKE_HIP23_COMPILE_FEATURES}) - endif() - if (CMAKE_HIP20_COMPILE_FEATURES AND CMAKE_HIP23_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_HIP23_COMPILE_FEATURES ${CMAKE_HIP20_COMPILE_FEATURES}) - endif() - if (CMAKE_HIP17_COMPILE_FEATURES AND CMAKE_HIP20_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_HIP20_COMPILE_FEATURES ${CMAKE_HIP17_COMPILE_FEATURES}) - endif() - if (CMAKE_HIP14_COMPILE_FEATURES AND CMAKE_HIP17_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_HIP17_COMPILE_FEATURES ${CMAKE_HIP14_COMPILE_FEATURES}) - endif() - if (CMAKE_HIP11_COMPILE_FEATURES AND CMAKE_HIP14_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_HIP14_COMPILE_FEATURES ${CMAKE_HIP11_COMPILE_FEATURES}) - endif() - if (CMAKE_HIP98_COMPILE_FEATURES AND CMAKE_HIP11_COMPILE_FEATURES) - list(REMOVE_ITEM CMAKE_HIP11_COMPILE_FEATURES ${CMAKE_HIP98_COMPILE_FEATURES}) - endif() - - if(NOT CMAKE_HIP_COMPILE_FEATURES) - set(CMAKE_HIP_COMPILE_FEATURES - ${CMAKE_HIP98_COMPILE_FEATURES} - ${CMAKE_HIP11_COMPILE_FEATURES} - ${CMAKE_HIP14_COMPILE_FEATURES} - ${CMAKE_HIP17_COMPILE_FEATURES} - ${CMAKE_HIP20_COMPILE_FEATURES} - ${CMAKE_HIP23_COMPILE_FEATURES} - ${CMAKE_HIP26_COMPILE_FEATURES} - ) - endif() - - set(CMAKE_HIP_COMPILE_FEATURES ${CMAKE_HIP_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP98_COMPILE_FEATURES ${CMAKE_HIP98_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP11_COMPILE_FEATURES ${CMAKE_HIP11_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP14_COMPILE_FEATURES ${CMAKE_HIP14_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP17_COMPILE_FEATURES ${CMAKE_HIP17_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP20_COMPILE_FEATURES ${CMAKE_HIP20_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP23_COMPILE_FEATURES ${CMAKE_HIP23_COMPILE_FEATURES} PARENT_SCOPE) - set(CMAKE_HIP26_COMPILE_FEATURES ${CMAKE_HIP26_COMPILE_FEATURES} PARENT_SCOPE) - - message(CHECK_PASS "done") - - endif() - -endfunction()
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index 87218d2..4a75e25 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake
@@ -53,7 +53,14 @@ __TestCompiler_setTryCompileTargetType() # Avoid failing ABI detection on warnings. - string(REGEX REPLACE "(^| )-Werror([= ][^-][^ ]*)?( |$)" " " CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}") + if(CMAKE_TRY_COMPILE_CONFIGURATION) + string(TOUPPER "${CMAKE_TRY_COMPILE_CONFIGURATION}" _tc_config) + else() + set(_tc_config "DEBUG") + endif() + foreach(v CMAKE_${lang}_FLAGS CMAKE_${lang}_FLAGS_${_tc_config}) + string(REGEX REPLACE "(^| )-Werror([= ][^-][^ ]*)?( |$)" " " ${v} "${${v}}") + endforeach() # Save the current LC_ALL, LC_MESSAGES, and LANG environment variables # and set them to "C" that way GCC's "search starts here" text is in @@ -92,7 +99,45 @@ # Load the resulting information strings. if(CMAKE_${lang}_ABI_COMPILED) message(CHECK_PASS "done") + if(CMAKE_HOST_APPLE AND CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES "\\$") + file(READ_MACHO "${BIN}" ARCHITECTURES archs CAPTURE_ERROR macho_error) # undocumented file() subcommand + if (NOT macho_error) + # sort and prune the list of found architectures + set(arch_list_sorted ${archs}) + list(SORT arch_list_sorted) + list(REMOVE_DUPLICATES arch_list_sorted) + # sort and prune the list of requested architectures + set(requested_arch_list ${CMAKE_OSX_ARCHITECTURES}) + list(SORT requested_arch_list) + list(REMOVE_DUPLICATES requested_arch_list) + message(CONFIGURE_LOG + "Effective list of requested architectures (possibly empty) : \"${requested_arch_list}\"\n" + "Effective list of architectures found in the ABI info binary: \"${arch_list_sorted}\"\n") + # If all generated architectures were known to READ_MACHO (i.e. libmacho): + # Compare requested and found: + # - if no architecture(s) were requested explicitly, just check if READ_MACHO returned + # an architecture for the ABI info binary. + # - otherwise, check if the requested and found lists are equal + if(arch_list_sorted MATCHES "unknown") + # Rare but not impossible: a host with a toolchain capable of generating binaries with + # architectures that the system libmacho is too old to know. Report the found archs as + # usual, warn about the unknowns and skip the comparison with CMAKE_OSX_ARCHITECTURES. + message(WARNING "The ${lang} compiler generates universal binaries with at least 1 architecture not known to the host") + elseif(requested_arch_list AND arch_list_sorted + AND NOT "${requested_arch_list}" STREQUAL "${arch_list_sorted}") + # inform the user of the mismatch but show the raw input and output lists + message(FATAL_ERROR + "The ${lang} compiler targets architectures:\n" + " \"${archs}\"\n" + "but CMAKE_OSX_ARCHITECTURES is\n" + " \"${CMAKE_OSX_ARCHITECTURES}\"\n") + endif() + endif() + endif() + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${BIN}" ABI_STRINGS LIMIT_COUNT 32 REGEX "INFO:[A-Za-z0-9_]+\\[[^]]*\\]") + cmake_policy(POP) set(ABI_SIZEOF_DPTR "NOTFOUND") set(ABI_BYTE_ORDER "NOTFOUND") set(ABI_NAME "NOTFOUND")
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 08de0f9..e58ae42 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -312,6 +312,29 @@ endif () endif () + set(CMAKE_${lang}_STANDARD_LIBRARY "") + if ("x${lang}" STREQUAL "xCXX" AND + EXISTS "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/${lang}-DetectStdlib.h" AND + "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xClang" AND + "x${CMAKE_${lang}_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU") + # See #20851 for a proper abstraction for this. + execute_process( + COMMAND "${CMAKE_${lang}_COMPILER}" + ${CMAKE_${lang}_COMPILER_ID_ARG1} + ${CMAKE_CXX_COMPILER_ID_FLAGS_LIST} + -E + -x c++-header + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/${lang}-DetectStdlib.h" + -o - # Write to stdout. + OUTPUT_VARIABLE _lang_stdlib_out + ERROR_VARIABLE _lang_stdlib_err + RESULT_VARIABLE _lang_stdlib_res + ERROR_STRIP_TRAILING_WHITESPACE) + if (_lang_stdlib_res EQUAL 0) + string(REGEX REPLACE ".*CMAKE-STDLIB-DETECT: (.+)\n.*" "\\1" "CMAKE_${lang}_STANDARD_LIBRARY" "${_lang_stdlib_out}") + endif () + endif () + # Display the final identification result. if(CMAKE_${lang}_COMPILER_ID) if(CMAKE_${lang}_COMPILER_VERSION) @@ -355,6 +378,7 @@ set(CMAKE_${lang}_COMPILER_PRODUCED_OUTPUT "${COMPILER_${lang}_PRODUCED_OUTPUT}" PARENT_SCOPE) set(CMAKE_${lang}_COMPILER_PRODUCED_FILES "${COMPILER_${lang}_PRODUCED_FILES}" PARENT_SCOPE) set(CMAKE_${lang}_COMPILER_CLANG_RESOURCE_DIR "${CMAKE_${lang}_COMPILER_CLANG_RESOURCE_DIR}" PARENT_SCOPE) + set(CMAKE_${lang}_STANDARD_LIBRARY "${CMAKE_${lang}_STANDARD_LIBRARY}" PARENT_SCOPE) endfunction() include(CMakeCompilerIdDetection) @@ -919,9 +943,12 @@ set(SIMULATE_VERSION) set(CMAKE_${lang}_COMPILER_ID_STRING_REGEX ".?I.?N.?F.?O.?:.?[A-Za-z0-9_]+\\[[^]]*\\]") foreach(encoding "" "ENCODING;UTF-16LE" "ENCODING;UTF-16BE") + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${file}" CMAKE_${lang}_COMPILER_ID_STRINGS LIMIT_COUNT 38 ${encoding} REGEX "${CMAKE_${lang}_COMPILER_ID_STRING_REGEX}") + cmake_policy(POP) if(NOT CMAKE_${lang}_COMPILER_ID_STRINGS STREQUAL "") break() endif()
diff --git a/Modules/CMakeDetermineCompilerSupport.cmake b/Modules/CMakeDetermineCompilerSupport.cmake new file mode 100644 index 0000000..d561e7b --- /dev/null +++ b/Modules/CMakeDetermineCompilerSupport.cmake
@@ -0,0 +1,260 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +function(cmake_determine_compiler_support lang) + + if("x${lang}" STREQUAL "xC" AND COMMAND cmake_record_c_compile_features) + message(CHECK_START "Detecting ${lang} compile features") + + set(CMAKE_C90_COMPILE_FEATURES) + set(CMAKE_C99_COMPILE_FEATURES) + set(CMAKE_C11_COMPILE_FEATURES) + set(CMAKE_C17_COMPILE_FEATURES) + set(CMAKE_C23_COMPILE_FEATURES) + + include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") + + cmake_record_c_compile_features() + + if(NOT _result EQUAL 0) + message(CHECK_FAIL "failed") + return() + endif() + + if (CMAKE_C17_COMPILE_FEATURES AND CMAKE_C23_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_C23_COMPILE_FEATURES ${CMAKE_C17_COMPILE_FEATURES}) + endif() + if (CMAKE_C11_COMPILE_FEATURES AND CMAKE_C17_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_C17_COMPILE_FEATURES ${CMAKE_C11_COMPILE_FEATURES}) + endif() + if (CMAKE_C99_COMPILE_FEATURES AND CMAKE_C11_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_C11_COMPILE_FEATURES ${CMAKE_C99_COMPILE_FEATURES}) + endif() + if (CMAKE_C90_COMPILE_FEATURES AND CMAKE_C99_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_C99_COMPILE_FEATURES ${CMAKE_C90_COMPILE_FEATURES}) + endif() + + if(NOT CMAKE_C_COMPILE_FEATURES) + set(CMAKE_C_COMPILE_FEATURES + ${CMAKE_C90_COMPILE_FEATURES} + ${CMAKE_C99_COMPILE_FEATURES} + ${CMAKE_C11_COMPILE_FEATURES} + ${CMAKE_C17_COMPILE_FEATURES} + ${CMAKE_C23_COMPILE_FEATURES} + ) + endif() + + set(CMAKE_C_COMPILE_FEATURES ${CMAKE_C_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_C90_COMPILE_FEATURES ${CMAKE_C90_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_C99_COMPILE_FEATURES ${CMAKE_C99_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_C11_COMPILE_FEATURES ${CMAKE_C11_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_C17_COMPILE_FEATURES ${CMAKE_C17_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_C23_COMPILE_FEATURES ${CMAKE_C23_COMPILE_FEATURES} PARENT_SCOPE) + + message(CHECK_PASS "done") + + elseif("x${lang}" STREQUAL "xCXX" AND COMMAND cmake_record_cxx_compile_features) + message(CHECK_START "Detecting ${lang} compile features") + + 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) + + include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") + + cmake_record_cxx_compile_features() + + if(NOT _result EQUAL 0) + message(CHECK_FAIL "failed") + return() + endif() + + if (CMAKE_CXX23_COMPILE_FEATURES AND CMAKE_CXX26_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES}) + endif() + if (CMAKE_CXX20_COMPILE_FEATURES AND CMAKE_CXX23_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES}) + endif() + 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() + + # Create targets for use with `import std;` here. + set(CMAKE_CXX_IMPORT_STD "") + foreach (_cmake_import_std_version IN ITEMS 23 26) + if (CMAKE_CXX${_cmake_import_std_version}_COMPILE_FEATURES) + set(_cmake_cxx_import_std "") + cmake_create_cxx_import_std("${_cmake_import_std_version}" _cmake_cxx_import_std) + if (_cmake_cxx_import_std) + string(APPEND CMAKE_CXX_IMPORT_STD "### Imported target for C++${_cmake_import_std_version} standard library\n") + string(APPEND CMAKE_CXX_IMPORT_STD "${_cmake_cxx_import_std}\n\n") + endif () + endif () + endforeach () + + 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) + set(CMAKE_CXX_IMPORT_STD ${CMAKE_CXX_IMPORT_STD} PARENT_SCOPE) + + message(CHECK_PASS "done") + + elseif("x${lang}" STREQUAL "xCUDA" AND COMMAND cmake_record_cuda_compile_features) + message(CHECK_START "Detecting ${lang} compile features") + + set(CMAKE_CUDA03_COMPILE_FEATURES) + set(CMAKE_CUDA11_COMPILE_FEATURES) + set(CMAKE_CUDA14_COMPILE_FEATURES) + set(CMAKE_CUDA17_COMPILE_FEATURES) + set(CMAKE_CUDA20_COMPILE_FEATURES) + set(CMAKE_CUDA23_COMPILE_FEATURES) + set(CMAKE_CUDA26_COMPILE_FEATURES) + + include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") + + cmake_record_cuda_compile_features() + + if(NOT _result EQUAL 0) + message(CHECK_FAIL "failed") + return() + endif() + + if (CMAKE_CUDA23_COMPILE_FEATURES AND CMAKE_CUDA26_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CUDA26_COMPILE_FEATURES ${CMAKE_CUDA23_COMPILE_FEATURES}) + endif() + if (CMAKE_CUDA20_COMPILE_FEATURES AND CMAKE_CUDA23_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CUDA23_COMPILE_FEATURES ${CMAKE_CUDA20_COMPILE_FEATURES}) + endif() + if (CMAKE_CUDA17_COMPILE_FEATURES AND CMAKE_CUDA20_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CUDA20_COMPILE_FEATURES ${CMAKE_CUDA17_COMPILE_FEATURES}) + endif() + if (CMAKE_CUDA14_COMPILE_FEATURES AND CMAKE_CUDA17_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CUDA17_COMPILE_FEATURES ${CMAKE_CUDA14_COMPILE_FEATURES}) + endif() + if (CMAKE_CUDA11_COMPILE_FEATURES AND CMAKE_CUDA14_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CUDA14_COMPILE_FEATURES ${CMAKE_CUDA11_COMPILE_FEATURES}) + endif() + if (CMAKE_CUDA03_COMPILE_FEATURES AND CMAKE_CUDA11_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_CUDA11_COMPILE_FEATURES ${CMAKE_CUDA03_COMPILE_FEATURES}) + endif() + + if(NOT CMAKE_CUDA_COMPILE_FEATURES) + set(CMAKE_CUDA_COMPILE_FEATURES + ${CMAKE_CUDA03_COMPILE_FEATURES} + ${CMAKE_CUDA11_COMPILE_FEATURES} + ${CMAKE_CUDA14_COMPILE_FEATURES} + ${CMAKE_CUDA17_COMPILE_FEATURES} + ${CMAKE_CUDA20_COMPILE_FEATURES} + ${CMAKE_CUDA23_COMPILE_FEATURES} + ${CMAKE_CUDA26_COMPILE_FEATURES} + ) + endif() + + set(CMAKE_CUDA_COMPILE_FEATURES ${CMAKE_CUDA_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA03_COMPILE_FEATURES ${CMAKE_CUDA03_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA11_COMPILE_FEATURES ${CMAKE_CUDA11_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA14_COMPILE_FEATURES ${CMAKE_CUDA14_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA17_COMPILE_FEATURES ${CMAKE_CUDA17_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA20_COMPILE_FEATURES ${CMAKE_CUDA20_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA23_COMPILE_FEATURES ${CMAKE_CUDA23_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_CUDA26_COMPILE_FEATURES ${CMAKE_CUDA26_COMPILE_FEATURES} PARENT_SCOPE) + + message(CHECK_PASS "done") + + elseif(lang STREQUAL HIP AND COMMAND cmake_record_hip_compile_features) + message(CHECK_START "Detecting ${lang} compile features") + + set(CMAKE_HIP98_COMPILE_FEATURES) + set(CMAKE_HIP11_COMPILE_FEATURES) + set(CMAKE_HIP14_COMPILE_FEATURES) + set(CMAKE_HIP17_COMPILE_FEATURES) + set(CMAKE_HIP20_COMPILE_FEATURES) + set(CMAKE_HIP23_COMPILE_FEATURES) + set(CMAKE_HIP26_COMPILE_FEATURES) + + + include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake") + + cmake_record_hip_compile_features() + + if(NOT _result EQUAL 0) + message(CHECK_FAIL "failed") + return() + endif() + + if (CMAKE_HIP23_COMPILE_FEATURES AND CMAKE_HIP26_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_HIP26_COMPILE_FEATURES ${CMAKE_HIP23_COMPILE_FEATURES}) + endif() + if (CMAKE_HIP20_COMPILE_FEATURES AND CMAKE_HIP23_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_HIP23_COMPILE_FEATURES ${CMAKE_HIP20_COMPILE_FEATURES}) + endif() + if (CMAKE_HIP17_COMPILE_FEATURES AND CMAKE_HIP20_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_HIP20_COMPILE_FEATURES ${CMAKE_HIP17_COMPILE_FEATURES}) + endif() + if (CMAKE_HIP14_COMPILE_FEATURES AND CMAKE_HIP17_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_HIP17_COMPILE_FEATURES ${CMAKE_HIP14_COMPILE_FEATURES}) + endif() + if (CMAKE_HIP11_COMPILE_FEATURES AND CMAKE_HIP14_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_HIP14_COMPILE_FEATURES ${CMAKE_HIP11_COMPILE_FEATURES}) + endif() + if (CMAKE_HIP98_COMPILE_FEATURES AND CMAKE_HIP11_COMPILE_FEATURES) + list(REMOVE_ITEM CMAKE_HIP11_COMPILE_FEATURES ${CMAKE_HIP98_COMPILE_FEATURES}) + endif() + + if(NOT CMAKE_HIP_COMPILE_FEATURES) + set(CMAKE_HIP_COMPILE_FEATURES + ${CMAKE_HIP98_COMPILE_FEATURES} + ${CMAKE_HIP11_COMPILE_FEATURES} + ${CMAKE_HIP14_COMPILE_FEATURES} + ${CMAKE_HIP17_COMPILE_FEATURES} + ${CMAKE_HIP20_COMPILE_FEATURES} + ${CMAKE_HIP23_COMPILE_FEATURES} + ${CMAKE_HIP26_COMPILE_FEATURES} + ) + endif() + + set(CMAKE_HIP_COMPILE_FEATURES ${CMAKE_HIP_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP98_COMPILE_FEATURES ${CMAKE_HIP98_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP11_COMPILE_FEATURES ${CMAKE_HIP11_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP14_COMPILE_FEATURES ${CMAKE_HIP14_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP17_COMPILE_FEATURES ${CMAKE_HIP17_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP20_COMPILE_FEATURES ${CMAKE_HIP20_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP23_COMPILE_FEATURES ${CMAKE_HIP23_COMPILE_FEATURES} PARENT_SCOPE) + set(CMAKE_HIP26_COMPILE_FEATURES ${CMAKE_HIP26_COMPILE_FEATURES} PARENT_SCOPE) + + message(CHECK_PASS "done") + + endif() + +endfunction()
diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake index e364755..984a39d 100644 --- a/Modules/CMakeFortranInformation.cmake +++ b/Modules/CMakeFortranInformation.cmake
@@ -67,105 +67,6 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE_Fortran "${_override}") endif() -if(NOT CMAKE_Fortran_COMPILE_OPTIONS_PIC) - set(CMAKE_Fortran_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC}) -endif() - -if(NOT CMAKE_Fortran_COMPILE_OPTIONS_PIE) - set(CMAKE_Fortran_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE}) -endif() -if(NOT CMAKE_Fortran_LINK_OPTIONS_PIE) - set(CMAKE_Fortran_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE}) -endif() -if(NOT CMAKE_Fortran_LINK_OPTIONS_NO_PIE) - set(CMAKE_Fortran_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE}) -endif() - -if(NOT CMAKE_Fortran_COMPILE_OPTIONS_DLL) - set(CMAKE_Fortran_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL}) -endif() - -# Create a set of shared library variable specific to Fortran -# For 90% of the systems, these are the same flags as the C versions -# so if these are not set just copy the flags from the c version -if(NOT DEFINED CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS) - set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_Fortran_FLAGS) - set(CMAKE_SHARED_LIBRARY_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS) - set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG) - set(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP) - set(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG) - set(CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_EXE_EXPORTS_Fortran_FLAG) - set(CMAKE_EXE_EXPORTS_Fortran_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG) - set(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG}) -endif() - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_Fortran_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS}) -endif() - -# repeat for modules -if(NOT DEFINED CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS) - set(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_SHARED_MODULE_Fortran_FLAGS) - set(CMAKE_SHARED_MODULE_Fortran_FLAGS ${CMAKE_SHARED_MODULE_C_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG) - set(CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG}) -endif() - -if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP) - set(CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP}) -endif() - -if(NOT DEFINED CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG) - set(CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH) - set(CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) -endif() - -if(NOT CMAKE_INCLUDE_FLAG_Fortran) - set(CMAKE_INCLUDE_FLAG_Fortran ${CMAKE_INCLUDE_FLAG_C}) -endif() - -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - set(CMAKE_VERBOSE_MAKEFILE FALSE CACHE BOOL "If this value is on, makefiles will be generated without the .SILENT directive, and all commands will be echoed to the console during the make. This is useful for debugging only. With Visual Studio IDE projects all commands are done without /nologo.") set(CMAKE_Fortran_FLAGS_INIT "$ENV{FFLAGS} ${CMAKE_Fortran_FLAGS_INIT}") @@ -178,6 +79,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(Fortran) # now define the following rule variables # CMAKE_Fortran_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index 9d70e03..601ffaf 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in
@@ -5,6 +5,7 @@ set(CMAKE_HIP_COMPILER_VERSION "@CMAKE_HIP_COMPILER_VERSION@") set(CMAKE_HIP_STANDARD_COMPUTED_DEFAULT "@CMAKE_HIP_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_HIP_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_HIP_EXTENSIONS_COMPUTED_DEFAULT@") +set(CMAKE_HIP_STANDARD_LATEST "@CMAKE_HIP_STANDARD_LATEST@") set(CMAKE_HIP_COMPILE_FEATURES "@CMAKE_HIP_COMPILE_FEATURES@") set(CMAKE_HIP98_COMPILE_FEATURES "@CMAKE_HIP03_COMPILE_FEATURES@") set(CMAKE_HIP11_COMPILE_FEATURES "@CMAKE_HIP11_COMPILE_FEATURES@") @@ -12,6 +13,7 @@ set(CMAKE_HIP17_COMPILE_FEATURES "@CMAKE_HIP17_COMPILE_FEATURES@") set(CMAKE_HIP20_COMPILE_FEATURES "@CMAKE_HIP20_COMPILE_FEATURES@") set(CMAKE_HIP23_COMPILE_FEATURES "@CMAKE_HIP23_COMPILE_FEATURES@") +set(CMAKE_HIP26_COMPILE_FEATURES "@CMAKE_HIP26_COMPILE_FEATURES@") set(CMAKE_HIP_PLATFORM_ID "@CMAKE_HIP_PLATFORM_ID@") set(CMAKE_HIP_SIMULATE_ID "@CMAKE_HIP_SIMULATE_ID@")
diff --git a/Modules/CMakeHIPCompilerId.hip.in b/Modules/CMakeHIPCompilerId.hip.in index 4ac0f30..fa97667 100644 --- a/Modules/CMakeHIPCompilerId.hip.in +++ b/Modules/CMakeHIPCompilerId.hip.in
@@ -16,16 +16,27 @@ @CMAKE_HIP_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_HIP_COMPILER_ID_ERROR_FOR_TEST@ +#define CXX_STD_98 199711L +#define CXX_STD_11 201103L +#define CXX_STD_14 201402L +#define CXX_STD_17 201703L +#define CXX_STD_20 202002L +#define CXX_STD_23 202302L + +#define CXX_STD __cplusplus + const char* info_language_standard_default = "INFO" ":" "standard_default[" -#if __cplusplus > 202002L +#if CXX_STD > CXX_STD_23 + "26" +#elif CXX_STD > CXX_STD_20 "23" -#elif __cplusplus > 201703L +#elif CXX_STD > CXX_STD_17 "20" -#elif __cplusplus >= 201703L +#elif CXX_STD > CXX_STD_14 "17" -#elif __cplusplus >= 201402L +#elif CXX_STD > CXX_STD_11 "14" -#elif __cplusplus >= 201103L +#elif CXX_STD >= CXX_STD_11 "11" #else "98"
diff --git a/Modules/CMakeHIPInformation.cmake b/Modules/CMakeHIPInformation.cmake index 3995c36..dc76c63 100644 --- a/Modules/CMakeHIPInformation.cmake +++ b/Modules/CMakeHIPInformation.cmake
@@ -36,60 +36,6 @@ endif() -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG) - set(CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG_SEP) - set(CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_HIP_FLAG) - set(CMAKE_SHARED_LIBRARY_RPATH_LINK_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_EXE_EXPORTS_HIP_FLAG) - set(CMAKE_EXE_EXPORTS_HIP_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_HIP_FLAG) - set(CMAKE_SHARED_LIBRARY_SONAME_HIP_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG) - set(CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG_SEP) - set(CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG_SEP}) -endif() - -if(NOT CMAKE_EXECUTABLE_RPATH_LINK_HIP_FLAG) - set(CMAKE_EXECUTABLE_RPATH_LINK_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_HIP_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_HIP_WITH_RUNTIME_PATH) - set(CMAKE_SHARED_LIBRARY_LINK_HIP_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) -endif() - - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_HIP_FLAGS ${CMAKE_SHARED_LIBRARY_HIP_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_HIP_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_HIP_FLAGS}) -endif() - -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - # add the flags to the cache based # on the initial values computed in the platform/*.cmake files # use _INIT variables so that this only happens the first time @@ -110,6 +56,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(HIP) # now define the following rules: # CMAKE_HIP_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in index a8bb0d2..fb699fc 100644 --- a/Modules/CMakeOBJCCompiler.cmake.in +++ b/Modules/CMakeOBJCCompiler.cmake.in
@@ -6,6 +6,7 @@ set(CMAKE_OBJC_COMPILER_WRAPPER "@CMAKE_OBJC_COMPILER_WRAPPER@") set(CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_OBJC_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_OBJC_EXTENSIONS_COMPUTED_DEFAULT@") +set(CMAKE_OBJC_STANDARD_LATEST "@CMAKE_OBJC_STANDARD_LATEST@") set(CMAKE_OBJC_COMPILE_FEATURES "@CMAKE_OBJC_COMPILE_FEATURES@") set(CMAKE_OBJC90_COMPILE_FEATURES "@CMAKE_OBJC90_COMPILE_FEATURES@") set(CMAKE_OBJC99_COMPILE_FEATURES "@CMAKE_OBJC99_COMPILE_FEATURES@")
diff --git a/Modules/CMakeOBJCCompilerId.m.in b/Modules/CMakeOBJCCompilerId.m.in index 89bfe02..e88bb6f 100644 --- a/Modules/CMakeOBJCCompilerId.m.in +++ b/Modules/CMakeOBJCCompilerId.m.in
@@ -20,20 +20,28 @@ @CMAKE_OBJC_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_OBJC_COMPILER_ID_ERROR_FOR_TEST@ +#define C_STD_99 199901L +#define C_STD_11 201112L +#define C_STD_17 201710L +#define C_STD_23 202311L + +#ifdef __STDC_VERSION__ +# define C_STD __STDC_VERSION__ +#endif + #if !defined(__STDC__) -# if (defined(_MSC_VER) && !defined(__clang__)) \ - || (defined(__ibmxl__) || defined(__IBMC__)) +# if defined(__ibmxl__) || defined(__IBMC__) # define C_VERSION "90" # else # define C_VERSION # endif -#elif __STDC_VERSION__ > 201710L +#elif C_STD > C_STD_17 # define C_VERSION "23" -#elif __STDC_VERSION__ >= 201710L +#elif C_STD > C_STD_11 # define C_VERSION "17" -#elif __STDC_VERSION__ >= 201000L +#elif C_STD > C_STD_99 # define C_VERSION "11" -#elif __STDC_VERSION__ >= 199901L +#elif C_STD >= C_STD_99 # define C_VERSION "99" #else # define C_VERSION "90"
diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake index 8b8f10e..bcb8b11 100644 --- a/Modules/CMakeOBJCInformation.cmake +++ b/Modules/CMakeOBJCInformation.cmake
@@ -91,24 +91,6 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC "${_override}") endif() -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_OBJC_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_OBJC_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS}) -endif() - set(CMAKE_OBJC_FLAGS_INIT "$ENV{OBJCFLAGS} ${CMAKE_OBJC_FLAGS_INIT}") cmake_initialize_per_config_variable(CMAKE_OBJC_FLAGS "Flags used by the Objective-C compiler") @@ -130,6 +112,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(OBJC) # now define the following rule variables
diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in index 6a80d50..3bf12b1 100644 --- a/Modules/CMakeOBJCXXCompiler.cmake.in +++ b/Modules/CMakeOBJCXXCompiler.cmake.in
@@ -6,6 +6,7 @@ set(CMAKE_OBJCXX_COMPILER_WRAPPER "@CMAKE_OBJCXX_COMPILER_WRAPPER@") set(CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_OBJCXX_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_OBJCXX_EXTENSIONS_COMPUTED_DEFAULT@") +set(CMAKE_OBJCXX_STANDARD_LATEST "@CMAKE_OBJCXX_STANDARD_LATEST@") set(CMAKE_OBJCXX_COMPILE_FEATURES "@CMAKE_OBJCXX_COMPILE_FEATURES@") set(CMAKE_OBJCXX98_COMPILE_FEATURES "@CMAKE_OBJCXX98_COMPILE_FEATURES@") set(CMAKE_OBJCXX11_COMPILE_FEATURES "@CMAKE_OBJCXX11_COMPILE_FEATURES@")
diff --git a/Modules/CMakeOBJCXXCompilerId.mm.in b/Modules/CMakeOBJCXXCompilerId.mm.in index 2145b40..cadbaff 100644 --- a/Modules/CMakeOBJCXXCompilerId.mm.in +++ b/Modules/CMakeOBJCXXCompilerId.mm.in
@@ -23,22 +23,27 @@ @CMAKE_OBJCXX_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_OBJCXX_COMPILER_ID_ERROR_FOR_TEST@ -#if defined(_MSC_VER) && defined(_MSVC_LANG) -#define CXX_STD _MSVC_LANG -#else +#define CXX_STD_98 199711L +#define CXX_STD_11 201103L +#define CXX_STD_14 201402L +#define CXX_STD_17 201703L +#define CXX_STD_20 202002L +#define CXX_STD_23 202302L + #define CXX_STD __cplusplus -#endif const char* info_language_standard_default = "INFO" ":" "standard_default[" -#if CXX_STD > 202002L +#if CXX_STD > CXX_STD_23 + "26" +#elif CXX_STD > CXX_STD_20 "23" -#elfif CXX_STD > 201703L +#elif CXX_STD > CXX_STD_17 "20" -#elif CXX_STD >= 201703L +#elif CXX_STD > CXX_STD_14 "17" -#elif CXX_STD >= 201402L +#elif CXX_STD > CXX_STD_11 "14" -#elif CXX_STD >= 201103L +#elif CXX_STD >= CXX_STD_11 "11" #else "98"
diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake index da1d6c6..4fcd469 100644 --- a/Modules/CMakeOBJCXXInformation.cmake +++ b/Modules/CMakeOBJCXXInformation.cmake
@@ -87,117 +87,6 @@ endif() -# Create a set of shared library variable specific to Objective-C++ -# For 90% of the systems, these are the same flags as the Objective-C versions -# so if these are not set just copy the flags from the Objective-C version -if(NOT CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS) - set(CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS}) -endif() - -if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIC) - set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIC ${CMAKE_OBJC_COMPILE_OPTIONS_PIC}) -endif() - -if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIE) - set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIE ${CMAKE_OBJC_COMPILE_OPTIONS_PIE}) -endif() -if(NOT CMAKE_OBJCXX_LINK_OPTIONS_PIE) - set(CMAKE_OBJCXX_LINK_OPTIONS_PIE ${CMAKE_OBJC_LINK_OPTIONS_PIE}) -endif() -if(NOT CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE) - set(CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE ${CMAKE_OBJC_LINK_OPTIONS_NO_PIE}) -endif() - -if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_DLL) - set(CMAKE_OBJCXX_COMPILE_OPTIONS_DLL ${CMAKE_OBJC_COMPILE_OPTIONS_DLL}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS) - set(CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS) - set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_OBJC_FLAGS}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG) - set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP) - set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG_SEP}) -endif() - -if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG) - set(CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG}) -endif() - -if(NOT DEFINED CMAKE_EXE_EXPORTS_OBJCXX_FLAG) - set(CMAKE_EXE_EXPORTS_OBJCXX_FLAG ${CMAKE_EXE_EXPORTS_OBJC_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG) - set(CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_OBJC_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG) - set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG}) -endif() - -if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP) - set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP}) -endif() - -if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG) - set(CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG}) -endif() - -if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH) - set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_OBJC_WITH_RUNTIME_PATH}) -endif() - -if(NOT CMAKE_INCLUDE_FLAG_OBJCXX) - set(CMAKE_INCLUDE_FLAG_OBJCXX ${CMAKE_INCLUDE_FLAG_C}) -endif() - -# for most systems a module is the same as a shared library -# so unless the variable CMAKE_MODULE_EXISTS is set just -# copy the values from the LIBRARY variables -if(NOT CMAKE_MODULE_EXISTS) - set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS}) - set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS}) -endif() - -# repeat for modules -if(NOT CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS) - set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS}) -endif() - -if(NOT CMAKE_SHARED_MODULE_OBJCXX_FLAGS) - set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_OBJC_FLAGS}) -endif() - -# Initialize OBJCXX link type selection flags from OBJC versions. -foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE) - if(NOT CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS) - set(CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS - ${CMAKE_${type}_LINK_STATIC_OBJC_FLAGS}) - endif() - if(NOT CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS) - set(CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS - ${CMAKE_${type}_LINK_DYNAMIC_OBJC_FLAGS}) - endif() -endforeach() - -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_OBJCXX_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_OBJCXX_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - # add the flags to the cache based # on the initial values computed in the platform/*.cmake files # use _INIT variables so that this only happens the first time @@ -223,6 +112,7 @@ endif() include(CMakeCommonLanguageInclude) +_cmake_common_language_platform_flags(OBJCXX) # now define the following rules: # CMAKE_OBJCXX_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakePackageConfigHelpers.cmake b/Modules/CMakePackageConfigHelpers.cmake index 9fa30b6..7d038d4 100644 --- a/Modules/CMakePackageConfigHelpers.cmake +++ b/Modules/CMakePackageConfigHelpers.cmake
@@ -76,6 +76,23 @@ relative and also for absolute locations. For absolute locations, it works only if the absolute location is a subdirectory of ``INSTALL_PREFIX``. +.. versionadded:: 3.30 + The variable ``PACKAGE_PREFIX_DIR`` will always be defined after the + ``@PACKAGE_INIT@`` line. It will hold the value of the base install + location. In general, variables defined via the ``PATH_VARS`` mechanism + should be used instead, but ``PACKAGE_PREFIX_DIR`` can be used for those + cases not easily handled by ``PATH_VARS``, such as for files installed + directly to the base install location rather than a subdirectory of it. + + .. note:: + When consumers of the generated file use CMake 3.29 or older, the value + of ``PACKAGE_PREFIX_DIR`` can be changed by a call to + :command:`find_dependency` or :command:`find_package`. + If a project relies on ``PACKAGE_PREFIX_DIR``, it is the project's + responsibility to ensure that the value of ``PACKAGE_PREFIX_DIR`` is + preserved across any such calls, or any other calls which might include + another file generated by ``configure_package_config_file()``. + .. versionadded:: 3.1 If the ``INSTALL_PREFIX`` argument is passed, this is used as the base path to calculate all the relative paths. The ``<path>`` argument must be an absolute
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index d891de5..869a508 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake
@@ -57,6 +57,8 @@ set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ") set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @) 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_LINKER_PREFERENCE 50) set(CMAKE_Swift_LINKER_PREFERENCE_PROPAGATES 1) @@ -136,7 +138,7 @@ endif() if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) - set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) + set(CMAKE_Swift_CREATE_SHARED_MODULE "${CMAKE_Swift_CREATE_SHARED_LIBRARY} <CMAKE_SHARED_MODULE_CREATE_Swift_FLAGS>") endif() if(NOT CMAKE_Swift_LINK_EXECUTABLE) @@ -160,7 +162,7 @@ endif() if(NOT CMAKE_Swift_CREATE_SHARED_MODULE) - set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY}) + set(CMAKE_Swift_CREATE_SHARED_MODULE "${CMAKE_Swift_CREATE_SHARED_LIBRARY} <CMAKE_SHARED_MODULE_CREATE_Swift_FLAGS>") endif() if(NOT CMAKE_Swift_LINK_EXECUTABLE)
diff --git a/Modules/CMakeTestCCompiler.cmake b/Modules/CMakeTestCCompiler.cmake index 58726db..8ebe570 100644 --- a/Modules/CMakeTestCCompiler.cmake +++ b/Modules/CMakeTestCCompiler.cmake
@@ -73,8 +73,8 @@ endif() # Try to identify the compiler features -include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) -CMAKE_DETERMINE_COMPILE_FEATURES(C) +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerSupport.cmake) +CMAKE_DETERMINE_COMPILER_SUPPORT(C) # Re-configure to save learned information. configure_file(
diff --git a/Modules/CMakeTestCUDACompiler.cmake b/Modules/CMakeTestCUDACompiler.cmake index 3057fe9..ef97f55 100644 --- a/Modules/CMakeTestCUDACompiler.cmake +++ b/Modules/CMakeTestCUDACompiler.cmake
@@ -65,8 +65,8 @@ endif() # Try to identify the compiler features -include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) -CMAKE_DETERMINE_COMPILE_FEATURES(CUDA) +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerSupport.cmake) +CMAKE_DETERMINE_COMPILER_SUPPORT(CUDA) if("x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC") set(CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES "${CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES}")
diff --git a/Modules/CMakeTestCXXCompiler.cmake b/Modules/CMakeTestCXXCompiler.cmake index a9fbb26..6f5b36c 100644 --- a/Modules/CMakeTestCXXCompiler.cmake +++ b/Modules/CMakeTestCXXCompiler.cmake
@@ -79,8 +79,8 @@ endif() # Try to identify the compiler features -include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) -CMAKE_DETERMINE_COMPILE_FEATURES(CXX) +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerSupport.cmake) +CMAKE_DETERMINE_COMPILER_SUPPORT(CXX) # Re-configure to save learned information. configure_file(
diff --git a/Modules/CMakeTestHIPCompiler.cmake b/Modules/CMakeTestHIPCompiler.cmake index ec54d80..5b49c0d 100644 --- a/Modules/CMakeTestHIPCompiler.cmake +++ b/Modules/CMakeTestHIPCompiler.cmake
@@ -83,8 +83,8 @@ # Try to identify the compiler features -include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) -CMAKE_DETERMINE_COMPILE_FEATURES(HIP) +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerSupport.cmake) +CMAKE_DETERMINE_COMPILER_SUPPORT(HIP) if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") include(Internal/CMakeNVCCFilterImplicitInfo)
diff --git a/Modules/CMakeTestOBJCCompiler.cmake b/Modules/CMakeTestOBJCCompiler.cmake index a36180b..bcb3e1e 100644 --- a/Modules/CMakeTestOBJCCompiler.cmake +++ b/Modules/CMakeTestOBJCCompiler.cmake
@@ -70,8 +70,8 @@ endif() # Try to identify the compiler features -include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) -CMAKE_DETERMINE_COMPILE_FEATURES(OBJC) +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerSupport.cmake) +CMAKE_DETERMINE_COMPILER_SUPPORT(OBJC) # Re-configure to save learned information. configure_file(
diff --git a/Modules/CMakeTestOBJCXXCompiler.cmake b/Modules/CMakeTestOBJCXXCompiler.cmake index f7935c7..68b3871 100644 --- a/Modules/CMakeTestOBJCXXCompiler.cmake +++ b/Modules/CMakeTestOBJCXXCompiler.cmake
@@ -69,8 +69,8 @@ endif() # Try to identify the compiler features -include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) -CMAKE_DETERMINE_COMPILE_FEATURES(OBJCXX) +include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerSupport.cmake) +CMAKE_DETERMINE_COMPILER_SUPPORT(OBJCXX) # Re-configure to save learned information. configure_file(
diff --git a/Modules/CMakeTestSwiftCompiler.cmake b/Modules/CMakeTestSwiftCompiler.cmake index c7df912..d89e606 100644 --- a/Modules/CMakeTestSwiftCompiler.cmake +++ b/Modules/CMakeTestSwiftCompiler.cmake
@@ -24,7 +24,10 @@ # Clear result from normal variable. unset(CMAKE_Swift_COMPILER_WORKS) # Puts test result in cache variable. - set(__CMAKE_Swift_TEST_SOURCE "print(\"CMake\")\n") + string(CONCAT __CMAKE_Swift_TEST_SOURCE + "public struct CMakeStruct {" + " let x: Int" + "}") try_compile(CMAKE_Swift_COMPILER_WORKS SOURCE_FROM_VAR main.swift __CMAKE_Swift_TEST_SOURCE OUTPUT_VARIABLE __CMAKE_Swift_COMPILER_OUTPUT)
diff --git a/Modules/CMakeVerifyManifest.cmake b/Modules/CMakeVerifyManifest.cmake index 705ef8a..19827f1 100644 --- a/Modules/CMakeVerifyManifest.cmake +++ b/Modules/CMakeVerifyManifest.cmake
@@ -31,7 +31,10 @@ # it will put the list of versions found into the variable # specified by list_var function(crt_version file list_var) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${file}" strings REGEX "Microsoft.VC...CRT" NEWLINE_CONSUME) + cmake_policy(POP) foreach(s ${strings}) set(has_match 1) string(REGEX
diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake index 813ac3c..ed9a7a5 100644 --- a/Modules/CPack.cmake +++ b/Modules/CPack.cmake
@@ -478,27 +478,30 @@ .. versionadded:: 3.25 Specify the ``readelf`` executable path used by CPack. - The default value will be ``CMAKE_READELF`` when set. Otherwise, - the default value will be empty and CPack will use :command:`find_program` - to determine the ``readelf`` path when needed. + The default value will be taken from the ``CMAKE_READELF`` variable, if set, + which may be populated by an internal CMake module. If ``CMAKE_READELF`` + is not set, CPack will use :command:`find_program` to determine the + ``readelf`` path when needed. .. variable:: CPACK_OBJCOPY_EXECUTABLE .. versionadded:: 3.25 Specify the ``objcopy`` executable path used by CPack. - The default value will be ``CMAKE_OBJCOPY`` when set. Otherwise, - the default value will be empty and CPack will use :command:`find_program` - to determine the ``objcopy`` path when needed. + The default value will be taken from the ``CMAKE_OBJCOPY`` variable, if set, + which may be populated by an internal CMake module. If ``CMAKE_OBJCOPY`` + is not set, CPack will use :command:`find_program` to determine the + ``objcopy`` path when needed. .. variable:: CPACK_OBJDUMP_EXECUTABLE .. versionadded:: 3.25 Specify the ``objdump`` executable path used by CPack. - The default value will be ``CMAKE_OBJDUMP`` when set. Otherwise, - the default value will be empty and CPack will use :command:`find_program` - to determine the ``objdump`` path when needed. + The default value will be taken from the ``CMAKE_OBJDUMP`` variable, if set, + which may be populated by an internal CMake module. If ``CMAKE_OBJDUMP`` + is not set, CPack will use :command:`find_program` to determine the + ``objdump`` path when needed. #]=======================================================================]
diff --git a/Modules/CTestTargets.cmake b/Modules/CTestTargets.cmake index 99ef8e5..5e8a07d 100644 --- a/Modules/CTestTargets.cmake +++ b/Modules/CTestTargets.cmake
@@ -20,15 +20,31 @@ # Use CTest # configure files -if(CTEST_NEW_FORMAT) - configure_file( - ${CMAKE_ROOT}/Modules/DartConfiguration.tcl.in - ${PROJECT_BINARY_DIR}/CTestConfiguration.ini ) -else() - configure_file( - ${CMAKE_ROOT}/Modules/DartConfiguration.tcl.in - ${PROJECT_BINARY_DIR}/DartConfiguration.tcl ) -endif() +block() + if(NOT DEFINED CTEST_TLS_VERSION) + if(DEFINED CMAKE_TLS_VERSION) + set(CTEST_TLS_VERSION "${CMAKE_TLS_VERSION}") + elseif(DEFINED ENV{CMAKE_TLS_VERSION}) + set(CTEST_TLS_VERSION "$ENV{CMAKE_TLS_VERSION}") + endif() + endif() + if(NOT DEFINED CTEST_TLS_VERIFY) + if(DEFINED CMAKE_TLS_VERIFY) + set(CTEST_TLS_VERIFY "${CMAKE_TLS_VERIFY}") + elseif(DEFINED ENV{CMAKE_TLS_VERIFY}) + set(CTEST_TLS_VERIFY "$ENV{CMAKE_TLS_VERIFY}") + endif() + endif() + if(CTEST_NEW_FORMAT) + configure_file( + ${CMAKE_ROOT}/Modules/DartConfiguration.tcl.in + ${PROJECT_BINARY_DIR}/CTestConfiguration.ini ) + else() + configure_file( + ${CMAKE_ROOT}/Modules/DartConfiguration.tcl.in + ${PROJECT_BINARY_DIR}/DartConfiguration.tcl ) + endif() +endblock() # # Section 3:
diff --git a/Modules/CXX-DetectStdlib.h b/Modules/CXX-DetectStdlib.h new file mode 100644 index 0000000..0214dee --- /dev/null +++ b/Modules/CXX-DetectStdlib.h
@@ -0,0 +1,10 @@ +#include <version> +// clang-format off +#if defined(_LIBCPP_VERSION) +CMAKE-STDLIB-DETECT: libc++ +#elif defined(__GLIBCXX__) +CMAKE-STDLIB-DETECT: libstdc++ +#else +CMAKE-STDLIB-DETECT: UNKNOWN +#endif + // clang-format on
diff --git a/Modules/CheckForPthreads.c b/Modules/CheckForPthreads.c index e70ceb1..ea1f7ac 100644 --- a/Modules/CheckForPthreads.c +++ b/Modules/CheckForPthreads.c
@@ -1,6 +1,6 @@ #include <pthread.h> -void* start_routine(void* args) +static void* start_routine(void* args) { return args; }
diff --git a/Modules/CheckIPOSupported.cmake b/Modules/CheckIPOSupported.cmake index de682b7..fe27295 100644 --- a/Modules/CheckIPOSupported.cmake +++ b/Modules/CheckIPOSupported.cmake
@@ -257,11 +257,6 @@ endif() endforeach() - if(CMAKE_GENERATOR MATCHES "^Visual Studio 9 ") - _ipo_not_supported("CMake doesn't support IPO for current generator") - return() - endif() - foreach(x ${languages}) _ipo_run_language_check(${x}) endforeach()
diff --git a/Modules/CheckIPOSupported/foo.c b/Modules/CheckIPOSupported/foo.c index 1e56597..6a64a99 100644 --- a/Modules/CheckIPOSupported/foo.c +++ b/Modules/CheckIPOSupported/foo.c
@@ -1,4 +1,4 @@ -int foo() +int foo(void) { return 0x42; }
diff --git a/Modules/CheckIPOSupported/main.c b/Modules/CheckIPOSupported/main.c index 8d2ab01..db0956c 100644 --- a/Modules/CheckIPOSupported/main.c +++ b/Modules/CheckIPOSupported/main.c
@@ -1,4 +1,4 @@ -int foo(); +int foo(void); int main(void) {
diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake index bad3590..35ad036 100644 --- a/Modules/CheckLanguage.cmake +++ b/Modules/CheckLanguage.cmake
@@ -85,6 +85,7 @@ set(_cl_content "cmake_minimum_required(VERSION ${CMAKE_VERSION}) +set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") project(Check${lang} ${lang}) file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" \"set(CMAKE_${lang}_COMPILER \\\"\${CMAKE_${lang}_COMPILER}\\\")\\n\"
diff --git a/Modules/CheckLibraryExists.lists.in b/Modules/CheckLibraryExists.lists.in deleted file mode 100644 index 741b87d..0000000 --- a/Modules/CheckLibraryExists.lists.in +++ /dev/null
@@ -1,8 +0,0 @@ -PROJECT(CHECK_LIBRARY_EXISTS) - - -ADD_DEFINITIONS(-DCHECK_FUNCTION_EXISTS=${CHECK_LIBRARY_EXISTS_FUNCTION}) -LINK_DIRECTORIES(${CHECK_LIBRARY_EXISTS_LOCATION}) -ADD_EXECUTABLE(CheckLibraryExists ${CHECK_LIBRARY_EXISTS_SOURCE}) -TARGET_LINK_LIBRARIES(CheckLibraryExists ${CHECK_LIBRARY_EXISTS_LIBRARY}) -
diff --git a/Modules/CheckStructHasMember.cmake b/Modules/CheckStructHasMember.cmake index 72fd093..959f8e5 100644 --- a/Modules/CheckStructHasMember.cmake +++ b/Modules/CheckStructHasMember.cmake
@@ -66,7 +66,7 @@ set(_CHECK_STRUCT_MEMBER_SOURCE_CODE " ${_INCLUDE_FILES} -int main() +int main(void) { (void)sizeof(((${_STRUCT} *)0)->${_MEMBER}); return 0;
diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake index 931ed4a..621df75 100644 --- a/Modules/CheckSymbolExists.cmake +++ b/Modules/CheckSymbolExists.cmake
@@ -75,14 +75,28 @@ endmacro() macro(__CHECK_SYMBOL_EXISTS_FILTER_FLAGS LANG) - set(__CMAKE_${LANG}_FLAGS_SAVED "${CMAKE_${LANG}_FLAGS}") - string(REGEX REPLACE "(^| )-Werror([= ][^ ]*)?( |$)" " " CMAKE_${LANG}_FLAGS "${CMAKE_${LANG}_FLAGS}") - string(REGEX REPLACE "(^| )-pedantic-errors( |$)" " " CMAKE_${LANG}_FLAGS "${CMAKE_${LANG}_FLAGS}") + if(CMAKE_TRY_COMPILE_CONFIGURATION) + string(TOUPPER "${CMAKE_TRY_COMPILE_CONFIGURATION}" _tc_config) + else() + set(_tc_config "DEBUG") + endif() + foreach(v CMAKE_${LANG}_FLAGS CMAKE_${LANG}_FLAGS_${_tc_config}) + set(__${v}_SAVED "${${v}}") + string(REGEX REPLACE "(^| )-Werror([= ][^-][^ ]*)?( |$)" " " ${v} "${${v}}") + string(REGEX REPLACE "(^| )-pedantic-errors( |$)" " " ${v} "${${v}}") + endforeach() endmacro() macro(__CHECK_SYMBOL_EXISTS_RESTORE_FLAGS LANG) - set(CMAKE_${LANG}_FLAGS "${__CMAKE_${LANG}_FLAGS_SAVED}") - unset(__CMAKE_${LANG}_FLAGS_SAVED) + if(CMAKE_TRY_COMPILE_CONFIGURATION) + string(TOUPPER "${CMAKE_TRY_COMPILE_CONFIGURATION}" _tc_config) + else() + set(_tc_config "DEBUG") + endif() + foreach(v CMAKE_${LANG}_FLAGS CMAKE_${LANG}_FLAGS_${_tc_config}) + set(${v} "${__${v}_SAVED}") + unset(__${v}_SAVED) + endforeach() endmacro() macro(__CHECK_SYMBOL_EXISTS_IMPL SOURCEFILE SYMBOL FILES VARIABLE)
diff --git a/Modules/CheckTypeSize.cmake b/Modules/CheckTypeSize.cmake index 01ce1d2..849d691 100644 --- a/Modules/CheckTypeSize.cmake +++ b/Modules/CheckTypeSize.cmake
@@ -92,6 +92,7 @@ block(SCOPE_FOR POLICIES) cmake_policy(SET CMP0054 NEW) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> #----------------------------------------------------------------------------- # Helper function. DO NOT CALL DIRECTLY.
diff --git a/Modules/Compiler/ADSP-ASM.cmake b/Modules/Compiler/ADSP-ASM.cmake index 9ef5142..67e8d96 100644 --- a/Modules/Compiler/ADSP-ASM.cmake +++ b/Modules/Compiler/ADSP-ASM.cmake
@@ -1,6 +1,4 @@ include(Compiler/ADSP) __compiler_adsp(ASM) -set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS asm) -set(CMAKE_ASM_OUTPUT_EXTENSION ".o" ) -set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> <SOURCE>") +set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS S;s;asm)
diff --git a/Modules/Compiler/ADSP-C.cmake b/Modules/Compiler/ADSP-C.cmake index cef3fb1..1898401 100644 --- a/Modules/Compiler/ADSP-C.cmake +++ b/Modules/Compiler/ADSP-C.cmake
@@ -8,4 +8,6 @@ set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C_STANDARD_LATEST 99) + __compiler_check_default_language_standard(C 8.0.0.0 99)
diff --git a/Modules/Compiler/ADSP-CXX.cmake b/Modules/Compiler/ADSP-CXX.cmake index b01cab1..e82d9fb 100644 --- a/Modules/Compiler/ADSP-CXX.cmake +++ b/Modules/Compiler/ADSP-CXX.cmake
@@ -6,11 +6,13 @@ set(CMAKE_CXX98_STANDARD_COMPILE_OPTION -c++) set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -g++) set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_CXX_STANDARD_LATEST 98) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.3.0.0) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION -c++11) set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -c++11 -g++) set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 11) endif() __compiler_check_default_language_standard(CXX 8.0.0.0 98)
diff --git a/Modules/Compiler/ARMClang-C.cmake b/Modules/Compiler/ARMClang-C.cmake index 01c4cea..fce1a8e 100644 --- a/Modules/Compiler/ARMClang-C.cmake +++ b/Modules/Compiler/ARMClang-C.cmake
@@ -21,3 +21,13 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_C_STANDARD_LATEST 11) + +# Including the "${CMAKE_ROOT}/Modules/Compiler/Clang-C.cmake" script above may set several other compile option +# variables which do not necessarily apply here. So, we unset those variables accordingly. +unset(CMAKE_C17_STANDARD_COMPILE_OPTION) +unset(CMAKE_C17_EXTENSION_COMPILE_OPTION) + +unset(CMAKE_C23_STANDARD_COMPILE_OPTION) +unset(CMAKE_C23_EXTENSION_COMPILE_OPTION)
diff --git a/Modules/Compiler/AppleClang-C.cmake b/Modules/Compiler/AppleClang-C.cmake index d711b6e..41e7988 100644 --- a/Modules/Compiler/AppleClang-C.cmake +++ b/Modules/Compiler/AppleClang-C.cmake
@@ -26,16 +26,20 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_C_STANDARD_LATEST 11) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17") + set(CMAKE_C_STANDARD_LATEST 17) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0.3) set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x") set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x") + set(CMAKE_C_STANDARD_LATEST 23) endif() __compiler_check_default_language_standard(C 4.0 99 9.1 11 12.0.5 17)
diff --git a/Modules/Compiler/AppleClang-CXX.cmake b/Modules/Compiler/AppleClang-CXX.cmake index 7c97969..8a6c855 100644 --- a/Modules/Compiler/AppleClang-CXX.cmake +++ b/Modules/Compiler/AppleClang-CXX.cmake
@@ -22,25 +22,31 @@ set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") + + set(CMAKE_CXX_STANDARD_LATEST 11) endif() if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.1) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 14) elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1) # AppleClang 5.0 knows this flag, but does not set a __cplusplus macro greater than 201103L set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++1y") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 14) endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + set(CMAKE_CXX_STANDARD_LATEST 17) elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.1) set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++1z") set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") + set(CMAKE_CXX_STANDARD_LATEST 17) endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) @@ -50,14 +56,17 @@ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") + set(CMAKE_CXX_STANDARD_LATEST 20) elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") + set(CMAKE_CXX_STANDARD_LATEST 20) endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0) set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++2b") set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") + set(CMAKE_CXX_STANDARD_LATEST 23) endif() __compiler_check_default_language_standard(CXX 4.0 98)
diff --git a/Modules/Compiler/AppleClang-OBJC.cmake b/Modules/Compiler/AppleClang-OBJC.cmake index f40c396..aa02d4b 100644 --- a/Modules/Compiler/AppleClang-OBJC.cmake +++ b/Modules/Compiler/AppleClang-OBJC.cmake
@@ -21,6 +21,8 @@ set(CMAKE_OBJC11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_OBJC11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_OBJC11_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC_STANDARD_LATEST 11) endif() # AppleClang 10.0 was the oldest compiler available to test C17 support @@ -28,12 +30,16 @@ set(CMAKE_OBJC17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_OBJC17_EXTENSION_COMPILE_OPTION "-std=gnu17") set(CMAKE_OBJC17_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC_STANDARD_LATEST 17) endif() -if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 11.0) +if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 11.0.3) set(CMAKE_OBJC23_STANDARD_COMPILE_OPTION "-std=c2x") set(CMAKE_OBJC23_EXTENSION_COMPILE_OPTION "-std=gnu2x") set(CMAKE_OBJC23_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC_STANDARD_LATEST 23) endif() # AppleClang 10.0 was the oldest compiler available to test default C11 support
diff --git a/Modules/Compiler/AppleClang-OBJCXX.cmake b/Modules/Compiler/AppleClang-OBJCXX.cmake index 172a343..d2573e5 100644 --- a/Modules/Compiler/AppleClang-OBJCXX.cmake +++ b/Modules/Compiler/AppleClang-OBJCXX.cmake
@@ -19,25 +19,31 @@ set(CMAKE_OBJCXX11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_OBJCXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") + + set(CMAKE_OBJCXX_STANDARD_LATEST 11) endif() if(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 6.1) set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_OBJCXX_STANDARD_LATEST 14) elseif(NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 5.1) # AppleClang 5.0 knows this flag, but does not set a __cplusplus macro greater than 201103L set(CMAKE_OBJCXX14_STANDARD_COMPILE_OPTION "-std=c++1y") set(CMAKE_OBJCXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") set(CMAKE_OBJCXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_OBJCXX_STANDARD_LATEST 14) endif() if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 10.0) set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + set(CMAKE_OBJCXX_STANDARD_LATEST 17) elseif (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 6.1) set(CMAKE_OBJCXX17_STANDARD_COMPILE_OPTION "-std=c++1z") set(CMAKE_OBJCXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") + set(CMAKE_OBJCXX_STANDARD_LATEST 17) endif() if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 8.0) @@ -47,6 +53,7 @@ if (NOT CMAKE_OBJCXX_COMPILER_VERSION VERSION_LESS 10.0) set(CMAKE_OBJCXX20_STANDARD_COMPILE_OPTION "-std=c++2a") set(CMAKE_OBJCXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") + set(CMAKE_OBJCXX_STANDARD_LATEST 20) endif() __compiler_check_default_language_standard(OBJCXX 4.0 98)
diff --git a/Modules/Compiler/CMakeCommonCompilerMacros.cmake b/Modules/Compiler/CMakeCommonCompilerMacros.cmake index 2f220d4..ffd02ec 100644 --- a/Modules/Compiler/CMakeCommonCompilerMacros.cmake +++ b/Modules/Compiler/CMakeCommonCompilerMacros.cmake
@@ -105,6 +105,9 @@ # Define to allow compile features to be automatically determined macro(cmake_record_cxx_compile_features) set(_result 0) + if(_result EQUAL 0 AND DEFINED CMAKE_CXX26_STANDARD_COMPILE_OPTION) + _has_compiler_features_cxx(26) + endif() if(_result EQUAL 0 AND DEFINED CMAKE_CXX23_STANDARD_COMPILE_OPTION) _has_compiler_features_cxx(23) endif() @@ -142,6 +145,9 @@ macro(cmake_record_cuda_compile_features) set(_result 0) + if(_result EQUAL 0 AND DEFINED CMAKE_CUDA26_STANDARD_COMPILE_OPTION) + _has_compiler_features_cuda(26) + endif() if(_result EQUAL 0 AND DEFINED CMAKE_CUDA23_STANDARD_COMPILE_OPTION) _has_compiler_features_cuda(23) endif() @@ -179,6 +185,9 @@ macro(cmake_record_hip_compile_features) set(_result 0) + if(_result EQUAL 0 AND DEFINED CMAKE_HIP26_STANDARD_COMPILE_OPTION) + _has_compiler_features_hip(26) + endif() if(_result EQUAL 0 AND DEFINED CMAKE_HIP23_STANDARD_COMPILE_OPTION) _has_compiler_features_hip(23) endif() @@ -192,3 +201,67 @@ _has_compiler_features_hip(11) _has_compiler_features_hip(98) endmacro() + +function(cmake_create_cxx_import_std std variable) + set(_cmake_supported_import_std_features + # Compilers support `import std` in C++20 as an extension. Skip + # for now. + # 20 + 23 + 26) + list(FIND _cmake_supported_import_std_features "${std}" _cmake_supported_import_std_idx) + if (_cmake_supported_import_std_idx EQUAL "-1") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Unsupported C++ standard: C++${std}\")\n" + PARENT_SCOPE) + return () + endif () + # If the target exists, skip. A toolchain file may have provided it. + if (TARGET "__CMAKE::CXX${std}") + return () + endif () + # The generator must support imported C++ modules. + if (NOT CMAKE_GENERATOR MATCHES "Ninja") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Unsupported generator: ${CMAKE_GENERATOR}\")\n" + PARENT_SCOPE) + return () + endif () + # Check if the compiler understands how to `import std;`. + include("${CMAKE_ROOT}/Modules/Compiler/${CMAKE_CXX_COMPILER_ID}-CXX-CXXImportStd.cmake" OPTIONAL RESULT_VARIABLE _cmake_import_std_res) + if (NOT _cmake_import_std_res) + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Toolchain does not support discovering `import std` support\")\n" + PARENT_SCOPE) + return () + endif () + if (NOT COMMAND _cmake_cxx_import_std) + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Toolchain does not provide `import std` discovery command\")\n" + PARENT_SCOPE) + return () + endif () + + # Check the experimental flag. Check it here to avoid triggering warnings in + # situations that don't support the feature anyways. + set(_cmake_supported_import_std_experimental "") + cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "CxxImportStd" + _cmake_supported_import_std_experimental) + if (NOT _cmake_supported_import_std_experimental) + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled (usually a `project()` call)\")\n" + PARENT_SCOPE) + return () + endif () + + _cmake_cxx_import_std("${std}" target_definition) + string(CONCAT guarded_target_definition + "if (NOT TARGET \"__CMAKE::CXX${std}\")\n" + "${target_definition}" + "endif ()\n" + "if (TARGET \"__CMAKE::CXX${std}\")\n" + " list(APPEND CMAKE_CXX_COMPILER_IMPORT_STD \"${std}\")\n" + "endif ()\n") + set("${variable}" "${guarded_target_definition}" PARENT_SCOPE) +endfunction()
diff --git a/Modules/Compiler/Clang-C.cmake b/Modules/Compiler/Clang-C.cmake index 480a5de..c9b1669 100644 --- a/Modules/Compiler/Clang-C.cmake +++ b/Modules/Compiler/Clang-C.cmake
@@ -33,27 +33,34 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION "-std=c99") set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99") + + set(CMAKE_C_STANDARD_LATEST 99) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1) set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") + set(CMAKE_C_STANDARD_LATEST 11) elseif(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.0) set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c1x") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu1x") + set(CMAKE_C_STANDARD_LATEST 11) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17") + set(CMAKE_C_STANDARD_LATEST 17) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 18.0) set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c23") set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu23") + set(CMAKE_C_STANDARD_LATEST 23) elseif(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 9.0) set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x") set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x") + set(CMAKE_C_STANDARD_LATEST 23) endif() else() set(CMAKE_C90_STANDARD_COMPILE_OPTION "") @@ -74,6 +81,8 @@ set(CMAKE_C17_STANDARD_COMPILE_OPTION "") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "") endif() + + set(CMAKE_C_STANDARD_LATEST 17) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 2.1)
diff --git a/Modules/Compiler/Clang-CXX-CXXImportStd.cmake b/Modules/Compiler/Clang-CXX-CXXImportStd.cmake new file mode 100644 index 0000000..f58f17e --- /dev/null +++ b/Modules/Compiler/Clang-CXX-CXXImportStd.cmake
@@ -0,0 +1,171 @@ +function (_cmake_cxx_import_std std variable) + if (NOT CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Only `libc++` is supported\")\n" + PARENT_SCOPE) + return () + endif () + + execute_process( + COMMAND + "${CMAKE_CXX_COMPILER}" + ${CMAKE_CXX_COMPILER_ID_ARG1} + -print-file-name=libc++.modules.json + OUTPUT_VARIABLE _clang_libcxx_modules_json_file + ERROR_VARIABLE _clang_libcxx_modules_json_file_err + RESULT_VARIABLE _clang_libcxx_modules_json_file_res + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE) + if (_clang_libcxx_modules_json_file_res) + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Could not find `libc++.modules.json` resource\")\n" + PARENT_SCOPE) + return () + endif () + + # Without this file, we do not have modules installed. + if (NOT EXISTS "${_clang_libcxx_modules_json_file}") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libc++.modules.json` resource does not exist\")\n" + PARENT_SCOPE) + return () + endif () + + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "18.1.2") + # 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("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"LLVM 18.1.2 is required for `libc++.modules.json` format fix\")\n" + PARENT_SCOPE) + return () + endif () + + file(READ "${_clang_libcxx_modules_json_file}" _clang_libcxx_modules_json) + string(JSON _clang_modules_json_version GET "${_clang_libcxx_modules_json}" "version") + string(JSON _clang_modules_json_revision GET "${_clang_libcxx_modules_json}" "revision") + # Require version 1. + if (NOT _clang_modules_json_version EQUAL "1") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libc++.modules.json` version ${_clang_modules_json_version}.${_clang_modules_json_revision} is not recognized\")\n" + PARENT_SCOPE) + return () + endif () + + string(JSON _clang_modules_json_nmodules LENGTH "${_clang_libcxx_modules_json}" "modules") + # Don't declare the target without any modules. + if (NOT _clang_modules_json_nmodules) + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libc++.modules.json` does not list any available modules\")\n" + PARENT_SCOPE) + return () + endif () + + # Declare the target. + set(_clang_libcxx_target "") + # Clang 18 does not provide the module initializer for the `std` modules. + # Create a static library to hold these. Hope that Clang 19 can provide this, + # but never run the code. + string(APPEND _clang_libcxx_target + "add_library(__cmake_cxx${std} STATIC)\n") + string(APPEND _clang_libcxx_target + "target_sources(__cmake_cxx${std} INTERFACE \"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>:$<TARGET_OBJECTS:__cmake_cxx${std}>>\")\n") + string(APPEND _clang_libcxx_target + "set_property(TARGET __cmake_cxx${std} PROPERTY EXCLUDE_FROM_ALL 1)\n") + string(APPEND _clang_libcxx_target + "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_SCAN_FOR_MODULES 1)\n") + string(APPEND _clang_libcxx_target + "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_MODULE_STD 0)\n") + string(APPEND _clang_libcxx_target + "target_compile_features(__cmake_cxx${std} PUBLIC cxx_std_${std})\n") + + set(_clang_modules_is_stdlib 0) + set(_clang_modules_include_dirs_list "") + set(_clang_modules_module_paths "") + get_filename_component(_clang_modules_dir "${_clang_libcxx_modules_json_file}" DIRECTORY) + + # Add module sources. + math(EXPR _clang_modules_json_nmodules_range "${_clang_modules_json_nmodules} - 1") + foreach (_clang_modules_json_modules_idx RANGE 0 "${_clang_modules_json_nmodules_range}") + string(JSON _clang_modules_json_module GET "${_clang_libcxx_modules_json}" "modules" "${_clang_modules_json_modules_idx}") + + string(JSON _clang_modules_json_module_source GET "${_clang_modules_json_module}" "source-path") + string(JSON _clang_modules_json_module_is_stdlib GET "${_clang_modules_json_module}" "is-std-library") + string(JSON _clang_modules_json_module_local_arguments GET "${_clang_modules_json_module}" "local-arguments") + string(JSON _clang_modules_json_module_nsystem_include_directories LENGTH "${_clang_modules_json_module_local_arguments}" "system-include-directories") + + if (NOT IS_ABSOLUTE "${_clang_modules_json_module_source}") + string(PREPEND _clang_modules_json_module_source "${_clang_modules_dir}/") + endif () + list(APPEND _clang_modules_module_paths + "${_clang_modules_json_module_source}") + + if (_clang_modules_json_module_is_stdlib) + set(_clang_modules_is_stdlib 1) + endif () + + math(EXPR _clang_modules_json_module_nsystem_include_directories_range "${_clang_modules_json_module_nsystem_include_directories} - 1") + foreach (_clang_modules_json_modules_system_include_directories_idx RANGE 0 "${_clang_modules_json_module_nsystem_include_directories_range}") + string(JSON _clang_modules_json_module_system_include_directory GET "${_clang_modules_json_module_local_arguments}" "system-include-directories" "${_clang_modules_json_modules_system_include_directories_idx}") + + if (NOT IS_ABSOLUTE "${_clang_modules_json_module_system_include_directory}") + string(PREPEND _clang_modules_json_module_system_include_directory "${_clang_modules_dir}/") + endif () + list(APPEND _clang_modules_include_dirs_list + "${_clang_modules_json_module_system_include_directory}") + endforeach () + endforeach () + + # Split the paths into basedirs and module paths. + set(_clang_modules_base_dirs_list "") + set(_clang_modules_files "") + foreach (_clang_modules_module_path IN LISTS _clang_modules_module_paths) + get_filename_component(_clang_module_dir "${_clang_modules_module_path}" DIRECTORY) + + list(APPEND _clang_modules_base_dirs_list + "${_clang_module_dir}") + string(APPEND _clang_modules_files + " \"${_clang_modules_module_path}\"") + endforeach () + list(REMOVE_DUPLICATES _clang_modules_base_dirs_list) + set(_clang_modules_base_dirs "") + foreach (_clang_modules_base_dir IN LISTS _clang_modules_base_dirs_list) + string(APPEND _clang_modules_base_dirs + " \"${_clang_modules_base_dir}\"") + endforeach () + + # If we have a standard library module, suppress warnings about reserved + # module names. + if (_clang_modules_is_stdlib) + string(APPEND _clang_libcxx_target + "target_compile_options(__cmake_cxx${std} PRIVATE -Wno-reserved-module-identifier)\n") + endif () + + # Set up include directories. + list(REMOVE_DUPLICATES _clang_modules_include_dirs_list) + set(_clang_modules_include_dirs "") + foreach (_clang_modules_include_dir IN LISTS _clang_modules_include_dirs_list) + string(APPEND _clang_modules_include_dirs + " \"${_clang_modules_include_dir}\"") + endforeach () + string(APPEND _clang_libcxx_target + "target_include_directories(__cmake_cxx${std} PRIVATE ${_clang_modules_include_dirs})\n") + + # Create the file set for the modules. + string(APPEND _clang_libcxx_target + "target_sources(__cmake_cxx${std} + PUBLIC + FILE_SET std TYPE CXX_MODULES + BASE_DIRS ${_clang_modules_base_dirs} + FILES ${_clang_modules_files})\n") + + # Wrap the `__cmake_cxx${std}` target in a check. + string(PREPEND _clang_libcxx_target + "if (NOT TARGET \"__cmake_cxx${std}\")\n") + string(APPEND _clang_libcxx_target + "endif ()\n") + string(APPEND _clang_libcxx_target + "add_library(__CMAKE::CXX${std} ALIAS __cmake_cxx${std})\n") + + set("${variable}" "${_clang_libcxx_target}" PARENT_SCOPE) +endfunction ()
diff --git a/Modules/Compiler/Clang-OBJC.cmake b/Modules/Compiler/Clang-OBJC.cmake index 7a83b77..c374d71 100644 --- a/Modules/Compiler/Clang-OBJC.cmake +++ b/Modules/Compiler/Clang-OBJC.cmake
@@ -22,18 +22,24 @@ set(CMAKE_OBJC11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_OBJC11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_OBJC11_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC_STANDARD_LATEST 11) endif() if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 6.0) set(CMAKE_OBJC17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_OBJC17_EXTENSION_COMPILE_OPTION "-std=gnu17") set(CMAKE_OBJC17_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC_STANDARD_LATEST 17) endif() if(NOT CMAKE_OBJC_COMPILER_VERSION VERSION_LESS 9.0) set(CMAKE_OBJC23_STANDARD_COMPILE_OPTION "-std=c2x") set(CMAKE_OBJC23_EXTENSION_COMPILE_OPTION "-std=gnu2x") set(CMAKE_OBJC23_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_OBJC_STANDARD_LATEST 23) endif() __compiler_check_default_language_standard(OBJC 3.4 99 3.6 11)
diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake index e136e37..f09d36d 100644 --- a/Modules/Compiler/Clang.cmake +++ b/Modules/Compiler/Clang.cmake
@@ -135,6 +135,7 @@ if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 2.1) set(CMAKE_${lang}98_STANDARD_COMPILE_OPTION "-std=c++98") set(CMAKE_${lang}98_EXTENSION_COMPILE_OPTION "-std=gnu++98") + set(CMAKE_${lang}_STANDARD_LATEST 98) endif() if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.1) @@ -142,19 +143,23 @@ set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=gnu++11") set(CMAKE_${lang}11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}_STANDARD_LATEST 11) elseif(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 2.1) set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c++0x") set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=gnu++0x") + set(CMAKE_${lang}_STANDARD_LATEST 11) endif() if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.5) set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=gnu++14") set(CMAKE_${lang}14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}_STANDARD_LATEST 14) elseif(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4) set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++1y") set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") set(CMAKE_${lang}14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}_STANDARD_LATEST 14) endif() set(_clang_version_std17 5.0) @@ -165,17 +170,21 @@ if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS "${_clang_version_std17}") set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + set(CMAKE_${lang}_STANDARD_LATEST 17) elseif (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.5) set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++1z") set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") + set(CMAKE_${lang}_STANDARD_LATEST 17) endif() if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 11.0) set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++20") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=gnu++20") + set(CMAKE_${lang}_STANDARD_LATEST 20) elseif(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS "${_clang_version_std17}") set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++2a") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") + set(CMAKE_${lang}_STANDARD_LATEST 20) endif() unset(_clang_version_std17) @@ -190,9 +199,11 @@ set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=gnu++23") set(CMAKE_${lang}26_STANDARD_COMPILE_OPTION "-std=c++26") set(CMAKE_${lang}26_EXTENSION_COMPILE_OPTION "-std=gnu++26") + set(CMAKE_${lang}_STANDARD_LATEST 26) elseif(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 12.0) set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std=c++2b") set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") + set(CMAKE_${lang}_STANDARD_LATEST 23) endif() unset(_clang_version_std23) @@ -231,14 +242,18 @@ set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std:c++latest") endif() + set(CMAKE_${lang}_STANDARD_LATEST 17) + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std:c++latest") set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std:c++latest") set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std:c++20") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std:c++20") + set(CMAKE_${lang}_STANDARD_LATEST 23) elseif(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std:c++latest") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std:c++latest") + set(CMAKE_${lang}_STANDARD_LATEST 20) endif() __compiler_check_default_language_standard(${lang} 3.9 14) @@ -262,6 +277,14 @@ # There is no meaningful default for this set(CMAKE_${lang}_STANDARD_DEFAULT "") + if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.0) + set(CMAKE_${lang}_STANDARD_LATEST 17) + elseif(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 18.0) + set(CMAKE_${lang}_STANDARD_LATEST 14) + elseif(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 16.0) + set(CMAKE_${lang}_STANDARD_LATEST 11) + endif() + # There are no compiler modes so we only need to test features once. # Override the default macro for this special case. Pretend that # all language standards are available so that at least compilation
diff --git a/Modules/Compiler/Cray-C.cmake b/Modules/Compiler/Cray-C.cmake index 9340948..51f8cc4 100644 --- a/Modules/Compiler/Cray-C.cmake +++ b/Modules/Compiler/Cray-C.cmake
@@ -14,10 +14,12 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION -h c99,conform) set(CMAKE_C99_EXTENSION_COMPILE_OPTION -h c99,gnu) set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 99) if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.5) set(CMAKE_C11_STANDARD_COMPILE_OPTION -h std=c11,conform) set(CMAKE_C11_EXTENSION_COMPILE_OPTION -h std=c11,gnu) set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 11) endif () endif ()
diff --git a/Modules/Compiler/Cray-CXX.cmake b/Modules/Compiler/Cray-CXX.cmake index 38c8b1e..9a40b98 100644 --- a/Modules/Compiler/Cray-CXX.cmake +++ b/Modules/Compiler/Cray-CXX.cmake
@@ -11,15 +11,18 @@ set(CMAKE_CXX98_STANDARD_COMPILE_OPTION -h conform) set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -h gnu) set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 98) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.4) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION -h std=c++11) set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -h std=c++11,gnu) set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 11) endif() if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.6) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION -h std=c++14) set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -h std=c++14,gnu) set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 14) endif () endif ()
diff --git a/Modules/Compiler/CrayClang-C.cmake b/Modules/Compiler/CrayClang-C.cmake index bf878fc..96e46be 100644 --- a/Modules/Compiler/CrayClang-C.cmake +++ b/Modules/Compiler/CrayClang-C.cmake
@@ -27,4 +27,6 @@ set(CMAKE_C23_STANDARD_COMPILE_OPTION -std=c2x) set(CMAKE_C23_EXTENSION_COMPILE_OPTION -std=gnu2x) +set(CMAKE_C_STANDARD_LATEST 23) + __compiler_check_default_language_standard(C 15.0.0 17)
diff --git a/Modules/Compiler/CrayClang-CXX.cmake b/Modules/Compiler/CrayClang-CXX.cmake index de6a53c..a61edfa 100644 --- a/Modules/Compiler/CrayClang-CXX.cmake +++ b/Modules/Compiler/CrayClang-CXX.cmake
@@ -32,4 +32,6 @@ set(CMAKE_CXX23_STANDARD_COMPILE_OPTION -std=c++2b) set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION -std=gnu++2b) +set(CMAKE_CXX_STANDARD_LATEST 23) + __compiler_check_default_language_standard(CXX 15.0.0 14)
diff --git a/Modules/Compiler/Fujitsu-C.cmake b/Modules/Compiler/Fujitsu-C.cmake index dd31e43..0ffa82d 100644 --- a/Modules/Compiler/Fujitsu-C.cmake +++ b/Modules/Compiler/Fujitsu-C.cmake
@@ -15,6 +15,8 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION -std=c11) set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=gnu11) set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_C_STANDARD_LATEST 11) endif() __compiler_check_default_language_standard(C 4 11)
diff --git a/Modules/Compiler/Fujitsu-CXX.cmake b/Modules/Compiler/Fujitsu-CXX.cmake index 0f42196..56f16c1 100644 --- a/Modules/Compiler/Fujitsu-CXX.cmake +++ b/Modules/Compiler/Fujitsu-CXX.cmake
@@ -42,6 +42,8 @@ set(CMAKE_CXX17_STANDARD_COMPILE_OPTION -std=c++17) set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION -std=gnu++17) + + set(CMAKE_CXX_STANDARD_LATEST 17) endif() __compiler_check_default_language_standard(CXX 4 14)
diff --git a/Modules/Compiler/GNU-C.cmake b/Modules/Compiler/GNU-C.cmake index 03e8d2b..0ac4d8c 100644 --- a/Modules/Compiler/GNU-C.cmake +++ b/Modules/Compiler/GNU-C.cmake
@@ -1,5 +1,6 @@ include(Compiler/GNU) __compiler_gnu(C) +__compiler_gnu_c_standards(C) if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) @@ -12,38 +13,4 @@ set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c) -if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) - set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90") - set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90") -elseif (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) - set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c89") - set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu89") -endif() - -if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) - set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON) - set(CMAKE_C99_STANDARD_COMPILE_OPTION "-std=c99") - set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99") - set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) -endif() - -if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7) - set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") - set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") - set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) -elseif (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6) - set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c1x") - set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu1x") -endif() - -if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1) - set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17") - set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17") -endif() - -if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 9.1) - set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x") - set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x") -endif() - __compiler_check_default_language_standard(C 3.4 90 5.0 11 8.1 17)
diff --git a/Modules/Compiler/GNU-CXX.cmake b/Modules/Compiler/GNU-CXX.cmake index 2e1b4ad..e98909b 100644 --- a/Modules/Compiler/GNU-CXX.cmake +++ b/Modules/Compiler/GNU-CXX.cmake
@@ -1,5 +1,6 @@ include(Compiler/GNU) __compiler_gnu(CXX) +__compiler_gnu_cxx_standards(CXX) if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) @@ -22,55 +23,6 @@ endif() endif() -if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) - set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-std=c++98") - set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98") -endif() - -if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) - set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) - set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") - set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") -elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) - # 4.3 supports 0x variants - set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++0x") - set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++0x") -endif() - -if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.1) - set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) -endif() - -if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) - set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") - set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") -elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8) - set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++1y") - set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") -endif() - -if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) -endif() - -if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) - set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") - set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") -elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1) - set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++1z") - set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") -endif() - -if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 11.1) - set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") - set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") - set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++23") - set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++23") -elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) - set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a") - set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") -endif() - __compiler_check_default_language_standard(CXX 3.4 98 6.0 14 11.1 17) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0)
diff --git a/Modules/Compiler/GNU-OBJC.cmake b/Modules/Compiler/GNU-OBJC.cmake index 7eeed83..4a2cd85 100644 --- a/Modules/Compiler/GNU-OBJC.cmake +++ b/Modules/Compiler/GNU-OBJC.cmake
@@ -1,5 +1,6 @@ include(Compiler/GNU) __compiler_gnu(OBJC) +__compiler_gnu_c_standards(OBJC) if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
diff --git a/Modules/Compiler/GNU-OBJCXX.cmake b/Modules/Compiler/GNU-OBJCXX.cmake index 1047b5d..e3006f2 100644 --- a/Modules/Compiler/GNU-OBJCXX.cmake +++ b/Modules/Compiler/GNU-OBJCXX.cmake
@@ -1,5 +1,6 @@ include(Compiler/GNU) __compiler_gnu(OBJCXX) +__compiler_gnu_cxx_standards(OBJCXX) if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake index 138bb4b..3f705a9 100644 --- a/Modules/Compiler/GNU.cmake +++ b/Modules/Compiler/GNU.cmake
@@ -151,7 +151,7 @@ list(APPEND __lto_flags -flto) endif() - if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.7) + if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.7 AND NOT APPLE) # '-ffat-lto-objects' introduced since GCC 4.7: # * https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Option-Summary.html (no) # * https://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/Option-Summary.html (yes) @@ -207,3 +207,112 @@ set(CMAKE_${lang}_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF "-fno-diagnostics-color") endif() endmacro() + +macro(__compiler_gnu_c_standards lang) + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.5) + set(CMAKE_${lang}90_STANDARD_COMPILE_OPTION "-std=c90") + set(CMAKE_${lang}90_EXTENSION_COMPILE_OPTION "-std=gnu90") + set(CMAKE_${lang}_STANDARD_LATEST 90) + elseif (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4) + set(CMAKE_${lang}90_STANDARD_COMPILE_OPTION "-std=c89") + set(CMAKE_${lang}90_EXTENSION_COMPILE_OPTION "-std=gnu89") + set(CMAKE_${lang}_STANDARD_LATEST 90) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4) + set(CMAKE_${lang}90_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}99_STANDARD_COMPILE_OPTION "-std=c99") + set(CMAKE_${lang}99_EXTENSION_COMPILE_OPTION "-std=gnu99") + set(CMAKE_${lang}99_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}_STANDARD_LATEST 99) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.7) + set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c11") + set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=gnu11") + set(CMAKE_${lang}11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}_STANDARD_LATEST 11) + elseif (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.6) + set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c1x") + set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=gnu1x") + set(CMAKE_${lang}_STANDARD_LATEST 11) + endif() + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1) + set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c17") + set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=gnu17") + set(CMAKE_${lang}_STANDARD_LATEST 17) + endif() + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 9.1) + set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std=c2x") + set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=gnu2x") + set(CMAKE_${lang}_STANDARD_LATEST 23) + endif() +endmacro() + +macro(__compiler_gnu_cxx_standards lang) + if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4) + set(CMAKE_${lang}98_STANDARD_COMPILE_OPTION "-std=c++98") + set(CMAKE_${lang}98_EXTENSION_COMPILE_OPTION "-std=gnu++98") + set(CMAKE_${lang}_STANDARD_LATEST 98) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.7) + set(CMAKE_${lang}98_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c++11") + set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=gnu++11") + set(CMAKE_${lang}_STANDARD_LATEST 11) + elseif (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.4) + # 4.3 supports 0x variants + set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c++0x") + set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=gnu++0x") + set(CMAKE_${lang}_STANDARD_LATEST 11) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.8.1) + set(CMAKE_${lang}11_STANDARD__HAS_FULL_SUPPORT ON) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.9) + set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++14") + set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=gnu++14") + set(CMAKE_${lang}_STANDARD_LATEST 14) + elseif (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.8) + set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++1y") + set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") + set(CMAKE_${lang}_STANDARD_LATEST 14) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 5.0) + set(CMAKE_${lang}14_STANDARD__HAS_FULL_SUPPORT ON) + endif() + + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 8.0) + set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++17") + set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + set(CMAKE_${lang}_STANDARD_LATEST 17) + elseif (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 5.1) + set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++1z") + set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") + set(CMAKE_${lang}_STANDARD_LATEST 17) + endif() + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 11.1) + set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++20") + set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=gnu++20") + set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std=c++23") + set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=gnu++23") + set(CMAKE_${lang}_STANDARD_LATEST 23) + elseif(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) + set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++2a") + set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") + set(CMAKE_${lang}_STANDARD_LATEST 20) + endif() + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) + set(CMAKE_${lang}26_STANDARD_COMPILE_OPTION "-std=c++26") + set(CMAKE_${lang}26_EXTENSION_COMPILE_OPTION "-std=gnu++26") + set(CMAKE_${lang}_STANDARD_LATEST 26) + endif() +endmacro()
diff --git a/Modules/Compiler/IBMClang-C.cmake b/Modules/Compiler/IBMClang-C.cmake index b69b1b8..fde6a3b 100644 --- a/Modules/Compiler/IBMClang-C.cmake +++ b/Modules/Compiler/IBMClang-C.cmake
@@ -23,8 +23,11 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") +set(CMAKE_C_STANDARD_LATEST 11) + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 17.1.0) set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17") + set(CMAKE_C_STANDARD_LATEST 17) endif () __compiler_check_default_language_standard(C 17.1.0 17)
diff --git a/Modules/Compiler/IBMClang-CXX.cmake b/Modules/Compiler/IBMClang-CXX.cmake index be9b525..ec97381 100644 --- a/Modules/Compiler/IBMClang-CXX.cmake +++ b/Modules/Compiler/IBMClang-CXX.cmake
@@ -26,6 +26,8 @@ set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") +set(CMAKE_CXX_STANDARD_LATEST 14) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.1.0) set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") @@ -33,6 +35,7 @@ set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++2b") set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") + set(CMAKE_CXX_STANDARD_LATEST 23) endif() __compiler_check_default_language_standard(CXX 17.1.0 17)
diff --git a/Modules/Compiler/Intel-C.cmake b/Modules/Compiler/Intel-C.cmake index 9884b58..bfe5f07 100644 --- a/Modules/Compiler/Intel-C.cmake +++ b/Modules/Compiler/Intel-C.cmake
@@ -19,12 +19,6 @@ set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl") set(CMAKE_C_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl") - if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 16.0.0) - set(CMAKE_C11_STANDARD_COMPILE_OPTION "-Qstd=c11") - set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-Qstd=c11") - set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) - endif() - if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.0) set(CMAKE_C90_STANDARD_COMPILE_OPTION "-Qstd=c89") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-Qstd=c89") @@ -32,18 +26,20 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION "-Qstd=c99") set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-Qstd=c99") set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 99) + endif() + + if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 16.0.0) + set(CMAKE_C11_STANDARD_COMPILE_OPTION "-Qstd=c11") + set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-Qstd=c11") + set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 11) endif() else() set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c) - if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 15.0.0) - set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") - set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") - set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) - endif() - if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.0) set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c89") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu89") @@ -51,6 +47,14 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION "-std=c99") set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99") set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 99) + endif() + + if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 15.0.0) + set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") + set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") + set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 11) endif() endif()
diff --git a/Modules/Compiler/Intel-CXX.cmake b/Modules/Compiler/Intel-CXX.cmake index 7c9cca9..f9e3e7a 100644 --- a/Modules/Compiler/Intel-CXX.cmake +++ b/Modules/Compiler/Intel-CXX.cmake
@@ -18,57 +18,70 @@ set(CMAKE_CXX_CLANG_TIDY_DRIVER_MODE "cl") set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl") - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0) - set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-Qstd=c++20") - set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-Qstd=c++20") - endif() + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1) + set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") + set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "") + set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0.0) - set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-Qstd=c++17") - set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-Qstd=c++17") + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0) + set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-Qstd=c++11") + set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-Qstd=c++11") + else() + set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-Qstd=c++0x") + set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-Qstd=c++0x") + endif() + + set(CMAKE_CXX_STANDARD_LATEST 11) endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.0) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-Qstd=c++14") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-Qstd=c++14") + set(CMAKE_CXX_STANDARD_LATEST 14) endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0) - set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-Qstd=c++11") - set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-Qstd=c++11") - elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1) - set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-Qstd=c++0x") - set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-Qstd=c++0x") + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0.0) + set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-Qstd=c++17") + set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-Qstd=c++17") + set(CMAKE_CXX_STANDARD_LATEST 17) endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1) - set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") - set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "") - set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0) + set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-Qstd=c++20") + set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-Qstd=c++20") + set(CMAKE_CXX_STANDARD_LATEST 20) endif() else() set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++) - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0) - set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") - set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1) + set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-std=c++98") + set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98") + set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) + + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0) + set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") + set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") + else() + set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++0x") + set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++0x") + endif() + + set(CMAKE_CXX_STANDARD_LATEST 11) endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0.0) - set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") - set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") - endif() - - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0) - set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0) + set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0.2) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") + set(CMAKE_CXX_STANDARD_LATEST 14) elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0.0) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++1y") + set(CMAKE_CXX_STANDARD_LATEST 14) endif() # Intel 15.0.2 accepts c++14 instead of c++1y, but not gnu++14 @@ -79,22 +92,20 @@ set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0) - set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0) + set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0) - set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") - set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") - elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1) - set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++0x") - set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++0x") + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0.0) + set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") + set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + set(CMAKE_CXX_STANDARD_LATEST 17) endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.1) - set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-std=c++98") - set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98") - set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0.20190206) + set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") + set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") + set(CMAKE_CXX_STANDARD_LATEST 20) endif() endif()
diff --git a/Modules/Compiler/IntelLLVM-C.cmake b/Modules/Compiler/IntelLLVM-C.cmake index 3a81154..4da4dfb 100644 --- a/Modules/Compiler/IntelLLVM-C.cmake +++ b/Modules/Compiler/IntelLLVM-C.cmake
@@ -42,24 +42,33 @@ set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17") - set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x") - set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x") + set(CMAKE_C_STANDARD_LATEST 17) + + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 2022.1.0) + set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x") + set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x") + set(CMAKE_C_STANDARD_LATEST 23) + endif() else() - # clang-cl doesn't have any of these - set(CMAKE_C90_STANDARD_COMPILE_OPTION "") - set(CMAKE_C90_EXTENSION_COMPILE_OPTION "") + set(CMAKE_C90_STANDARD_COMPILE_OPTION "-Qstd:c90") + set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-Qstd:c90") - set(CMAKE_C99_STANDARD_COMPILE_OPTION "") - set(CMAKE_C99_EXTENSION_COMPILE_OPTION "") + set(CMAKE_C99_STANDARD_COMPILE_OPTION "-Qstd:c99") + set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-Qstd:c99") - set(CMAKE_C11_STANDARD_COMPILE_OPTION "") - set(CMAKE_C11_EXTENSION_COMPILE_OPTION "") + set(CMAKE_C11_STANDARD_COMPILE_OPTION "-Qstd:c11") + set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-Qstd:c11") - set(CMAKE_C17_STANDARD_COMPILE_OPTION "") - set(CMAKE_C17_EXTENSION_COMPILE_OPTION "") + set(CMAKE_C17_STANDARD_COMPILE_OPTION "-Qstd:c17") + set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-Qstd:c17") - set(CMAKE_C23_STANDARD_COMPILE_OPTION "") - set(CMAKE_C23_EXTENSION_COMPILE_OPTION "") + set(CMAKE_C_STANDARD_LATEST 17) + + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 2022.1.0) + set(CMAKE_C23_STANDARD_COMPILE_OPTION "-Qstd:c2x") + set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-Qstd:c2x") + set(CMAKE_C_STANDARD_LATEST 23) + endif() endif() __compiler_check_default_language_standard(C 2020 17)
diff --git a/Modules/Compiler/IntelLLVM-CXX.cmake b/Modules/Compiler/IntelLLVM-CXX.cmake index 45b723f..45e5d66 100644 --- a/Modules/Compiler/IntelLLVM-CXX.cmake +++ b/Modules/Compiler/IntelLLVM-CXX.cmake
@@ -47,8 +47,23 @@ set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") - set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++2b") - set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") + set(CMAKE_CXX_STANDARD_LATEST 20) + + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0) + set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++23") + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++23") + set(CMAKE_CXX_STANDARD_LATEST 23) + elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2021.2.0) + set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++2b") + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") + set(CMAKE_CXX_STANDARD_LATEST 23) + endif() + + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0) + 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() else() set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "") @@ -65,8 +80,24 @@ set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-Qstd:c++20") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-Qstd:c++20") - set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-Qstd:c++2b") - set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-Qstd:c++2b") + set(CMAKE_CXX_STANDARD_LATEST 20) + + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0) + set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-Qstd=c++23") + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-Qstd=c++23") + set(CMAKE_CXX_STANDARD_LATEST 23) + elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2021.2.0) + set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-Qstd:c++2b") + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-Qstd:c++2b") + set(CMAKE_CXX_STANDARD_LATEST 23) + endif() + + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0) + set(CMAKE_CXX26_STANDARD_COMPILE_OPTION "-Qstd=c++26") + set(CMAKE_CXX26_EXTENSION_COMPILE_OPTION "-Qstd=c++26") + set(CMAKE_CXX_STANDARD_LATEST 26) + endif() + endif() __compiler_check_default_language_standard(CXX 2020 14)
diff --git a/Modules/Compiler/IntelLLVM-FindBinUtils.cmake b/Modules/Compiler/IntelLLVM-FindBinUtils.cmake index c5b1ee6..a4445f7 100644 --- a/Modules/Compiler/IntelLLVM-FindBinUtils.cmake +++ b/Modules/Compiler/IntelLLVM-FindBinUtils.cmake
@@ -19,8 +19,11 @@ get_filename_component(__intel_llvm_hint_1 "${__intel_llvm_hint_1}/../bin-llvm" REALPATH) get_filename_component(__intel_llvm_hint_2 "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY) +get_filename_component(__intel_llvm_hint_2 "${__intel_llvm_hint_2}/compiler" REALPATH) -set(__intel_llvm_hints ${__intel_llvm_hint_1} ${__intel_llvm_hint_2}) +get_filename_component(__intel_llvm_hint_3 "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY) + +set(__intel_llvm_hints ${__intel_llvm_hint_1} ${__intel_llvm_hint_2} ${__intel_llvm_hint_3}) # http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ar.1.html find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES
diff --git a/Modules/Compiler/LCC-C.cmake b/Modules/Compiler/LCC-C.cmake index 52c3bf3..01e99d3 100644 --- a/Modules/Compiler/LCC-C.cmake +++ b/Modules/Compiler/LCC-C.cmake
@@ -22,15 +22,19 @@ set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99") set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C_STANDARD_LATEST 99) + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 1.20) set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 11) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 1.26) set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17") + set(CMAKE_C_STANDARD_LATEST 17) endif() __compiler_check_default_language_standard(C 1.17 89 1.23 99 1.26 17)
diff --git a/Modules/Compiler/LCC-CXX.cmake b/Modules/Compiler/LCC-CXX.cmake index 385947a..22f2cc3 100644 --- a/Modules/Compiler/LCC-CXX.cmake +++ b/Modules/Compiler/LCC-CXX.cmake
@@ -18,26 +18,32 @@ set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98") set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_CXX_STANDARD_LATEST 98) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.20) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 11) endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.21) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 14) endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.24) set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + set(CMAKE_CXX_STANDARD_LATEST 17) endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.26) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") + set(CMAKE_CXX_STANDARD_LATEST 20) endif() __compiler_check_default_language_standard(CXX 1.19 98 1.20 11 1.21 14 1.24 17 1.26 20)
diff --git a/Modules/Compiler/MSVC-C.cmake b/Modules/Compiler/MSVC-C.cmake index 6bf6b4e..d7fbf9f 100644 --- a/Modules/Compiler/MSVC-C.cmake +++ b/Modules/Compiler/MSVC-C.cmake
@@ -14,12 +14,15 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std:c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std:c11") + set(CMAKE_C_STANDARD_LATEST 11) + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28) set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON) set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std:c17") set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std:c17") + set(CMAKE_C_STANDARD_LATEST 17) else() # Special case for 19.27 (VS 16.7): C11 has partial support. macro(cmake_record_c_compile_features) @@ -41,6 +44,7 @@ set(CMAKE_C99_EXTENSION_COMPILE_OPTION "") set(CMAKE_C11_STANDARD_COMPILE_OPTION "") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "") + set(CMAKE_C_STANDARD_LATEST 11) # There is no meaningful default for this set(CMAKE_C_STANDARD_DEFAULT "")
diff --git a/Modules/Compiler/MSVC-CXX-CXXImportStd.cmake b/Modules/Compiler/MSVC-CXX-CXXImportStd.cmake new file mode 100644 index 0000000..08199f7 --- /dev/null +++ b/Modules/Compiler/MSVC-CXX-CXXImportStd.cmake
@@ -0,0 +1,115 @@ +function (_cmake_cxx_import_std std variable) + find_file(_msvc_modules_json_file + NAME modules.json + HINTS + "$ENV{VCToolsInstallDir}/modules" + PATHS + "$ENV{INCLUDE}" + "${CMAKE_CXX_COMPILER}/../../.." + "${CMAKE_CXX_COMPILER}/../.." # msvc-wine layout + PATH_SUFFIXES + ../modules + NO_CACHE) + # Without this file, we do not have modules installed. + if (NOT EXISTS "${_msvc_modules_json_file}") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Could not find `modules.json` resource\")\n" + PARENT_SCOPE) + return () + endif () + + file(READ "${_msvc_modules_json_file}" _msvc_modules_json) + string(JSON _msvc_json_version GET "${_msvc_modules_json}" "version") + string(JSON _msvc_json_revision GET "${_msvc_modules_json}" "revision") + # Require version 1. + if (NOT _msvc_json_version EQUAL "1") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`modules.json` version ${_msvc_json_version}.${_msvc_json_revision} is not recognized\")\n" + PARENT_SCOPE) + return () + endif () + + string(JSON _msvc_json_library GET "${_msvc_modules_json}" "library") + # Bail if we don't understand the library. + if (NOT _msvc_json_library STREQUAL "microsoft/STL") + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`modules.json` library `${_msvc_json_library}` is not recognized\")\n" + PARENT_SCOPE) + return () + endif () + + string(JSON _msvc_json_nmodules LENGTH "${_msvc_modules_json}" "module-sources") + # Don't declare the target without any modules. + if (NOT _msvc_json_nmodules) + set("${variable}" + "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`modules.json` does not list any available modules\")\n" + PARENT_SCOPE) + return () + endif () + + # Declare the target. + set(_msvc_std_target "") + string(APPEND _msvc_std_target + "add_library(__cmake_cxx${std} STATIC)\n") + string(APPEND _msvc_std_target + "target_sources(__cmake_cxx${std} INTERFACE \"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>:$<TARGET_OBJECTS:__cmake_cxx${std}>>\")\n") + string(APPEND _msvc_std_target + "set_property(TARGET __cmake_cxx${std} PROPERTY EXCLUDE_FROM_ALL 1)\n") + string(APPEND _msvc_std_target + "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_SCAN_FOR_MODULES 1)\n") + string(APPEND _msvc_std_target + "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_MODULE_STD 0)\n") + string(APPEND _msvc_std_target + "target_compile_features(__cmake_cxx${std} PUBLIC cxx_std_${std})\n") + + set(_msvc_modules_module_paths "") + get_filename_component(_msvc_modules_dir "${_msvc_modules_json_file}" DIRECTORY) + + # Add module sources. + math(EXPR _msvc_modules_json_nmodules_range "${_msvc_json_nmodules} - 1") + foreach (_msvc_modules_json_modules_idx RANGE 0 "${_msvc_modules_json_nmodules_range}") + string(JSON _msvc_modules_json_module_source GET "${_msvc_modules_json}" "module-sources" "${_msvc_modules_json_modules_idx}") + + if (NOT IS_ABSOLUTE "${_msvc_modules_json_module_source}") + string(PREPEND _msvc_modules_json_module_source "${_msvc_modules_dir}/") + endif () + list(APPEND _msvc_modules_module_paths + "${_msvc_modules_json_module_source}") + endforeach () + + # Split the paths into basedirs and module paths. + set(_msvc_modules_base_dirs_list "") + set(_msvc_modules_files "") + foreach (_msvc_modules_module_path IN LISTS _msvc_modules_module_paths) + get_filename_component(_msvc_module_dir "${_msvc_modules_module_path}" DIRECTORY) + + list(APPEND _msvc_modules_base_dirs_list + "${_msvc_module_dir}") + string(APPEND _msvc_modules_files + " \"${_msvc_modules_module_path}\"") + endforeach () + list(REMOVE_DUPLICATES _msvc_modules_base_dirs_list) + set(_msvc_modules_base_dirs "") + foreach (_msvc_modules_base_dir IN LISTS _msvc_modules_base_dirs_list) + string(APPEND _msvc_modules_base_dirs + " \"${_msvc_modules_base_dir}\"") + endforeach () + + # Create the file set for the modules. + string(APPEND _msvc_std_target + "target_sources(__cmake_cxx${std} + PUBLIC + FILE_SET std TYPE CXX_MODULES + BASE_DIRS ${_msvc_modules_base_dirs} + FILES ${_msvc_modules_files})\n") + + # Wrap the `__cmake_cxx${std}` target in a check. + string(PREPEND _msvc_std_target + "if (NOT TARGET \"__cmake_cxx${std}\")\n") + string(APPEND _msvc_std_target + "endif ()\n") + string(APPEND _msvc_std_target + "add_library(__CMAKE::CXX${std} ALIAS __cmake_cxx${std})\n") + + set("${variable}" "${_msvc_std_target}" PARENT_SCOPE) +endfunction ()
diff --git a/Modules/Compiler/MSVC-CXX.cmake b/Modules/Compiler/MSVC-CXX.cmake index 79cd2e0..2243a14 100644 --- a/Modules/Compiler/MSVC-CXX.cmake +++ b/Modules/Compiler/MSVC-CXX.cmake
@@ -30,14 +30,18 @@ set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std:c++latest") endif() + set(CMAKE_CXX_STANDARD_LATEST 17) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30129) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std:c++20") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std:c++20") set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std:c++latest") set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std:c++latest") + set(CMAKE_CXX_STANDARD_LATEST 23) elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.12.25835) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std:c++latest") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std:c++latest") + set(CMAKE_CXX_STANDARD_LATEST 20) endif() __compiler_check_default_language_standard(CXX 19.0 14) @@ -57,6 +61,14 @@ set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "") + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0) + set(CMAKE_CXX_STANDARD_LATEST 17) + elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 18.0) + set(CMAKE_CXX_STANDARD_LATEST 14) + else() + set(CMAKE_CXX_STANDARD_LATEST 11) + endif() + # There is no meaningful default for this set(CMAKE_CXX_STANDARD_DEFAULT "")
diff --git a/Modules/Compiler/NAG-Fortran.cmake b/Modules/Compiler/NAG-Fortran.cmake index b946cfd..a6636c4 100644 --- a/Modules/Compiler/NAG-Fortran.cmake +++ b/Modules/Compiler/NAG-Fortran.cmake
@@ -28,6 +28,13 @@ endif() endif() +# Initial configuration flags. +string(APPEND CMAKE_Fortran_FLAGS_INIT " ") +string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " -g") +string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " -O2 -DNDEBUG") +string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O4 -DNDEBUG") +string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") + set(CMAKE_Fortran_SUBMODULE_SEP ".") set(CMAKE_Fortran_SUBMODULE_EXT ".sub") set(CMAKE_Fortran_MODDIR_FLAG "-mdir ") @@ -39,3 +46,6 @@ set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "-PIC") set(CMAKE_Fortran_RESPONSE_FILE_LINK_FLAG "-Wl,@") set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-fpp") + +set(CMAKE_Fortran_PREPROCESS_SOURCE + "<CMAKE_Fortran_COMPILER> -fpp <DEFINES> <INCLUDES> <FLAGS> -F <SOURCE> -o <PREPROCESSED_SOURCE>")
diff --git a/Modules/Compiler/NVHPC-C.cmake b/Modules/Compiler/NVHPC-C.cmake index 9295abd..3838ebd 100644 --- a/Modules/Compiler/NVHPC-C.cmake +++ b/Modules/Compiler/NVHPC-C.cmake
@@ -7,6 +7,7 @@ if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 20.11) set(CMAKE_C17_STANDARD_COMPILE_OPTION -std=c17) set(CMAKE_C17_EXTENSION_COMPILE_OPTION -std=gnu17) + set(CMAKE_C_STANDARD_LATEST 17) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.07)
diff --git a/Modules/Compiler/NVHPC-CXX.cmake b/Modules/Compiler/NVHPC-CXX.cmake index 59ba7bf..0d804f4 100644 --- a/Modules/Compiler/NVHPC-CXX.cmake +++ b/Modules/Compiler/NVHPC-CXX.cmake
@@ -7,6 +7,7 @@ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 20.11) set(CMAKE_CXX20_STANDARD_COMPILE_OPTION -std=c++20) set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION -std=gnu++20) + set(CMAKE_CXX_STANDARD_LATEST 20) endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 21.07)
diff --git a/Modules/Compiler/NVIDIA.cmake b/Modules/Compiler/NVIDIA.cmake index a126c57..21e71cc 100644 --- a/Modules/Compiler/NVIDIA.cmake +++ b/Modules/Compiler/NVIDIA.cmake
@@ -17,6 +17,8 @@ set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "") set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "") + set(CMAKE_${lang}_STANDARD_LATEST 11) + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 9.0) if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.10.25017) set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++14") @@ -25,12 +27,15 @@ set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "") set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "") endif() + + set(CMAKE_${lang}_STANDARD_LATEST 14) endif() if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 11.0) if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505) set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=c++17") + set(CMAKE_${lang}_STANDARD_LATEST 17) endif() endif() @@ -38,6 +43,7 @@ if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505) 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() else() @@ -47,21 +53,27 @@ set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=c++11") + set(CMAKE_${lang}_STANDARD_LATEST 11) + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 9.0) set(CMAKE_${lang}03_STANDARD_COMPILE_OPTION "-std=c++03") set(CMAKE_${lang}03_EXTENSION_COMPILE_OPTION "-std=c++03") set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=c++14") + + set(CMAKE_${lang}_STANDARD_LATEST 14) endif() if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 11.0) set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=c++17") + set(CMAKE_${lang}_STANDARD_LATEST 17) endif() if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 12.0) 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()
diff --git a/Modules/Compiler/OrangeC-C.cmake b/Modules/Compiler/OrangeC-C.cmake index 15a6476..f0dfcd1 100644 --- a/Modules/Compiler/OrangeC-C.cmake +++ b/Modules/Compiler/OrangeC-C.cmake
@@ -15,6 +15,8 @@ set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=c11) set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C_STANDARD_LATEST 11) + __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 3f9d59c..575c09d 100644 --- a/Modules/Compiler/OrangeC-CXX.cmake +++ b/Modules/Compiler/OrangeC-CXX.cmake
@@ -20,6 +20,8 @@ set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=c++14") set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_CXX_STANDARD_LATEST 14) + __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/PGI-C.cmake b/Modules/Compiler/PGI-C.cmake index c39dbe5..798fc84 100644 --- a/Modules/Compiler/PGI-C.cmake +++ b/Modules/Compiler/PGI-C.cmake
@@ -10,10 +10,13 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION -c99) set(CMAKE_C99_EXTENSION_COMPILE_OPTION -c99) set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_C_STANDARD_LATEST 99) if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.3) set(CMAKE_C11_STANDARD_COMPILE_OPTION -c11) set(CMAKE_C11_EXTENSION_COMPILE_OPTION -c11) set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 11) endif () endif ()
diff --git a/Modules/Compiler/PGI-CXX.cmake b/Modules/Compiler/PGI-CXX.cmake index 1279c19..972e43a 100644 --- a/Modules/Compiler/PGI-CXX.cmake +++ b/Modules/Compiler/PGI-CXX.cmake
@@ -7,19 +7,23 @@ set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION --gnu_extensions) set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 98) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.10) set(CMAKE_CXX98_STANDARD_COMPILE_OPTION --c++03) set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION --c++03 --gnu_extensions) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION --c++11) set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION --c++11 --gnu_extensions) set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 11) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.7) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION --c++14) set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION --c++14 --gnu_extensions) set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 14) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.1) set(CMAKE_CXX17_STANDARD_COMPILE_OPTION --c++17) set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION --c++17 --gnu_extensions) + set(CMAKE_CXX_STANDARD_LATEST 17) endif() endif() endif()
diff --git a/Modules/Compiler/QCC.cmake b/Modules/Compiler/QCC.cmake index b720dc1..b5dca3e 100644 --- a/Modules/Compiler/QCC.cmake +++ b/Modules/Compiler/QCC.cmake
@@ -19,9 +19,6 @@ set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,") set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",") - set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE NO) - set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER NO) - if("${lang}" STREQUAL "CXX") set(CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "${CMAKE_${lang}_COMPILER}") if(CMAKE_${lang}_COMPILER_ARG1) @@ -31,9 +28,4 @@ endif() list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-Wp,-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp") endif() - - unset(CMAKE_${lang}_COMPILE_OPTIONS_IPO) - unset(CMAKE_${lang}_ARCHIVE_CREATE_IPO) - unset(CMAKE_${lang}_ARCHIVE_APPEND_IPO) - unset(CMAKE_${lang}_ARCHIVE_FINISH_IPO) endmacro()
diff --git a/Modules/Compiler/SunPro-C.cmake b/Modules/Compiler/SunPro-C.cmake index b06719d..7b4478c 100644 --- a/Modules/Compiler/SunPro-C.cmake +++ b/Modules/Compiler/SunPro-C.cmake
@@ -52,11 +52,15 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=c11") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_C_STANDARD_LATEST 11) elseif (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.11) set(CMAKE_C90_STANDARD_COMPILE_OPTION "") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "") set(CMAKE_C99_STANDARD_COMPILE_OPTION "-xc99") set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-xc99") + + set(CMAKE_C_STANDARD_LATEST 99) endif() __compiler_check_default_language_standard(C 5.11 90 5.14 11)
diff --git a/Modules/Compiler/SunPro-CXX.cmake b/Modules/Compiler/SunPro-CXX.cmake index f835f2d..1db6aa7 100644 --- a/Modules/Compiler/SunPro-CXX.cmake +++ b/Modules/Compiler/SunPro-CXX.cmake
@@ -54,14 +54,18 @@ set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=c++11") set(CMAKE_CXX_LINK_WITH_STANDARD_COMPILE_OPTION 1) + set(CMAKE_CXX_STANDARD_LATEST 11) + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.14) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=c++14") + set(CMAKE_CXX_STANDARD_LATEST 14) endif() else() set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-library=stlport4") set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-library=stlport4") set(CMAKE_CXX_LINK_WITH_STANDARD_COMPILE_OPTION 1) + set(CMAKE_CXX_STANDARD_LATEST 98) endif() __compiler_check_default_language_standard(CXX 1 98)
diff --git a/Modules/Compiler/TI-C.cmake b/Modules/Compiler/TI-C.cmake index bd88989..7f3b67c 100644 --- a/Modules/Compiler/TI-C.cmake +++ b/Modules/Compiler/TI-C.cmake
@@ -34,18 +34,23 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION "--c99" "--strict_ansi") set(CMAKE_C99_EXTENSION_COMPILE_OPTION "--c99" "--relaxed_ansi") + set(CMAKE_C_STANDARD_LATEST 99) + if(DEFINED __COMPILER_TI_C11_VERSION_${CMAKE_C_COMPILER_ARCHITECTURE_ID} AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "${__COMPILER_TI_C11_VERSION_${CMAKE_C_COMPILER_ARCHITECTURE_ID}}") set(CMAKE_C11_STANDARD_COMPILE_OPTION "--c11" "--strict_ansi") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "--c11" "--relaxed_ansi") + set(CMAKE_C_STANDARD_LATEST 11) + endif() else() set(CMAKE_C90_STANDARD_COMPILE_OPTION "--strict_ansi") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "--relaxed_ansi") + set(CMAKE_C_STANDARD_LATEST 90) endif()
diff --git a/Modules/Compiler/TI-CXX.cmake b/Modules/Compiler/TI-CXX.cmake index 4b6efcd..c904239 100644 --- a/Modules/Compiler/TI-CXX.cmake +++ b/Modules/Compiler/TI-CXX.cmake
@@ -39,16 +39,19 @@ set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "--c++14" "--strict_ansi") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "--c++14" "--relaxed_ansi") + set(CMAKE_CXX_STANDARD_LATEST 14) elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "${__COMPILER_TI_CXX03_VERSION}") set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "--c++03" "--strict_ansi") set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "--c++03" "--relaxed_ansi") + set(CMAKE_CXX_STANDARD_LATEST 98) else() set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "--strict_ansi") set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "--relaxed_ansi") + set(CMAKE_CXX_STANDARD_LATEST 98) endif()
diff --git a/Modules/Compiler/TIClang-C.cmake b/Modules/Compiler/TIClang-C.cmake index 2721fef..b6ce3dc 100644 --- a/Modules/Compiler/TIClang-C.cmake +++ b/Modules/Compiler/TIClang-C.cmake
@@ -21,3 +21,13 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_C_STANDARD_LATEST 11) + +# Including the "${CMAKE_ROOT}/Modules/Compiler/Clang-C.cmake" script above may set several other compile option +# variables which do not necessarily apply here. So, we unset those variables accordingly. +unset(CMAKE_C17_STANDARD_COMPILE_OPTION) +unset(CMAKE_C17_EXTENSION_COMPILE_OPTION) + +unset(CMAKE_C23_STANDARD_COMPILE_OPTION) +unset(CMAKE_C23_EXTENSION_COMPILE_OPTION)
diff --git a/Modules/Compiler/TIClang-CXX.cmake b/Modules/Compiler/TIClang-CXX.cmake index 860bb42..e1367df 100644 --- a/Modules/Compiler/TIClang-CXX.cmake +++ b/Modules/Compiler/TIClang-CXX.cmake
@@ -23,3 +23,16 @@ set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") + +set(CMAKE_CXX_STANDARD_LATEST 17) + +# Including the "${CMAKE_ROOT}/Modules/Compiler/Clang-CXX.cmake" script above may set several other compile option +# variables which do not necessarily apply here. So, we unset those variables accordingly. +unset(CMAKE_CXX20_STANDARD_COMPILE_OPTION) +unset(CMAKE_CXX20_EXTENSION_COMPILE_OPTION) + +unset(CMAKE_CXX23_STANDARD_COMPILE_OPTION) +unset(CMAKE_CXX23_EXTENSION_COMPILE_OPTION) + +unset(CMAKE_CXX26_STANDARD_COMPILE_OPTION) +unset(CMAKE_CXX26_EXTENSION_COMPILE_OPTION)
diff --git a/Modules/Compiler/Tasking-C.cmake b/Modules/Compiler/Tasking-C.cmake index 0ea3cd2..938f46e 100644 --- a/Modules/Compiler/Tasking-C.cmake +++ b/Modules/Compiler/Tasking-C.cmake
@@ -10,6 +10,8 @@ set(CMAKE_C11_STANDARD_COMPILE_OPTION "--iso=11" "--strict") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "--iso=11" " ") +set(CMAKE_C_STANDARD_LATEST 11) + if(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "TriCore") if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode") __compiler_check_default_language_standard(C 10.1 11)
diff --git a/Modules/Compiler/Tasking-CXX.cmake b/Modules/Compiler/Tasking-CXX.cmake index 635104c..20e007f 100644 --- a/Modules/Compiler/Tasking-CXX.cmake +++ b/Modules/Compiler/Tasking-CXX.cmake
@@ -12,6 +12,8 @@ set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "--c++=14" "--strict") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "--c++=14" " ") +set(CMAKE_CXX_STANDARD_LATEST 14) + if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "TriCore") if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode") __compiler_check_default_language_standard(CXX 10.1 14)
diff --git a/Modules/Compiler/XL-C.cmake b/Modules/Compiler/XL-C.cmake index 2077bda..d14ac85 100644 --- a/Modules/Compiler/XL-C.cmake +++ b/Modules/Compiler/XL-C.cmake
@@ -13,10 +13,13 @@ set(CMAKE_C99_STANDARD_COMPILE_OPTION "-qlanglvl=stdc99") set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-qlanglvl=extc99") set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_C_STANDARD_LATEST 99) if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1) set(CMAKE_C11_STANDARD_COMPILE_OPTION "-qlanglvl=extc1x") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-qlanglvl=extc1x") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_C_STANDARD_LATEST 11) endif () endif()
diff --git a/Modules/Compiler/XL-CXX.cmake b/Modules/Compiler/XL-CXX.cmake index 41e3e11..57bd5a9 100644 --- a/Modules/Compiler/XL-CXX.cmake +++ b/Modules/Compiler/XL-CXX.cmake
@@ -24,10 +24,13 @@ # compiler mode for the corresponding standard. set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 11) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.1.0 AND CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-qlanglvl=extended1y") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-qlanglvl=extended1y") set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + set(CMAKE_CXX_STANDARD_LATEST 14) endif() endif ()
diff --git a/Modules/Compiler/XLClang-C.cmake b/Modules/Compiler/XLClang-C.cmake index 1668a4d..7ca76e4 100644 --- a/Modules/Compiler/XLClang-C.cmake +++ b/Modules/Compiler/XLClang-C.cmake
@@ -12,11 +12,14 @@ set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) set(CMAKE_C11_STANDARD_COMPILE_OPTION "-qlanglvl=extc1x") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-qlanglvl=extc1x") + if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1.2) set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11") set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11") set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) endif () + + set(CMAKE_C_STANDARD_LATEST 11) endif() __compiler_check_default_language_standard(C 13.1.1 99)
diff --git a/Modules/Compiler/XLClang-CXX.cmake b/Modules/Compiler/XLClang-CXX.cmake index 02638c7..b324987 100644 --- a/Modules/Compiler/XLClang-CXX.cmake +++ b/Modules/Compiler/XLClang-CXX.cmake
@@ -10,12 +10,17 @@ set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-qlanglvl=extended0x") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-qlanglvl=extended0x") set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_CXX_STANDARD_LATEST 11) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1.2) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++1y") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + + set(CMAKE_CXX_STANDARD_LATEST 14) endif () if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.1.0) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
diff --git a/Modules/DartConfiguration.tcl.in b/Modules/DartConfiguration.tcl.in index afa36f7..67d9f8f 100644 --- a/Modules/DartConfiguration.tcl.in +++ b/Modules/DartConfiguration.tcl.in
@@ -95,6 +95,9 @@ # so would cause the system load to exceed this value. TestLoad: @CTEST_TEST_LOAD@ +TLSVerify: @CTEST_TLS_VERIFY@ +TLSVersion: @CTEST_TLS_VERSION@ + UseLaunchers: @CTEST_USE_LAUNCHERS@ CurlOptions: @CTEST_CURL_OPTIONS@ # warning, if you add new options here that have to do with submit,
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 5dab044..86d83f5 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake
@@ -225,22 +225,45 @@ Provides an arbitrary list of HTTP headers for the download operation. This can be useful for accessing content in systems like AWS, etc. +``TLS_VERSION <min>`` + .. versionadded:: 3.30 + + Specify minimum TLS version for ``https://`` URLs. If this option is + not provided, the value of the :variable:`CMAKE_TLS_VERSION` variable + or the :envvar:`CMAKE_TLS_VERSION` environment variable will be used + instead (see :command:`file(DOWNLOAD)`). + + This option also applies to ``git clone`` invocations, although the + default behavior is different. If none of the ``TLS_VERSION`` option, + :variable:`CMAKE_TLS_VERSION` variable, or :envvar:`CMAKE_TLS_VERSION` + environment variable is specified, the behavior will be determined by + git's default or a ``http.sslVersion`` git config option the user may + have set at a global level. + ``TLS_VERIFY <bool>`` Specifies whether certificate verification should be performed for - https URLs. If this option is not provided, the default behavior is - determined by the :variable:`CMAKE_TLS_VERIFY` variable (see - :command:`file(DOWNLOAD)`). If that is also not set, certificate - verification will not be performed. In situations where ``URL_HASH`` - cannot be provided, this option can be an alternative verification - measure. + ``https://`` URLs. If this option is not provided, the value of the + :variable:`CMAKE_TLS_VERIFY` variable or the :envvar:`CMAKE_TLS_VERIFY` + environment variable will be used instead (see :command:`file(DOWNLOAD)`). + If neither of those is set, certificate verification will not be performed. + In situations where ``URL_HASH`` cannot be provided, this option can + be an alternative verification measure. + + This option also applies to ``git clone`` invocations, although the + default behavior is different. If none of the ``TLS_VERIFY`` option, + :variable:`CMAKE_TLS_VERIFY` variable, or :envvar:`CMAKE_TLS_VERIFY` + environment variable is specified, the behavior will be determined by + git's default (true) or a ``http.sslVerify`` git config option the + user may have set at a global level. .. versionchanged:: 3.6 - This option also applies to ``git clone`` invocations, although the - default behavior is different. If ``TLS_VERIFY`` is not given and - :variable:`CMAKE_TLS_VERIFY` is not set, the behavior will be - determined by git's defaults. Normally, the ``sslVerify`` git - config setting defaults to true, but the user may have overridden - this at a global level. + + Previously this option did not apply to ``git clone`` invocations. + + .. versionchanged:: 3.30 + + Previously the :envvar:`CMAKE_TLS_VERIFY` environment variable + was not checked. ``TLS_CAINFO <file>`` Specify a custom certificate authority file to use if ``TLS_VERIFY`` @@ -1273,442 +1296,12 @@ cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST -macro(_ep_get_hash_algos out_var) - set(${out_var} - MD5 - SHA1 - SHA224 - SHA256 - SHA384 - SHA512 - SHA3_224 - SHA3_256 - SHA3_384 - SHA3_512 - ) -endmacro() - -macro(_ep_get_hash_regex out_var) - _ep_get_hash_algos(${out_var}) - list(JOIN ${out_var} "|" ${out_var}) - set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$") -endmacro() - -function(_ep_parse_arguments - f - keywords - name - ns - args -) - # Transfer the arguments to this function into target properties for the - # new custom target we just added so that we can set up all the build steps - # correctly based on target properties. - # - # Because some keywords can be repeated, we can't use cmake_parse_arguments(). - # Instead, we loop through ARGN and consider the namespace starting with an - # upper-case letter followed by at least two more upper-case letters, - # numbers or underscores to be keywords. - - set(key) - - foreach(arg IN LISTS args) - set(is_value 1) - - if(arg MATCHES "^[A-Z][A-Z0-9_][A-Z0-9_]+$" AND - NOT (("x${arg}x" STREQUAL "x${key}x") AND - ("x${key}x" STREQUAL "xCOMMANDx")) AND - NOT arg MATCHES "^(TRUE|FALSE)$") - if(arg IN_LIST keywords) - set(is_value 0) - endif() - endif() - - if(is_value) - if(key) - # Value - if(NOT arg STREQUAL "") - set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}") - else() - get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET) - if(have_key) - get_property(value TARGET ${name} PROPERTY ${ns}${key}) - set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}") - else() - set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}") - endif() - endif() - else() - # Missing Keyword - message(AUTHOR_WARNING - "value '${arg}' with no previous keyword in ${f}" - ) - endif() - else() - set(key "${arg}") - endif() - endforeach() -endfunction() - - define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED) define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED) define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED) define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED) define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED) -function(_ep_write_gitclone_script - script_filename - source_dir - git_EXECUTABLE - git_repository - git_tag - git_remote_name - init_submodules - git_submodules_recurse - git_submodules - git_shallow - git_progress - git_config - src_name - work_dir - gitclone_infofile - gitclone_stampfile - tls_verify -) - - if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) - # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path. - set(git_checkout_explicit-- "--") - else() - # Use `git checkout <branch>` even though this risks ambiguity with a - # local path. Unfortunately we cannot use `git checkout <tree-ish> --` - # because that will not search for remote branch names, a common use case. - set(git_checkout_explicit-- "") - endif() - if("${git_tag}" STREQUAL "") - message(FATAL_ERROR "Tag for git checkout should not be empty.") - endif() - - set(git_submodules_config_options "") - - if(GIT_VERSION_STRING VERSION_LESS 2.20 OR - 2.21 VERSION_LESS_EQUAL GIT_VERSION_STRING) - set(git_clone_options "--no-checkout") - else() - set(git_clone_options) - endif() - if(git_shallow) - if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10) - list(APPEND git_clone_options "--depth 1 --no-single-branch") - else() - list(APPEND git_clone_options "--depth 1") - endif() - endif() - if(git_progress) - list(APPEND git_clone_options --progress) - endif() - foreach(config IN LISTS git_config) - list(APPEND git_clone_options --config \"${config}\") - endforeach() - if(NOT ${git_remote_name} STREQUAL "origin") - list(APPEND git_clone_options --origin \"${git_remote_name}\") - endif() - if(NOT "x${tls_verify}" STREQUAL "x") - # The clone config option is sticky, it will apply to all subsequent git - # update operations. The submodules config option is not sticky, because - # git doesn't provide any way to do that. Thus, we will have to pass the - # same config option in the update step too for submodules, but not for - # the main git repo. - if(tls_verify) - # Default git behavior is "true", but the user might have changed the - # global default to "false". Since TLS_VERIFY was given, ensure we honor - # the specified setting regardless of what the global default might be. - list(APPEND git_clone_options -c http.sslVerify=true) - set(git_submodules_config_options -c http.sslVerify=true) - else() - list(APPEND git_clone_options -c http.sslVerify=false) - set(git_submodules_config_options -c http.sslVerify=false) - endif() - endif() - - string (REPLACE ";" " " git_clone_options "${git_clone_options}") - - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in - ${script_filename} - @ONLY - ) -endfunction() - -function(_ep_write_hgclone_script - script_filename - source_dir - hg_EXECUTABLE - hg_repository - hg_tag - src_name - work_dir - hgclone_infofile - hgclone_stampfile -) - - if("${hg_tag}" STREQUAL "") - message(FATAL_ERROR "Tag for hg checkout should not be empty.") - endif() - - configure_file( - ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in - ${script_filename} - @ONLY - ) -endfunction() - - -function(_ep_write_gitupdate_script - script_filename - git_EXECUTABLE - git_tag - git_remote_name - init_submodules - git_submodules_recurse - git_submodules - git_repository - work_dir - git_update_strategy - tls_verify -) - - if("${git_tag}" STREQUAL "") - message(FATAL_ERROR "Tag for git checkout should not be empty.") - endif() - set(git_stash_save_options --quiet) - if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7) - # This avoids stashing files covered by .gitignore - list(APPEND git_stash_save_options --include-untracked) - elseif(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.6) - # Untracked files, but also ignored files, so potentially slower - list(APPEND git_stash_save_options --all) - endif() - - set(git_submodules_config_options "") - if(NOT "x${tls_verify}" STREQUAL "x") - # The submodules config option is not sticky, git doesn't provide any way - # to do that. We have to pass this config option for the update step too. - # We don't need to set it for the non-submodule update because it gets - # recorded as part of the clone operation in a sticky manner. - if(tls_verify) - # Default git behavior is "true", but the user might have changed the - # global default to "false". Since TLS_VERIFY was given, ensure we honor - # the specified setting regardless of what the global default might be. - set(git_submodules_config_options -c http.sslVerify=true) - else() - set(git_submodules_config_options -c http.sslVerify=false) - endif() - endif() - - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitupdate.cmake.in" - "${script_filename}" - @ONLY - ) -endfunction() - -function(_ep_write_downloadfile_script - script_filename - REMOTE - LOCAL - timeout - inactivity_timeout - no_progress - hash - tls_verify - tls_cainfo - userpwd - http_headers - netrc - netrc_file -) - if("x${REMOTE}" STREQUAL "x") - message(FATAL_ERROR "REMOTE can't be empty") - endif() - if("x${LOCAL}" STREQUAL "x") - message(FATAL_ERROR "LOCAL can't be empty") - endif() - - # REMOTE could contain special characters that parse as separate arguments. - # Things like parentheses are legitimate characters in a URL, but would be - # seen as the start of a new unquoted argument by the cmake language parser. - # Avoid those special cases by preparing quoted strings for direct inclusion - # in the foreach() call that iterates over the set of URLs in REMOTE. - set(REMOTE "[====[${REMOTE}]====]") - string(REPLACE ";" "]====] [====[" REMOTE "${REMOTE}") - - if(timeout) - set(TIMEOUT_ARGS TIMEOUT ${timeout}) - set(TIMEOUT_MSG "${timeout} seconds") - else() - set(TIMEOUT_ARGS "# no TIMEOUT") - set(TIMEOUT_MSG "none") - endif() - if(inactivity_timeout) - set(INACTIVITY_TIMEOUT_ARGS INACTIVITY_TIMEOUT ${inactivity_timeout}) - set(INACTIVITY_TIMEOUT_MSG "${inactivity_timeout} seconds") - else() - set(INACTIVITY_TIMEOUT_ARGS "# no INACTIVITY_TIMEOUT") - set(INACTIVITY_TIMEOUT_MSG "none") - endif() - - if(no_progress) - set(SHOW_PROGRESS "") - else() - set(SHOW_PROGRESS "SHOW_PROGRESS") - endif() - - _ep_get_hash_regex(_ep_hash_regex) - if("${hash}" MATCHES "${_ep_hash_regex}") - set(ALGO "${CMAKE_MATCH_1}") - string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE) - else() - set(ALGO "") - set(EXPECT_VALUE "") - endif() - - set(TLS_VERIFY_CODE "") - set(TLS_CAINFO_CODE "") - set(NETRC_CODE "") - set(NETRC_FILE_CODE "") - - # check for curl globals in the project - if(DEFINED CMAKE_TLS_VERIFY) - set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY ${CMAKE_TLS_VERIFY})") - endif() - if(DEFINED CMAKE_TLS_CAINFO) - set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${CMAKE_TLS_CAINFO}\")") - endif() - if(DEFINED CMAKE_NETRC) - set(NETRC_CODE "set(CMAKE_NETRC \"${CMAKE_NETRC}\")") - endif() - if(DEFINED CMAKE_NETRC_FILE) - set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${CMAKE_NETRC_FILE}\")") - endif() - - # now check for curl locals so that the local values - # will override the globals - - # check for tls_verify argument - string(LENGTH "${tls_verify}" tls_verify_len) - if(tls_verify_len GREATER 0) - set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY ${tls_verify})") - endif() - # check for tls_cainfo argument - string(LENGTH "${tls_cainfo}" tls_cainfo_len) - if(tls_cainfo_len GREATER 0) - set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${tls_cainfo}\")") - endif() - # check for netrc argument - string(LENGTH "${netrc}" netrc_len) - if(netrc_len GREATER 0) - set(NETRC_CODE "set(CMAKE_NETRC \"${netrc}\")") - endif() - # check for netrc_file argument - string(LENGTH "${netrc_file}" netrc_file_len) - if(netrc_file_len GREATER 0) - set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${netrc_file}\")") - endif() - - if(userpwd STREQUAL ":") - set(USERPWD_ARGS) - else() - set(USERPWD_ARGS USERPWD "${userpwd}") - endif() - - set(HTTP_HEADERS_ARGS "") - if(NOT http_headers STREQUAL "") - foreach(header IN LISTS http_headers) - string(PREPEND HTTP_HEADERS_ARGS - "HTTPHEADER \"${header}\"\n " - ) - endforeach() - endif() - - # Used variables: - # * TLS_VERIFY_CODE - # * TLS_CAINFO_CODE - # * ALGO - # * EXPECT_VALUE - # * REMOTE - # * LOCAL - # * SHOW_PROGRESS - # * TIMEOUT_ARGS - # * TIMEOUT_MSG - # * USERPWD_ARGS - # * HTTP_HEADERS_ARGS - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/download.cmake.in" - "${script_filename}" - @ONLY - ) -endfunction() - -function(_ep_write_verifyfile_script - script_filename - LOCAL - hash -) - _ep_get_hash_regex(_ep_hash_regex) - if("${hash}" MATCHES "${_ep_hash_regex}") - set(ALGO "${CMAKE_MATCH_1}") - string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE) - else() - set(ALGO "") - set(EXPECT_VALUE "") - endif() - - # Used variables: - # * ALGO - # * EXPECT_VALUE - # * LOCAL - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/verify.cmake.in" - "${script_filename}" - @ONLY - ) -endfunction() - - -function(_ep_write_extractfile_script - script_filename - name - filename - directory options -) - set(args "") - - if(filename MATCHES - "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$") - set(args xfz) - endif() - - if(filename MATCHES "(\\.|=)tar$") - set(args xf) - endif() - - if(args STREQUAL "") - message(FATAL_ERROR - "Do not know how to extract '${filename}' -- known types are: " - ".7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip" - ) - endif() - - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in" - "${script_filename}" - @ONLY - ) -endfunction() - - function(_ep_set_directories name) get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX) if(NOT prefix) @@ -1761,6 +1354,7 @@ endif() file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir) set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}") + set(_EP_${VAR}_DIR "${${var}_dir}" PARENT_SCOPE) endforeach() # Special case for default log directory based on stamp directory. @@ -1773,10 +1367,12 @@ endif() file(TO_CMAKE_PATH "${log_dir}" log_dir) set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}") + set(_EP_LOG_DIR "${log_dir}" PARENT_SCOPE) get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR) if(NOT source_subdir) set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "") + set(_EP_SOURCE_SUBDIR "" PARENT_SCOPE) elseif(IS_ABSOLUTE "${source_subdir}") message(FATAL_ERROR "External project ${name} has non-relative SOURCE_SUBDIR!" @@ -1786,6 +1382,7 @@ # behaves as expected. file(TO_CMAKE_PATH "${source_subdir}" source_subdir) set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}") + set(_EP_SOURCE_SUBDIR "/${source_subdir}" PARENT_SCOPE) endif() if(build_in_source) get_property(source_dir TARGET ${name} PROPERTY _EP_SOURCE_DIR) @@ -1793,10 +1390,12 @@ set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}/${source_subdir}" ) + set(_EP_BINARY_DIR "${source_dir}/${source_subdir}" PARENT_SCOPE) else() set_property(TARGET ${name} PROPERTY _EP_BINARY_DIR "${source_dir}" ) + set(_EP_BINARY_DIR "${source_dir}" PARENT_SCOPE) endif() endif() @@ -2055,6 +1654,7 @@ set(${cmd_var} "${cmd}" PARENT_SCOPE) endfunction() + function(_ep_write_log_script name step @@ -2218,6 +1818,7 @@ set(${cmd_var} "${command}" PARENT_SCOPE) endfunction() + # On multi-config generators, provide a placeholder for a per-config subdir. # On single-config generators, this is empty. function(_ep_get_configuration_subdir_genex suffix_var) @@ -2413,6 +2014,10 @@ endfunction() +# While this function is referenced in shared_internal_commands.cmake in a few +# places, all of those code paths will only be reached by calling one of the +# functions defined in this file. Keep it here, since it is part of the public +# interface of ExternalProject. function(ExternalProject_Add_Step name step) get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114) _ep_get_complete_stampfile(${name} complete_stamp_file) @@ -2802,908 +2407,6 @@ endfunction() -function(_ep_is_dir_empty dir empty_var) - file(GLOB gr "${dir}/*") - if("${gr}" STREQUAL "") - set(${empty_var} 1 PARENT_SCOPE) - else() - set(${empty_var} 0 PARENT_SCOPE) - endif() -endfunction() - -function(_ep_get_git_submodules_recurse git_submodules_recurse) - # Checks for GIT_SUBMODULES_RECURSE property. Default is ON, which sets - # git_submodules_recurse output variable to "--recursive". Otherwise, the - # output variable is set to an empty value "". - get_property(git_submodules_recurse_set - TARGET ${name} - PROPERTY _EP_GIT_SUBMODULES_RECURSE - SET - ) - if(NOT git_submodules_recurse_set) - set(recurseFlag "--recursive") - else() - get_property(git_submodules_recurse_value - TARGET ${name} - PROPERTY _EP_GIT_SUBMODULES_RECURSE - ) - if(git_submodules_recurse_value) - set(recurseFlag "--recursive") - else() - set(recurseFlag "") - endif() - endif() - set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE) - - # The git submodule update '--recursive' flag requires git >= v1.6.5 - if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) - message(FATAL_ERROR - "git version 1.6.5 or later required for --recursive flag with " - "'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'" - ) - endif() -endfunction() - - -function(_ep_add_download_command name) - ExternalProject_Get_Property(${name} - source_dir - stamp_dir - download_dir - tmp_dir - ) - - get_property(cmd_set TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND SET) - get_property(cmd TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND) - get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) - get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) - get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) - get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) - get_property(url TARGET ${name} PROPERTY _EP_URL) - get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME) - - # TODO: Perhaps file:// should be copied to download dir before extraction. - string(REGEX REPLACE "file://" "" url "${url}") - - set(depends) - set(comment) - set(work_dir) - set(extra_repo_info) - - if(cmd_set) - set(work_dir ${download_dir}) - set(method custom) - elseif(cvs_repository) - set(method cvs) - find_package(CVS QUIET) - if(NOT CVS_EXECUTABLE) - message(FATAL_ERROR "error: could not find cvs for checkout of ${name}") - endif() - - get_target_property(cvs_module ${name} _EP_CVS_MODULE) - if(NOT cvs_module) - message(FATAL_ERROR "error: no CVS_MODULE") - endif() - - get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) - get_filename_component(src_name "${source_dir}" NAME) - get_filename_component(work_dir "${source_dir}" PATH) - set(comment "Performing download step (CVS checkout) for '${name}'") - set(cmd - ${CVS_EXECUTABLE} - -d ${cvs_repository} - -q - co ${cvs_tag} - -d ${src_name} - ${cvs_module} - ) - - elseif(svn_repository) - set(method svn) - find_package(Subversion QUIET) - if(NOT Subversion_SVN_EXECUTABLE) - message(FATAL_ERROR "error: could not find svn for checkout of ${name}") - endif() - - get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) - get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) - get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) - get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) - get_property(uses_terminal - TARGET ${name} - PROPERTY _EP_USES_TERMINAL_DOWNLOAD - ) - # The --trust-server-cert option requires --non-interactive - if(uses_terminal AND NOT svn_trust_cert) - set(svn_interactive_args "") - else() - set(svn_interactive_args "--non-interactive") - endif() - - get_filename_component(src_name "${source_dir}" NAME) - get_filename_component(work_dir "${source_dir}" PATH) - set(comment "Performing download step (SVN checkout) for '${name}'") - set(svn_user_pw_args "") - if(DEFINED svn_username) - set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") - endif() - if(DEFINED svn_password) - set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") - endif() - if(svn_trust_cert) - set(svn_trust_cert_args --trust-server-cert) - endif() - set(cmd - ${Subversion_SVN_EXECUTABLE} - co - ${svn_repository} - ${svn_revision} - ${svn_interactive_args} - ${svn_trust_cert_args} - ${svn_user_pw_args} - ${src_name} - ) - - elseif(git_repository) - set(method git) - # FetchContent gives us these directly, so don't try to recompute them - if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) - unset(CMAKE_MODULE_PATH) # Use CMake builtin find module - find_package(Git QUIET) - if(NOT GIT_EXECUTABLE) - message(FATAL_ERROR "error: could not find git for clone of ${name}") - endif() - endif() - - _ep_get_git_submodules_recurse(git_submodules_recurse) - - get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG) - if(NOT git_tag) - set(git_tag "master") - endif() - - set(git_init_submodules TRUE) - get_property(git_submodules_set - TARGET ${name} - PROPERTY _EP_GIT_SUBMODULES SET - ) - if(git_submodules_set) - get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES) - if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") - set(git_init_submodules FALSE) - endif() - endif() - - get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME) - if(NOT git_remote_name) - set(git_remote_name "origin") - endif() - - get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) - if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY) - set(tls_verify "${CMAKE_TLS_VERIFY}") - endif() - get_property(git_shallow TARGET ${name} PROPERTY _EP_GIT_SHALLOW) - get_property(git_progress TARGET ${name} PROPERTY _EP_GIT_PROGRESS) - get_property(git_config TARGET ${name} PROPERTY _EP_GIT_CONFIG) - - # If git supports it, make checkouts quiet when checking out a git hash. - # This avoids the very noisy detached head message. - if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7) - list(PREPEND git_config advice.detachedHead=false) - endif() - - # The command doesn't expose any details, so we need to record additional - # information in the RepositoryInfo.txt file. For the download step, only - # the things specifically affecting the clone operation should be recorded. - # If the repo changes, the clone script should be run again. - # But if only the tag changes, avoid running the clone script again. - # Let the 'always' running update step checkout the new tag. - # - set(extra_repo_info -"repository=${git_repository} -remote=${git_remote_name} -init_submodules=${git_init_submodules} -recurse_submodules=${git_submodules_recurse} -submodules=${git_submodules} -CMP0097=${_EP_CMP0097} -") - get_filename_component(src_name "${source_dir}" NAME) - get_filename_component(work_dir "${source_dir}" PATH) - - # 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. - # - _ep_write_gitclone_script( - ${tmp_dir}/${name}-gitclone.cmake - ${source_dir} - ${GIT_EXECUTABLE} - ${git_repository} - ${git_tag} - ${git_remote_name} - ${git_init_submodules} - "${git_submodules_recurse}" - "${git_submodules}" - "${git_shallow}" - "${git_progress}" - "${git_config}" - ${src_name} - ${work_dir} - ${stamp_dir}/${name}-gitinfo.txt - ${stamp_dir}/${name}-gitclone-lastrun.txt - "${tls_verify}" - ) - set(comment "Performing download step (git clone) for '${name}'") - set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake) - - elseif(hg_repository) - set(method hg) - find_package(Hg QUIET) - if(NOT HG_EXECUTABLE) - message(FATAL_ERROR "error: could not find hg for clone of ${name}") - endif() - - get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG) - if(NOT hg_tag) - set(hg_tag "tip") - endif() - - # The command doesn't expose any details, so we need to record additional - # information in the RepositoryInfo.txt file. For the download step, only - # the things specifically affecting the clone operation should be recorded. - # If the repo changes, the clone script should be run again. - # But if only the tag changes, avoid running the clone script again. - # Let the 'always' running update step checkout the new tag. - # - set(extra_repo_info "repository=${hg_repository}") - get_filename_component(src_name "${source_dir}" NAME) - get_filename_component(work_dir "${source_dir}" PATH) - - # Since hg 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 hg clone. - # - _ep_write_hgclone_script( - ${tmp_dir}/${name}-hgclone.cmake - ${source_dir} - ${HG_EXECUTABLE} - ${hg_repository} - ${hg_tag} - ${src_name} - ${work_dir} - ${stamp_dir}/${name}-hginfo.txt - ${stamp_dir}/${name}-hgclone-lastrun.txt - ) - set(comment "Performing download step (hg clone) for '${name}'") - set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-hgclone.cmake) - - elseif(url) - set(method url) - get_filename_component(work_dir "${source_dir}" PATH) - get_property(hash TARGET ${name} PROPERTY _EP_URL_HASH) - _ep_get_hash_regex(_ep_hash_regex) - if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}") - _ep_get_hash_algos(_ep_hash_algos) - list(JOIN _ep_hash_algos "|" _ep_hash_algos) - message(FATAL_ERROR - "URL_HASH is set to\n" - " ${hash}\n" - "but must be ALGO=value where ALGO is\n" - " ${_ep_hash_algos}\n" - "and value is a hex string." - ) - endif() - get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5) - if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}") - message(FATAL_ERROR - "URL_MD5 is set to\n" - " ${md5}\n" - "but must be a hex string." - ) - endif() - if(md5 AND NOT hash) - set(hash "MD5=${md5}") - endif() - set(extra_repo_info -"url(s)=${url} -hash=${hash} -") - - list(LENGTH url url_list_length) - if(NOT "${url_list_length}" STREQUAL "1") - foreach(entry IN LISTS url) - if(NOT "${entry}" MATCHES "^[a-z]+://") - message(FATAL_ERROR - "At least one entry of URL is a path (invalid in a list)" - ) - endif() - endforeach() - if("x${fname}" STREQUAL "x") - list(GET url 0 fname) - endif() - endif() - - if(IS_DIRECTORY "${url}") - get_filename_component(abs_dir "${url}" ABSOLUTE) - set(comment "Performing download step (DIR copy) for '${name}'") - set(cmd - ${CMAKE_COMMAND} -E rm -rf ${source_dir} - COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir} - ) - else() - get_property(no_extract - TARGET "${name}" - PROPERTY _EP_DOWNLOAD_NO_EXTRACT - ) - string(APPEND extra_repo_info "no_extract=${no_extract}\n") - if("${url}" MATCHES "^[a-z]+://") - # TODO: Should download and extraction be different steps? - if("x${fname}" STREQUAL "x") - set(fname "${url}") - endif() - set(ext_regex [[7z|tar|tar\.bz2|tar\.gz|tar\.xz|tbz2|tgz|txz|zip]]) - if("${fname}" MATCHES "([^/\\?#]+(\\.|=)(${ext_regex}))([/?#].*)?$") - set(fname "${CMAKE_MATCH_1}") - elseif(no_extract) - get_filename_component(fname "${fname}" NAME) - else() - # Fall back to a default file name. The actual file name does not - # matter because it is used only internally and our extraction tool - # inspects the file content directly. If it turns out the wrong URL - # was given that will be revealed during the build which is an easier - # place for users to diagnose than an error here anyway. - set(fname "archive.tar") - endif() - string(REPLACE ";" "-" fname "${fname}") - set(file ${download_dir}/${fname}) - get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT) - get_property(inactivity_timeout - TARGET ${name} - PROPERTY _EP_INACTIVITY_TIMEOUT - ) - get_property(no_progress - TARGET ${name} - PROPERTY _EP_DOWNLOAD_NO_PROGRESS - ) - get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) - get_property(tls_cainfo TARGET ${name} PROPERTY _EP_TLS_CAINFO) - get_property(netrc TARGET ${name} PROPERTY _EP_NETRC) - get_property(netrc_file TARGET ${name} PROPERTY _EP_NETRC_FILE) - get_property(http_username TARGET ${name} PROPERTY _EP_HTTP_USERNAME) - get_property(http_password TARGET ${name} PROPERTY _EP_HTTP_PASSWORD) - get_property(http_headers TARGET ${name} PROPERTY _EP_HTTP_HEADER) - set(download_script "${stamp_dir}/download-${name}.cmake") - _ep_write_downloadfile_script( - "${download_script}" - "${url}" - "${file}" - "${timeout}" - "${inactivity_timeout}" - "${no_progress}" - "${hash}" - "${tls_verify}" - "${tls_cainfo}" - "${http_username}:${http_password}" - "${http_headers}" - "${netrc}" - "${netrc_file}" - ) - set(cmd - ${CMAKE_COMMAND} -P "${download_script}" - COMMAND - ) - if (no_extract) - set(steps "download and verify") - else () - set(steps "download, verify and extract") - endif () - set(comment "Performing download step (${steps}) for '${name}'") - # already verified by 'download_script' - file(WRITE "${stamp_dir}/verify-${name}.cmake" "") - - # Rather than adding everything to the RepositoryInfo.txt file, it is - # more robust to just depend on the download script. That way, we will - # re-download if any aspect of the download changes. - list(APPEND depends "${download_script}") - else() - set(file "${url}") - if (no_extract) - set(steps "verify") - else () - set(steps "verify and extract") - endif () - set(comment "Performing download step (${steps}) for '${name}'") - _ep_write_verifyfile_script( - "${stamp_dir}/verify-${name}.cmake" - "${file}" - "${hash}" - ) - endif() - list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake) - get_target_property(extract_timestamp ${name} - _EP_DOWNLOAD_EXTRACT_TIMESTAMP - ) - if(no_extract) - if(NOT extract_timestamp STREQUAL "extract_timestamp-NOTFOUND") - message(FATAL_ERROR - "Cannot specify DOWNLOAD_EXTRACT_TIMESTAMP when using " - "DOWNLOAD_NO_EXTRACT TRUE" - ) - endif() - set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) - else() - if(extract_timestamp STREQUAL "extract_timestamp-NOTFOUND") - # Default depends on policy CMP0135 - if(_EP_CMP0135 STREQUAL "") - message(AUTHOR_WARNING - "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 the time of extraction, otherwise " - "code that depends on the extracted contents might not be " - "rebuilt if the URL changes. The OLD behavior preserves the " - "timestamps from the archive instead, but this is usually not " - "what you want. Update your project to the NEW behavior or " - "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of " - "true to avoid this robustness issue." - ) - set(extract_timestamp TRUE) - elseif(_EP_CMP0135 STREQUAL "NEW") - set(extract_timestamp FALSE) - else() - set(extract_timestamp TRUE) - endif() - endif() - if(extract_timestamp) - set(options "") - else() - set(options "--touch") - endif() - _ep_write_extractfile_script( - "${stamp_dir}/extract-${name}.cmake" - "${name}" - "${file}" - "${source_dir}" - "${options}" - ) - list(APPEND cmd - COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake - ) - endif () - endif() - else() - set(method source_dir) - _ep_is_dir_empty("${source_dir}" empty) - if(${empty}) - message(FATAL_ERROR - "No download info given for '${name}' and its source directory:\n" - " ${source_dir}\n" - "is not an existing non-empty directory. Please specify one of:\n" - " * SOURCE_DIR with an existing non-empty directory\n" - " * DOWNLOAD_COMMAND\n" - " * URL\n" - " * GIT_REPOSITORY\n" - " * SVN_REPOSITORY\n" - " * HG_REPOSITORY\n" - " * CVS_REPOSITORY and CVS_MODULE" - ) - endif() - endif() - - # We use configure_file() to write the repo_info_file so that the file's - # timestamp is not updated if we don't change the contents - - set(repo_info_file ${stamp_dir}/${name}-${method}info.txt) - list(APPEND depends ${repo_info_file}) - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in" - "${repo_info_file}" - @ONLY - ) - - get_property(log - TARGET ${name} - PROPERTY _EP_LOG_DOWNLOAD - ) - if(log) - set(log LOG 1) - else() - set(log "") - endif() - - get_property(uses_terminal - TARGET ${name} - PROPERTY _EP_USES_TERMINAL_DOWNLOAD - ) - if(uses_terminal) - set(uses_terminal USES_TERMINAL 1) - else() - set(uses_terminal "") - endif() - - set(__cmdQuoted) - foreach(__item IN LISTS cmd) - string(APPEND __cmdQuoted " [==[${__item}]==]") - endforeach() - cmake_language(EVAL CODE " - ExternalProject_Add_Step(\${name} download - INDEPENDENT TRUE - COMMENT \${comment} - COMMAND ${__cmdQuoted} - WORKING_DIRECTORY \${work_dir} - DEPENDS \${depends} - DEPENDEES mkdir - ${log} - ${uses_terminal} - )" - ) -endfunction() - -function(_ep_get_update_disconnected var name) - get_property(update_disconnected_set - TARGET ${name} - PROPERTY _EP_UPDATE_DISCONNECTED - SET - ) - if(update_disconnected_set) - get_property(update_disconnected - TARGET ${name} - PROPERTY _EP_UPDATE_DISCONNECTED - ) - else() - get_property(update_disconnected - DIRECTORY - PROPERTY EP_UPDATE_DISCONNECTED - ) - endif() - set(${var} "${update_disconnected}" PARENT_SCOPE) -endfunction() - -function(_ep_add_update_command name) - ExternalProject_Get_Property(${name} source_dir stamp_dir tmp_dir) - - get_property(cmd_set TARGET ${name} PROPERTY _EP_UPDATE_COMMAND SET) - get_property(cmd TARGET ${name} PROPERTY _EP_UPDATE_COMMAND) - get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY) - get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY) - get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY) - get_property(hg_repository TARGET ${name} PROPERTY _EP_HG_REPOSITORY ) - - _ep_get_update_disconnected(update_disconnected ${name}) - - set(work_dir) - set(comment) - set(always) - set(file_deps) - - if(cmd_set) - set(work_dir ${source_dir}) - if(NOT "x${cmd}" STREQUAL "x") - set(always 1) - endif() - elseif(cvs_repository) - if(NOT CVS_EXECUTABLE) - message(FATAL_ERROR "error: could not find cvs for update of ${name}") - endif() - set(work_dir ${source_dir}) - set(comment "Performing update step (CVS update) for '${name}'") - get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG) - set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag}) - set(always 1) - elseif(svn_repository) - if(NOT Subversion_SVN_EXECUTABLE) - message(FATAL_ERROR "error: could not find svn for update of ${name}") - endif() - set(work_dir ${source_dir}) - set(comment "Performing update step (SVN update) for '${name}'") - get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION) - get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME) - get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD) - get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT) - get_property(uses_terminal TARGET ${name} PROPERTY _EP_USES_TERMINAL_UPDATE) - # The --trust-server-cert option requires --non-interactive - if(uses_terminal AND NOT svn_trust_cert) - set(svn_interactive_args "") - else() - set(svn_interactive_args "--non-interactive") - endif() - set(svn_user_pw_args "") - if(DEFINED svn_username) - set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") - endif() - if(DEFINED svn_password) - set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") - endif() - if(svn_trust_cert) - set(svn_trust_cert_args --trust-server-cert) - endif() - set(cmd - ${Subversion_SVN_EXECUTABLE} - up - ${svn_revision} - ${svn_interactive_args} - ${svn_trust_cert_args} - ${svn_user_pw_args} - ) - set(always 1) - elseif(git_repository) - # FetchContent gives us these directly, so don't try to recompute them - if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) - unset(CMAKE_MODULE_PATH) # Use CMake builtin find module - find_package(Git QUIET) - if(NOT GIT_EXECUTABLE) - message(FATAL_ERROR "error: could not find git for fetch of ${name}") - endif() - endif() - set(work_dir ${source_dir}) - set(comment "Performing update step for '${name}'") - set(comment_disconnected "Performing disconnected update step for '${name}'") - - get_property(git_tag - TARGET ${name} - PROPERTY _EP_GIT_TAG - ) - if(NOT git_tag) - set(git_tag "master") - endif() - - get_property(git_remote_name - TARGET ${name} - PROPERTY _EP_GIT_REMOTE_NAME - ) - if(NOT git_remote_name) - set(git_remote_name "origin") - endif() - - set(git_init_submodules TRUE) - get_property(git_submodules_set - TARGET ${name} - PROPERTY _EP_GIT_SUBMODULES - SET - ) - if(git_submodules_set) - get_property(git_submodules - TARGET ${name} - PROPERTY _EP_GIT_SUBMODULES - ) - if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") - set(git_init_submodules FALSE) - endif() - endif() - - get_property(git_update_strategy - TARGET ${name} - PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY - ) - if(NOT git_update_strategy) - set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") - endif() - if(NOT git_update_strategy) - set(git_update_strategy REBASE) - endif() - set(strategies CHECKOUT REBASE REBASE_CHECKOUT) - if(NOT git_update_strategy IN_LIST strategies) - message(FATAL_ERROR - "'${git_update_strategy}' is not one of the supported strategies: " - "${strategies}" - ) - endif() - - _ep_get_git_submodules_recurse(git_submodules_recurse) - - get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY) - if("x${tls_verify}" STREQUAL "x" AND DEFINED CMAKE_TLS_VERIFY) - set(tls_verify "${CMAKE_TLS_VERIFY}") - endif() - - set(update_script "${tmp_dir}/${name}-gitupdate.cmake") - list(APPEND file_deps ${update_script}) - _ep_write_gitupdate_script( - "${update_script}" - "${GIT_EXECUTABLE}" - "${git_tag}" - "${git_remote_name}" - "${git_init_submodules}" - "${git_submodules_recurse}" - "${git_submodules}" - "${git_repository}" - "${work_dir}" - "${git_update_strategy}" - "${tls_verify}" - ) - set(cmd ${CMAKE_COMMAND} -Dcan_fetch=YES -P ${update_script}) - set(cmd_disconnected ${CMAKE_COMMAND} -Dcan_fetch=NO -P ${update_script}) - set(always 1) - elseif(hg_repository) - if(NOT HG_EXECUTABLE) - message(FATAL_ERROR "error: could not find hg for pull of ${name}") - endif() - set(work_dir ${source_dir}) - set(comment "Performing update step (hg pull) for '${name}'") - set(comment_disconnected "Performing disconnected update step for '${name}'") - - get_property(hg_tag - TARGET ${name} - PROPERTY _EP_HG_TAG - ) - if(NOT hg_tag) - set(hg_tag "tip") - endif() - - if("${HG_VERSION_STRING}" STREQUAL "2.1") - set(notesAnchor - "#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X" - ) - message(WARNING -"Mercurial 2.1 does not distinguish an empty pull from a failed pull: - http://mercurial.selenic.com/wiki/UpgradeNotes${notesAnchor} - http://thread.gmane.org/gmane.comp.version-control.mercurial.devel/47656 -Update to Mercurial >= 2.1.1. -") - endif() - - set(cmd - ${HG_EXECUTABLE} pull - COMMAND ${HG_EXECUTABLE} update ${hg_tag} - ) - set(cmd_disconnected ${HG_EXECUTABLE} update ${hg_tag}) - set(always 1) - endif() - - # We use configure_file() to write the update_info_file so that the file's - # timestamp is not updated if we don't change the contents - if(NOT DEFINED cmd_disconnected) - set(cmd_disconnected "${cmd}") - endif() - set(update_info_file ${stamp_dir}/${name}-update-info.txt) - list(APPEND file_deps ${update_info_file}) - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/UpdateInfo.txt.in" - "${update_info_file}" - @ONLY - ) - - get_property(log - TARGET ${name} - PROPERTY _EP_LOG_UPDATE - ) - if(log) - set(log LOG 1) - else() - set(log "") - endif() - - get_property(uses_terminal - TARGET ${name} - PROPERTY _EP_USES_TERMINAL_UPDATE - ) - if(uses_terminal) - set(uses_terminal USES_TERMINAL 1) - else() - set(uses_terminal "") - endif() - - set(__cmdQuoted) - foreach(__item IN LISTS cmd) - string(APPEND __cmdQuoted " [==[${__item}]==]") - endforeach() - cmake_language(EVAL CODE " - ExternalProject_Add_Step(${name} update - INDEPENDENT TRUE - COMMENT \${comment} - COMMAND ${__cmdQuoted} - ALWAYS \${always} - EXCLUDE_FROM_MAIN \${update_disconnected} - WORKING_DIRECTORY \${work_dir} - DEPENDEES download - DEPENDS \${file_deps} - ${log} - ${uses_terminal} - )" - ) - if(update_disconnected) - if(NOT DEFINED comment_disconnected) - set(comment_disconnected "${comment}") - endif() - set(__cmdQuoted) - foreach(__item IN LISTS cmd_disconnected) - string(APPEND __cmdQuoted " [==[${__item}]==]") - endforeach() - - cmake_language(EVAL CODE " - ExternalProject_Add_Step(${name} update_disconnected - INDEPENDENT TRUE - COMMENT \${comment_disconnected} - COMMAND ${__cmdQuoted} - WORKING_DIRECTORY \${work_dir} - DEPENDEES download - DEPENDS \${file_deps} - ${log} - ${uses_terminal} - )" - ) - endif() - -endfunction() - - -function(_ep_add_patch_command name) - ExternalProject_Get_Property(${name} source_dir stamp_dir) - - get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET) - get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND) - - set(work_dir) - - if(cmd_set) - set(work_dir ${source_dir}) - endif() - - # We use configure_file() to write the patch_info_file so that the file's - # timestamp is not updated if we don't change the contents - set(patch_info_file ${stamp_dir}/${name}-patch-info.txt) - configure_file( - "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/PatchInfo.txt.in" - "${patch_info_file}" - @ONLY - ) - - get_property(log - TARGET ${name} - PROPERTY _EP_LOG_PATCH - ) - if(log) - set(log LOG 1) - else() - set(log "") - endif() - - get_property(uses_terminal - TARGET ${name} - PROPERTY _EP_USES_TERMINAL_PATCH - ) - if(uses_terminal) - set(uses_terminal USES_TERMINAL 1) - else() - set(uses_terminal "") - endif() - - _ep_get_update_disconnected(update_disconnected ${name}) - - set(__cmdQuoted) - foreach(__item IN LISTS cmd) - string(APPEND __cmdQuoted " [==[${__item}]==]") - endforeach() - cmake_language(EVAL CODE " - ExternalProject_Add_Step(${name} patch - INDEPENDENT TRUE - COMMAND ${__cmdQuoted} - WORKING_DIRECTORY \${work_dir} - EXCLUDE_FROM_MAIN \${update_disconnected} - DEPENDEES update - DEPENDS \${patch_info_file} - ${log} - ${uses_terminal} - )" - ) - - if(update_disconnected) - cmake_language(EVAL CODE " - ExternalProject_Add_Step(${name} patch_disconnected - INDEPENDENT TRUE - COMMAND ${__cmdQuoted} - WORKING_DIRECTORY \${work_dir} - DEPENDEES update_disconnected - DEPENDS \${patch_info_file} - ${log} - ${uses_terminal} - )" - ) - endif() - -endfunction() - function(_ep_get_file_deps var name) set(file_deps) @@ -3731,6 +2434,7 @@ set("${var}" "${file_deps}" PARENT_SCOPE) endfunction() + function(_ep_extract_configure_command var name) get_property(cmd_set TARGET ${name} @@ -3886,6 +2590,7 @@ set("${var}" "${cmd}" PARENT_SCOPE) endfunction() + # TODO: Make sure external projects use the proper compiler function(_ep_add_configure_command name) ExternalProject_Get_Property(${name} binary_dir tmp_dir) @@ -4254,143 +2959,7 @@ _EP_CMP0114 "${cmp0114}" ) - set(keywords - # - # Directory options - # - PREFIX - TMP_DIR - STAMP_DIR - LOG_DIR - DOWNLOAD_DIR - SOURCE_DIR - BINARY_DIR - INSTALL_DIR - # - # Download step options - # - DOWNLOAD_COMMAND - # - URL - URL_HASH - URL_MD5 - DOWNLOAD_NAME - DOWNLOAD_EXTRACT_TIMESTAMP - DOWNLOAD_NO_EXTRACT - DOWNLOAD_NO_PROGRESS - TIMEOUT - INACTIVITY_TIMEOUT - HTTP_USERNAME - HTTP_PASSWORD - HTTP_HEADER - TLS_VERIFY # Also used for git clone operations - TLS_CAINFO - NETRC - NETRC_FILE - # - GIT_REPOSITORY - GIT_TAG - GIT_REMOTE_NAME - GIT_SUBMODULES - GIT_SUBMODULES_RECURSE - GIT_SHALLOW - GIT_PROGRESS - GIT_CONFIG - GIT_REMOTE_UPDATE_STRATEGY - # - SVN_REPOSITORY - SVN_REVISION - SVN_USERNAME - SVN_PASSWORD - SVN_TRUST_CERT - # - HG_REPOSITORY - HG_TAG - # - CVS_REPOSITORY - CVS_MODULE - CVS_TAG - # - # Update step options - # - UPDATE_COMMAND - UPDATE_DISCONNECTED - # - # Patch step options - # - PATCH_COMMAND - # - # Configure step options - # - CONFIGURE_COMMAND - CMAKE_COMMAND - CMAKE_GENERATOR - CMAKE_GENERATOR_PLATFORM - CMAKE_GENERATOR_TOOLSET - CMAKE_GENERATOR_INSTANCE - CMAKE_ARGS - CMAKE_CACHE_ARGS - CMAKE_CACHE_DEFAULT_ARGS - SOURCE_SUBDIR - CONFIGURE_HANDLED_BY_BUILD - # - # Build step options - # - BUILD_COMMAND - BUILD_IN_SOURCE - BUILD_ALWAYS - BUILD_BYPRODUCTS - BUILD_JOB_SERVER_AWARE - # - # Install step options - # - INSTALL_COMMAND - INSTALL_BYPRODUCTS - # - # Test step options - # - TEST_COMMAND - TEST_BEFORE_INSTALL - TEST_AFTER_INSTALL - TEST_EXCLUDE_FROM_MAIN - # - # Logging options - # - LOG_DOWNLOAD - LOG_UPDATE - LOG_PATCH - LOG_CONFIGURE - LOG_BUILD - LOG_INSTALL - LOG_TEST - LOG_MERGED_STDOUTERR - LOG_OUTPUT_ON_FAILURE - # - # Terminal access options - # - USES_TERMINAL_DOWNLOAD - USES_TERMINAL_UPDATE - USES_TERMINAL_PATCH - USES_TERMINAL_CONFIGURE - USES_TERMINAL_BUILD - USES_TERMINAL_INSTALL - USES_TERMINAL_TEST - # - # Target options - # - DEPENDS - EXCLUDE_FROM_ALL - STEP_TARGETS - INDEPENDENT_STEP_TARGETS - # - # Miscellaneous options - # - LIST_SEPARATOR - # - # Internal options (undocumented) - # - EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR - ) + _ep_get_add_keywords(keywords) _ep_parse_arguments( ExternalProject_Add "${keywords}" @@ -4420,6 +2989,7 @@ get_filename_component(work_dir "${source_dir}" PATH) _ep_resolve_git_remote(resolved_git_repository "${repo}" "${cmp0150}" "${work_dir}") set_property(TARGET ${name} PROPERTY _EP_GIT_REPOSITORY ${resolved_git_repository}) + set(_EP_GIT_REPOSITORY "${resolved_git_repository}") endif() # The 'complete' step depends on all other steps and creates a
diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in index 0ad0dd3..77d43d7 100644 --- a/Modules/ExternalProject/download.cmake.in +++ b/Modules/ExternalProject/download.cmake.in
@@ -21,14 +21,14 @@ set("${has_hash}" TRUE PARENT_SCOPE) - message(STATUS "verifying file... + message(VERBOSE "verifying file... file='@LOCAL@'") file("@ALGO@" "@LOCAL@" actual_value) if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@") set("${hash_is_good}" FALSE PARENT_SCOPE) - message(STATUS "@ALGO@ hash of + message(VERBOSE "@ALGO@ hash of @LOCAL@ does not match expected value expected: '@EXPECT_VALUE@' @@ -44,7 +44,7 @@ endif() if(attempt EQUAL 1) - message(STATUS "Retrying...") + message(VERBOSE "Retrying...") return() endif() @@ -66,7 +66,7 @@ set(sleep_seconds 1200) endif() - message(STATUS "Retry after ${sleep_seconds} seconds (attempt #${attempt}) ...") + message(VERBOSE "Retry after ${sleep_seconds} seconds (attempt #${attempt}) ...") execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep "${sleep_seconds}") endfunction() @@ -75,17 +75,17 @@ check_file_hash(has_hash hash_is_good) if(has_hash) if(hash_is_good) - message(STATUS "File already exists and hash match (skip download): + message(VERBOSE "File already exists and hash match (skip download): file='@LOCAL@' @ALGO@='@EXPECT_VALUE@'" ) return() else() - message(STATUS "File already exists but hash mismatch. Removing...") + message(VERBOSE "File already exists but hash mismatch. Removing...") file(REMOVE "@LOCAL@") endif() else() - message(STATUS "File already exists but no hash specified (use URL_HASH): + message(VERBOSE "File already exists but no hash specified (use URL_HASH): file='@LOCAL@' Old file will be removed and new file downloaded from URL." ) @@ -95,12 +95,12 @@ set(retry_number 5) -message(STATUS "Downloading... +message(VERBOSE "Downloading... dst='@LOCAL@' timeout='@TIMEOUT_MSG@' inactivity timeout='@INACTIVITY_TIMEOUT_MSG@'" ) -set(download_retry_codes 7 6 8 15 28) +set(download_retry_codes 7 6 8 15 28 35) set(skip_url_list) set(status_code) foreach(i RANGE ${retry_number}) @@ -109,8 +109,9 @@ endif() foreach(url IN ITEMS @REMOTE@) if(NOT url IN_LIST skip_url_list) - message(STATUS "Using src='${url}'") + message(VERBOSE "Using src='${url}'") + @TLS_VERSION_CODE@ @TLS_VERIFY_CODE@ @TLS_CAINFO_CODE@ @NETRC_CODE@ @@ -134,10 +135,10 @@ if(status_code EQUAL 0) check_file_hash(has_hash hash_is_good) if(has_hash AND NOT hash_is_good) - message(STATUS "Hash mismatch, removing...") + message(VERBOSE "Hash mismatch, removing...") file(REMOVE "@LOCAL@") else() - message(STATUS "Downloading... done") + message(VERBOSE "Downloading... done") return() endif() else()
diff --git a/Modules/ExternalProject/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in index 984565b..39daaff 100644 --- a/Modules/ExternalProject/extractfile.cmake.in +++ b/Modules/ExternalProject/extractfile.cmake.in
@@ -8,7 +8,7 @@ get_filename_component(filename "@filename@" ABSOLUTE) get_filename_component(directory "@directory@" ABSOLUTE) -message(STATUS "extracting... +message(VERBOSE "extracting... src='${filename}' dst='${directory}'" ) @@ -28,21 +28,21 @@ # Extract it: # -message(STATUS "extracting... [tar @args@]") +message(VERBOSE "extracting... [tar @args@]") execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename} @options@ WORKING_DIRECTORY ${ut_dir} RESULT_VARIABLE rv ) if(NOT rv EQUAL 0) - message(STATUS "extracting... [error clean up]") + message(VERBOSE "extracting... [error clean up]") file(REMOVE_RECURSE "${ut_dir}") message(FATAL_ERROR "Extract of '${filename}' failed") endif() # Analyze what came out of the tar file: # -message(STATUS "extracting... [analysis]") +message(VERBOSE "extracting... [analysis]") file(GLOB contents "${ut_dir}/*") list(REMOVE_ITEM contents "${ut_dir}/.DS_Store") list(LENGTH contents n) @@ -52,14 +52,14 @@ # Move "the one" directory to the final directory: # -message(STATUS "extracting... [rename]") +message(VERBOSE "extracting... [rename]") file(REMOVE_RECURSE ${directory}) get_filename_component(contents ${contents} ABSOLUTE) file(RENAME ${contents} ${directory}) # Clean up: # -message(STATUS "extracting... [clean up]") +message(VERBOSE "extracting... [clean up]") file(REMOVE_RECURSE "${ut_dir}") -message(STATUS "extracting... done") +message(VERBOSE "extracting... done")
diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in index 94b329a..b80bcb1 100644 --- a/Modules/ExternalProject/gitclone.cmake.in +++ b/Modules/ExternalProject/gitclone.cmake.in
@@ -5,16 +5,26 @@ if(EXISTS "@gitclone_stampfile@" AND EXISTS "@gitclone_infofile@" AND "@gitclone_stampfile@" IS_NEWER_THAN "@gitclone_infofile@") - message(STATUS + message(VERBOSE "Avoiding repeated git clone, stamp file is up to date: " "'@gitclone_stampfile@'" ) return() endif() +# Even at VERBOSE level, we don't want to see the commands executed, but +# enabling them to be shown for DEBUG may be useful to help diagnose problems. +cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level) +if(active_log_level MATCHES "DEBUG|TRACE") + set(maybe_show_command COMMAND_ECHO STDOUT) +else() + set(maybe_show_command "") +endif() + execute_process( COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to remove directory: '@source_dir@'") @@ -29,11 +39,12 @@ clone @git_clone_options@ "@git_repository@" "@src_name@" WORKING_DIRECTORY "@work_dir@" RESULT_VARIABLE error_code + ${maybe_show_command} ) math(EXPR number_of_tries "${number_of_tries} + 1") endwhile() if(number_of_tries GREATER 1) - message(STATUS "Had to git clone more than once: ${number_of_tries} times.") + 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@'") @@ -44,6 +55,7 @@ checkout "@git_tag@" @git_checkout_explicit--@ WORKING_DIRECTORY "@work_dir@/@src_name@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to checkout tag: '@git_tag@'") @@ -56,6 +68,7 @@ submodule update @git_submodules_recurse@ --init @git_submodules@ WORKING_DIRECTORY "@work_dir@/@src_name@" RESULT_VARIABLE error_code + ${maybe_show_command} ) endif() if(error_code) @@ -67,6 +80,7 @@ execute_process( COMMAND ${CMAKE_COMMAND} -E copy "@gitclone_infofile@" "@gitclone_stampfile@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@gitclone_stampfile@'")
diff --git a/Modules/ExternalProject/gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in index 171aa7b..fb0c6a6 100644 --- a/Modules/ExternalProject/gitupdate.cmake.in +++ b/Modules/ExternalProject/gitupdate.cmake.in
@@ -3,12 +3,22 @@ cmake_minimum_required(VERSION 3.5) +# Even at VERBOSE level, we don't want to see the commands executed, but +# enabling them to be shown for DEBUG may be useful to help diagnose problems. +cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level) +if(active_log_level MATCHES "DEBUG|TRACE") + set(maybe_show_command COMMAND_ECHO STDOUT) +else() + set(maybe_show_command "") +endif() + function(do_fetch) message(VERBOSE "Fetching latest from the remote @git_remote_name@") execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git fetch --tags --force "@git_remote_name@" WORKING_DIRECTORY "@work_dir@" COMMAND_ERROR_IS_FATAL LAST + ${maybe_show_command} ) endfunction() @@ -34,6 +44,9 @@ message(FATAL_ERROR "Failed to get the hash for HEAD:\n${error_msg}") endif() +if("${can_fetch}" STREQUAL "") + set(can_fetch "@can_fetch_default@") +endif() execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git show-ref "@git_tag@" @@ -57,7 +70,7 @@ # FIXME: We should provide an option to always fetch for this case get_hash_for_ref("@git_tag@" tag_sha error_msg) if(tag_sha STREQUAL head_sha) - message(VERBOSE "Already at requested tag: ${tag_sha}") + message(VERBOSE "Already at requested tag: @git_tag@") return() endif() @@ -97,7 +110,7 @@ # because it can be confusing for users to see a failed git command. # That failure is being handled here, so it isn't an error. if(NOT error_msg STREQUAL "") - message(VERBOSE "${error_msg}") + message(DEBUG "${error_msg}") endif() do_fetch() set(checkout_name "@git_tag@") @@ -181,6 +194,7 @@ COMMAND "@git_EXECUTABLE@" --git-dir=.git stash save @git_stash_save_options@ WORKING_DIRECTORY "@work_dir@" COMMAND_ERROR_IS_FATAL ANY + ${maybe_show_command} ) endif() @@ -189,6 +203,7 @@ COMMAND "@git_EXECUTABLE@" --git-dir=.git checkout "${checkout_name}" WORKING_DIRECTORY "@work_dir@" COMMAND_ERROR_IS_FATAL ANY + ${maybe_show_command} ) else() execute_process( @@ -203,6 +218,7 @@ execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git rebase --abort WORKING_DIRECTORY "@work_dir@" + ${maybe_show_command} ) if(NOT git_update_strategy STREQUAL "REBASE_CHECKOUT") @@ -211,6 +227,7 @@ execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet WORKING_DIRECTORY "@work_dir@" + ${maybe_show_command} ) endif() message(FATAL_ERROR "\nFailed to rebase in: '@work_dir@'." @@ -236,12 +253,14 @@ ${tag_name} WORKING_DIRECTORY "@work_dir@" COMMAND_ERROR_IS_FATAL ANY + ${maybe_show_command} ) execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git checkout "${checkout_name}" WORKING_DIRECTORY "@work_dir@" COMMAND_ERROR_IS_FATAL ANY + ${maybe_show_command} ) endif() endif() @@ -252,27 +271,32 @@ COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet WORKING_DIRECTORY "@work_dir@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) # Stash pop --index failed: Try again dropping the index execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git reset --hard --quiet WORKING_DIRECTORY "@work_dir@" + ${maybe_show_command} ) execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --quiet WORKING_DIRECTORY "@work_dir@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) # Stash pop failed: Restore previous state. execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git reset --hard --quiet ${head_sha} WORKING_DIRECTORY "@work_dir@" + ${maybe_show_command} ) execute_process( COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet WORKING_DIRECTORY "@work_dir@" + ${maybe_show_command} ) message(FATAL_ERROR "\nFailed to unstash changes in: '@work_dir@'." "\nYou will have to resolve the conflicts manually") @@ -288,5 +312,6 @@ submodule update @git_submodules_recurse@ --init @git_submodules@ WORKING_DIRECTORY "@work_dir@" COMMAND_ERROR_IS_FATAL ANY + ${maybe_show_command} ) endif()
diff --git a/Modules/ExternalProject/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in index e2b55ba..96449aa 100644 --- a/Modules/ExternalProject/hgclone.cmake.in +++ b/Modules/ExternalProject/hgclone.cmake.in
@@ -5,16 +5,26 @@ if(EXISTS "@hgclone_stampfile@" AND EXISTS "@hgclone_infofile@" AND "@hgclone_stampfile@" IS_NEWER_THAN "@hgclone_infofile@") - message(STATUS + message(VERBOSE "Avoiding repeated hg clone, stamp file is up to date: " "'@hgclone_stampfile@'" ) return() endif() +# Even at VERBOSE level, we don't want to see the commands executed, but +# enabling them to be shown for DEBUG may be useful to help diagnose problems. +cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level) +if(active_log_level MATCHES "DEBUG|TRACE") + set(maybe_show_command COMMAND_ECHO STDOUT) +else() + set(maybe_show_command "") +endif() + execute_process( COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to remove directory: '@source_dir@'") @@ -24,6 +34,7 @@ COMMAND "@hg_EXECUTABLE@" clone -U "@hg_repository@" "@src_name@" WORKING_DIRECTORY "@work_dir@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to clone repository: '@hg_repository@'") @@ -33,6 +44,7 @@ COMMAND "@hg_EXECUTABLE@" update @hg_tag@ WORKING_DIRECTORY "@work_dir@/@src_name@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to checkout tag: '@hg_tag@'") @@ -43,6 +55,7 @@ execute_process( COMMAND ${CMAKE_COMMAND} -E copy "@hgclone_infofile@" "@hgclone_stampfile@" RESULT_VARIABLE error_code + ${maybe_show_command} ) if(error_code) message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@hgclone_stampfile@'")
diff --git a/Modules/ExternalProject/mkdirs.cmake.in b/Modules/ExternalProject/mkdirs.cmake.in index bb835cf..03676a2 100644 --- a/Modules/ExternalProject/mkdirs.cmake.in +++ b/Modules/ExternalProject/mkdirs.cmake.in
@@ -3,8 +3,13 @@ cmake_minimum_required(VERSION 3.5) +# If CMAKE_DISABLE_SOURCE_CHANGES is set to true and the source directory is an +# existing directory in our source tree, calling file(MAKE_DIRECTORY) on it +# would cause a fatal error, even though it would be a no-op. +if(NOT EXISTS "@source_dir@") + file(MAKE_DIRECTORY "@source_dir@") +endif() file(MAKE_DIRECTORY - "@source_dir@" "@binary_dir@" "@install_dir@" "@tmp_dir@"
diff --git a/Modules/ExternalProject/shared_internal_commands.cmake b/Modules/ExternalProject/shared_internal_commands.cmake index ca3cd9f..09b9764 100644 --- a/Modules/ExternalProject/shared_internal_commands.cmake +++ b/Modules/ExternalProject/shared_internal_commands.cmake
@@ -75,6 +75,7 @@ set("${output_variable}" "${git_remote_url}" PARENT_SCOPE) endfunction() + function(_ep_is_relative_git_remote output_variable remote_url) if(remote_url MATCHES "^\\.\\./") set("${output_variable}" TRUE PARENT_SCOPE) @@ -83,6 +84,7 @@ endif() endfunction() + # Return an absolute remote URL given an existing remote URL and relative path. # The output_variable will be set to an empty string if an absolute URL # could not be computed (no error message is output). @@ -133,6 +135,7 @@ set("${output_variable}" "${protocol}${auth}${host}${separator}${path}" PARENT_SCOPE) endfunction() + # The output_variable will be set to the original git_repository if it # could not be resolved (no error message is output). The original value is # also returned if it doesn't need to be resolved. @@ -180,3 +183,1815 @@ set("${output_variable}" "${cmp0150_old_base_dir}/${git_repository}" PARENT_SCOPE) endfunction() + + +macro(_ep_get_hash_algos out_var) + set(${out_var} + MD5 + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHA3_224 + SHA3_256 + SHA3_384 + SHA3_512 + ) +endmacro() + + +macro(_ep_get_hash_regex out_var) + _ep_get_hash_algos(${out_var}) + list(JOIN ${out_var} "|" ${out_var}) + set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$") +endmacro() + + +function(_ep_parse_arguments_to_vars + f + keywords + name + ns + args +) + # Transfer the arguments into variables in the calling scope. + # Because some keywords can be repeated, we can't use cmake_parse_arguments(). + # Instead, we loop through the args and consider the namespace starting with + # an upper-case letter followed by at least two more upper-case letters, + # numbers or underscores to be keywords. + + foreach(key IN LISTS keywords) + unset(${ns}${key}) + endforeach() + + set(key) + + foreach(arg IN LISTS args) + set(is_value 1) + + if(arg MATCHES "^[A-Z][A-Z0-9_][A-Z0-9_]+$" AND + NOT (("x${arg}x" STREQUAL "x${key}x") AND + ("x${key}x" STREQUAL "xCOMMANDx")) AND + NOT arg MATCHES "^(TRUE|FALSE|YES)$") + if(arg IN_LIST keywords) + set(is_value 0) + endif() + endif() + + if(is_value) + if(key) + # Value + list(APPEND ${ns}${key} "${arg}") + else() + # Missing Keyword + message(AUTHOR_WARNING + "value '${arg}' with no previous keyword in ${f}" + ) + endif() + else() + set(key "${arg}") + endif() + endforeach() + + foreach(key IN LISTS keywords) + if(DEFINED ${ns}${key}) + set(${ns}${key} "${${ns}${key}}" PARENT_SCOPE) + else() + unset(${ns}${key} PARENT_SCOPE) + endif() + endforeach() + +endfunction() + + +# NOTE: This cannot be a macro because that will evaluate anything that looks +# like a CMake variable in any of the args. +function(_ep_parse_arguments + f + keywords + name + ns + args +) + _ep_parse_arguments_to_vars( + "${f}" + "${keywords}" + ${name} + ${ns} + "${args}" + ) + + foreach(key IN LISTS keywords) + if(DEFINED ${ns}${key}) + set(${ns}${key} "${${ns}${key}}" PARENT_SCOPE) + else() + unset(${ns}${key} PARENT_SCOPE) + endif() + endforeach() + + # Transfer the arguments to the target as target properties. These are + # read by the various steps, potentially from different scopes. + foreach(key IN LISTS keywords) + if(DEFINED ${ns}${key}) + set_property(TARGET ${name} PROPERTY ${ns}${key} "${${ns}${key}}") + endif() + endforeach() + +endfunction() + + +function(_ep_get_tls_version name tls_version_var) + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + set(tls_version_regex "^1\\.[0-3]$") + set(tls_version "${_EP_TLS_VERSION}") + if(NOT "x${tls_version}" STREQUAL "x") + if(NOT tls_version MATCHES "${tls_version_regex}") + message(FATAL_ERROR "TLS_VERSION '${tls_version}' not known") + endif() + elseif(NOT "x${CMAKE_TLS_VERSION}" STREQUAL "x") + set(tls_version "${CMAKE_TLS_VERSION}") + if(NOT tls_version MATCHES "${tls_version_regex}") + message(FATAL_ERROR "CMAKE_TLS_VERSION '${tls_version}' not known") + endif() + elseif(NOT "x$ENV{CMAKE_TLS_VERSION}" STREQUAL "x") + set(tls_version "$ENV{CMAKE_TLS_VERSION}") + if(NOT tls_version MATCHES "${tls_version_regex}") + message(FATAL_ERROR "ENV{CMAKE_TLS_VERSION} '${tls_version}' not known") + endif() + endif() + set("${tls_version_var}" "${tls_version}" PARENT_SCOPE) +endfunction() + + +function(_ep_get_tls_verify name tls_verify_var) + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + set(tls_verify "${_EP_TLS_VERIFY}") + if("x${tls_verify}" STREQUAL "x") + if(NOT "x${CMAKE_TLS_VERIFY}" STREQUAL "x") + set(tls_verify "${CMAKE_TLS_VERIFY}") + elseif(NOT "x$ENV{CMAKE_TLS_VERIFY}" STREQUAL "x") + set(tls_verify "$ENV{CMAKE_TLS_VERIFY}") + endif() + endif() + set("${tls_verify_var}" "${tls_verify}" PARENT_SCOPE) +endfunction() + + +function(_ep_get_tls_cainfo name tls_cainfo_var) + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + set(tls_cainfo "${_EP_TLS_CAINFO}") + if("x${tls_cainfo}" STREQUAL "x" AND DEFINED CMAKE_TLS_CAINFO) + set(tls_cainfo "${CMAKE_TLS_CAINFO}") + endif() + set("${tls_cainfo_var}" "${tls_cainfo}" PARENT_SCOPE) +endfunction() + + +function(_ep_get_netrc name netrc_var) + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + set(netrc "${_EP_NETRC}") + if("x${netrc}" STREQUAL "x" AND DEFINED CMAKE_NETRC) + set(netrc "${CMAKE_NETRC}") + endif() + set("${netrc_var}" "${netrc}" PARENT_SCOPE) +endfunction() + + +function(_ep_get_netrc_file name netrc_file_var) + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + set(netrc_file "${_EP_NETRC_FILE}") + if("x${netrc_file}" STREQUAL "x" AND DEFINED CMAKE_NETRC_FILE) + set(netrc_file "${CMAKE_NETRC_FILE}") + endif() + set("${netrc_file_var}" "${netrc_file}" PARENT_SCOPE) +endfunction() + + +function(_ep_write_gitclone_script + script_filename + source_dir + git_EXECUTABLE + git_repository + git_tag + git_remote_name + init_submodules + git_submodules_recurse + git_submodules + git_shallow + git_progress + git_config + src_name + work_dir + gitclone_infofile + gitclone_stampfile + tls_version + tls_verify +) + + if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5) + # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path. + set(git_checkout_explicit-- "--") + else() + # Use `git checkout <branch>` even though this risks ambiguity with a + # local path. Unfortunately we cannot use `git checkout <tree-ish> --` + # because that will not search for remote branch names, a common use case. + set(git_checkout_explicit-- "") + endif() + if("${git_tag}" STREQUAL "") + message(FATAL_ERROR "Tag for git checkout should not be empty.") + endif() + + if(GIT_VERSION_STRING VERSION_LESS 2.20 OR + 2.21 VERSION_LESS_EQUAL GIT_VERSION_STRING) + set(git_clone_options "--no-checkout") + else() + set(git_clone_options) + endif() + if(git_shallow) + if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10) + list(APPEND git_clone_options "--depth 1 --no-single-branch") + else() + list(APPEND git_clone_options "--depth 1") + endif() + endif() + if(git_progress) + list(APPEND git_clone_options --progress) + endif() + foreach(config IN LISTS git_config) + list(APPEND git_clone_options --config \"${config}\") + endforeach() + if(NOT ${git_remote_name} STREQUAL "origin") + list(APPEND git_clone_options --origin \"${git_remote_name}\") + endif() + + # The clone config option is sticky, it will apply to all subsequent git + # update operations. The submodules config option is not sticky, because + # git doesn't provide any way to do that. Thus, we will have to pass the + # same config option in the update step too for submodules, but not for + # the main git repo. + set(git_submodules_config_options "") + if(NOT "x${tls_version}" STREQUAL "x") + list(APPEND git_clone_options -c http.sslVersion=tlsv${tls_version}) + list(APPEND git_submodules_config_options -c http.sslVersion=tlsv${tls_version}) + endif() + if(NOT "x${tls_verify}" STREQUAL "x") + if(tls_verify) + # Default git behavior is "true", but the user might have changed the + # global default to "false". Since TLS_VERIFY was given, ensure we honor + # the specified setting regardless of what the global default might be. + list(APPEND git_clone_options -c http.sslVerify=true) + list(APPEND git_submodules_config_options -c http.sslVerify=true) + else() + list(APPEND git_clone_options -c http.sslVerify=false) + list(APPEND git_submodules_config_options -c http.sslVerify=false) + endif() + endif() + + string (REPLACE ";" " " git_clone_options "${git_clone_options}") + + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/gitclone.cmake.in + ${script_filename} + @ONLY + ) +endfunction() + + +function(_ep_write_hgclone_script + script_filename + source_dir + hg_EXECUTABLE + hg_repository + hg_tag + src_name + work_dir + hgclone_infofile + hgclone_stampfile +) + + if("${hg_tag}" STREQUAL "") + message(FATAL_ERROR "Tag for hg checkout should not be empty.") + endif() + + configure_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/hgclone.cmake.in + ${script_filename} + @ONLY + ) +endfunction() + + +function(_ep_write_gitupdate_script + script_filename + git_EXECUTABLE + git_tag + git_remote_name + init_submodules + git_submodules_recurse + git_submodules + git_repository + work_dir + git_update_strategy + tls_version + tls_verify +) + + if("${git_tag}" STREQUAL "") + message(FATAL_ERROR "Tag for git checkout should not be empty.") + endif() + set(git_stash_save_options --quiet) + if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7) + # This avoids stashing files covered by .gitignore + list(APPEND git_stash_save_options --include-untracked) + elseif(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.6) + # Untracked files, but also ignored files, so potentially slower + list(APPEND git_stash_save_options --all) + endif() + + # The submodules config option is not sticky, git doesn't provide any way + # to do that. We have to pass this config option for the update step too. + # We don't need to set it for the non-submodule update because it gets + # recorded as part of the clone operation in a sticky manner. + set(git_submodules_config_options "") + if(NOT "x${tls_version}" STREQUAL "x") + list(APPEND git_submodules_config_options -c http.sslVersion=tlsv${tls_version}) + endif() + if(NOT "x${tls_verify}" STREQUAL "x") + if(tls_verify) + # Default git behavior is "true", but the user might have changed the + # global default to "false". Since TLS_VERIFY was given, ensure we honor + # the specified setting regardless of what the global default might be. + list(APPEND git_submodules_config_options -c http.sslVerify=true) + else() + list(APPEND git_submodules_config_options -c http.sslVerify=false) + endif() + endif() + + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/gitupdate.cmake.in" + "${script_filename}" + @ONLY + ) +endfunction() + + +function(_ep_write_downloadfile_script + script_filename + REMOTE + LOCAL + timeout + inactivity_timeout + no_progress + hash + tls_version + tls_verify + tls_cainfo + userpwd + http_headers + netrc + netrc_file +) + if("x${REMOTE}" STREQUAL "x") + message(FATAL_ERROR "REMOTE can't be empty") + endif() + if("x${LOCAL}" STREQUAL "x") + message(FATAL_ERROR "LOCAL can't be empty") + endif() + + # REMOTE could contain special characters that parse as separate arguments. + # Things like parentheses are legitimate characters in a URL, but would be + # seen as the start of a new unquoted argument by the cmake language parser. + # Avoid those special cases by preparing quoted strings for direct inclusion + # in the foreach() call that iterates over the set of URLs in REMOTE. + set(REMOTE "[====[${REMOTE}]====]") + string(REPLACE ";" "]====] [====[" REMOTE "${REMOTE}") + + if(timeout) + set(TIMEOUT_ARGS TIMEOUT ${timeout}) + set(TIMEOUT_MSG "${timeout} seconds") + else() + set(TIMEOUT_ARGS "# no TIMEOUT") + set(TIMEOUT_MSG "none") + endif() + if(inactivity_timeout) + set(INACTIVITY_TIMEOUT_ARGS INACTIVITY_TIMEOUT ${inactivity_timeout}) + set(INACTIVITY_TIMEOUT_MSG "${inactivity_timeout} seconds") + else() + set(INACTIVITY_TIMEOUT_ARGS "# no INACTIVITY_TIMEOUT") + set(INACTIVITY_TIMEOUT_MSG "none") + endif() + + if(no_progress) + set(SHOW_PROGRESS "") + else() + set(SHOW_PROGRESS "SHOW_PROGRESS") + endif() + + _ep_get_hash_regex(_ep_hash_regex) + if("${hash}" MATCHES "${_ep_hash_regex}") + set(ALGO "${CMAKE_MATCH_1}") + string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE) + else() + set(ALGO "") + set(EXPECT_VALUE "") + endif() + + set(TLS_VERSION_CODE "") + if(NOT "x${tls_version}" STREQUAL "x") + set(TLS_VERSION_CODE "set(CMAKE_TLS_VERSION \"${tls_version}\")") + endif() + + set(TLS_VERIFY_CODE "") + if(NOT "x${tls_verify}" STREQUAL "x") + set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY \"${tls_verify}\")") + endif() + + set(TLS_CAINFO_CODE "") + if(NOT "x${tls_cainfo}" STREQUAL "x") + set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${tls_cainfo}\")") + endif() + + set(NETRC_CODE "") + if(NOT "x${netrc}" STREQUAL "x") + set(NETRC_CODE "set(CMAKE_NETRC \"${netrc}\")") + endif() + + set(NETRC_FILE_CODE "") + if(NOT "x${netrc_file}" STREQUAL "x") + set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${netrc_file}\")") + endif() + + if(userpwd STREQUAL ":") + set(USERPWD_ARGS) + else() + set(USERPWD_ARGS USERPWD "${userpwd}") + endif() + + set(HTTP_HEADERS_ARGS "") + if(NOT http_headers STREQUAL "") + foreach(header IN LISTS http_headers) + string(PREPEND HTTP_HEADERS_ARGS + "HTTPHEADER \"${header}\"\n " + ) + endforeach() + endif() + + # Used variables: + # * TLS_VERSION_CODE + # * TLS_VERIFY_CODE + # * TLS_CAINFO_CODE + # * ALGO + # * EXPECT_VALUE + # * REMOTE + # * LOCAL + # * SHOW_PROGRESS + # * TIMEOUT_ARGS + # * TIMEOUT_MSG + # * USERPWD_ARGS + # * HTTP_HEADERS_ARGS + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/download.cmake.in" + "${script_filename}" + @ONLY + ) +endfunction() + + +function(_ep_write_verifyfile_script + script_filename + LOCAL + hash +) + _ep_get_hash_regex(_ep_hash_regex) + if("${hash}" MATCHES "${_ep_hash_regex}") + set(ALGO "${CMAKE_MATCH_1}") + string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE) + else() + set(ALGO "") + set(EXPECT_VALUE "") + endif() + + # Used variables: + # * ALGO + # * EXPECT_VALUE + # * LOCAL + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/verify.cmake.in" + "${script_filename}" + @ONLY + ) +endfunction() + + +function(_ep_write_extractfile_script + script_filename + name + filename + directory + options +) + set(args "") + + if(filename MATCHES + "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$") + set(args xfz) + endif() + + if(filename MATCHES "(\\.|=)tar$") + set(args xf) + endif() + + if(args STREQUAL "") + message(FATAL_ERROR + "Do not know how to extract '${filename}' -- known types are: " + ".7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip" + ) + endif() + + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/extractfile.cmake.in" + "${script_filename}" + @ONLY + ) +endfunction() + + +function(_ep_is_dir_empty dir empty_var) + file(GLOB gr "${dir}/*") + if("${gr}" STREQUAL "") + set(${empty_var} 1 PARENT_SCOPE) + else() + set(${empty_var} 0 PARENT_SCOPE) + endif() +endfunction() + +function(_ep_get_git_submodules_recurse git_submodules_recurse) + # Checks for GIT_SUBMODULES_RECURSE argument. Default is ON, which sets + # git_submodules_recurse output variable to "--recursive". Otherwise, the + # output variable is set to an empty value "". + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + if(NOT DEFINED _EP_GIT_SUBMODULES_RECURSE) + set(recurseFlag "--recursive") + else() + if(_EP_GIT_SUBMODULES_RECURSE) + set(recurseFlag "--recursive") + else() + set(recurseFlag "") + endif() + endif() + set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE) + + # The git submodule update '--recursive' flag requires git >= v1.6.5 + if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5) + message(FATAL_ERROR + "git version 1.6.5 or later required for --recursive flag with " + "'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'" + ) + endif() +endfunction() + + +function(_ep_add_script_commands script_var work_dir cmd) + # We only support a subset of what ep_replace_location_tags() handles + set(location_tags + SOURCE_DIR + SOURCE_SUBDIR + BINARY_DIR + TMP_DIR + DOWNLOAD_DIR + DOWNLOADED_FILE + ) + + # There can be multiple COMMANDs, but we have to split those up to + # one command per call to execute_process() + string(CONCAT execute_process_cmd + "execute_process(\n" + " WORKING_DIRECTORY \"${work_dir}\"\n" + " COMMAND_ERROR_IS_FATAL LAST\n" + ) + cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level) + if(active_log_level MATCHES "VERBOSE|DEBUG|TRACE") + string(APPEND execute_process_cmd " COMMAND_ECHO STDOUT\n") + endif() + string(APPEND execute_process_cmd " COMMAND ") + + string(APPEND ${script_var} "${execute_process_cmd}") + + foreach(cmd_arg IN LISTS cmd) + if(cmd_arg STREQUAL "COMMAND") + string(APPEND ${script_var} "\n)\n${execute_process_cmd}") + else() + if(_EP_LIST_SEPARATOR) + string(REPLACE "${_EP_LIST_SEPARATOR}" "\\;" cmd_arg "${cmd_arg}") + endif() + foreach(dir IN LISTS location_tags) + string(REPLACE "<${dir}>" "${_EP_${dir}}" cmd_arg "${cmd_arg}") + endforeach() + string(APPEND ${script_var} " [====[${cmd_arg}]====]") + endif() + endforeach() + + string(APPEND ${script_var} "\n)") + set(${script_var} "${${script_var}}" PARENT_SCOPE) +endfunction() + + +function(_ep_add_download_command name) + set(noValueOptions ) + set(singleValueOptions + SCRIPT_FILE # These should only be used by FetchContent + DEPENDS_VARIABLE # + ) + set(multiValueOptions ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}" + ) + + # The various _EP_... variables mentioned here and throughout this function + # are expected to already have been set by the caller via a call to + # _ep_parse_arguments() or ep_parse_arguments_to_vars(). Other variables + # with different names are assigned to for historical reasons only to keep + # the code more readable and minimize change. + + set(source_dir "${_EP_SOURCE_DIR}") + set(stamp_dir "${_EP_STAMP_DIR}") + set(download_dir "${_EP_DOWNLOAD_DIR}") + set(tmp_dir "${_EP_TMP_DIR}") + + set(cmd "${_EP_DOWNLOAD_COMMAND}") + set(cvs_repository "${_EP_CVS_REPOSITORY}") + set(svn_repository "${_EP_SVN_REPOSITORY}") + set(git_repository "${_EP_GIT_REPOSITORY}") + set(hg_repository "${_EP_HG_REPOSITORY}") + set(url "${_EP_URL}") + set(fname "${_EP_DOWNLOAD_NAME}") + + # TODO: Perhaps file:// should be copied to download dir before extraction. + string(REGEX REPLACE "file://" "" url "${url}") + + set(step_script_contents) + set(depends) + set(comment) + set(work_dir) + set(extra_repo_info) + + if(DEFINED _EP_DOWNLOAD_COMMAND) + set(work_dir ${download_dir}) + set(method custom) + if(NOT "x${cmd}" STREQUAL "x" AND arg_SCRIPT_FILE) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + + elseif(cvs_repository) + set(method cvs) + find_package(CVS QUIET) + if(NOT CVS_EXECUTABLE) + message(FATAL_ERROR "error: could not find cvs for checkout of ${name}") + endif() + + set(cvs_module "${_EP_CVS_MODULE}") + if(NOT cvs_module) + message(FATAL_ERROR "error: no CVS_MODULE") + endif() + + set(cvs_tag "${_EP_CVS_TAG}") + get_filename_component(src_name "${source_dir}" NAME) + get_filename_component(work_dir "${source_dir}" PATH) + set(comment "Performing download step (CVS checkout) for '${name}'") + set(cmd + ${CVS_EXECUTABLE} + -d ${cvs_repository} + -q + co ${cvs_tag} + -d ${src_name} + ${cvs_module} + ) + if(arg_SCRIPT_FILE) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + + elseif(svn_repository) + set(method svn) + find_package(Subversion QUIET) + if(NOT Subversion_SVN_EXECUTABLE) + message(FATAL_ERROR "error: could not find svn for checkout of ${name}") + endif() + + set(svn_revision "${_EP_SVN_REVISION}") + set(svn_username "${_EP_SVN_USERNAME}") + set(svn_password "${_EP_SVN_PASSWORD}") + set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") + set(uses_terminal "${_EP_USES_TERMINAL_DOWNLOAD}") + # The --trust-server-cert option requires --non-interactive + if(uses_terminal AND NOT svn_trust_cert) + set(svn_interactive_args "") + else() + set(svn_interactive_args "--non-interactive") + endif() + + get_filename_component(src_name "${source_dir}" NAME) + get_filename_component(work_dir "${source_dir}" PATH) + set(comment "Performing download step (SVN checkout) for '${name}'") + set(svn_user_pw_args "") + if(DEFINED _EP_SVN_USERNAME) + set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") + endif() + if(DEFINED _EP_SVN_PASSWORD) + set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") + endif() + if(svn_trust_cert) + set(svn_trust_cert_args --trust-server-cert) + endif() + set(cmd + ${Subversion_SVN_EXECUTABLE} + co + ${svn_repository} + ${svn_revision} + ${svn_interactive_args} + ${svn_trust_cert_args} + ${svn_user_pw_args} + ${src_name} + ) + if(arg_SCRIPT_FILE) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + + elseif(git_repository) + set(method git) + # FetchContent gives us these directly, so don't try to recompute them + if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) + unset(CMAKE_MODULE_PATH) # Use CMake builtin find module + find_package(Git QUIET) + if(NOT GIT_EXECUTABLE) + message(FATAL_ERROR "error: could not find git for clone of ${name}") + endif() + endif() + + _ep_get_git_submodules_recurse(git_submodules_recurse) + + set(git_tag "${_EP_GIT_TAG}") + if(NOT git_tag) + set(git_tag "master") + endif() + + set(git_init_submodules TRUE) + if(DEFINED _EP_GIT_SUBMODULES) + set(git_submodules "${_EP_GIT_SUBMODULES}") + if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") + set(git_init_submodules FALSE) + endif() + endif() + + set(git_remote_name "${_EP_GIT_REMOTE_NAME}") + if(NOT git_remote_name) + set(git_remote_name "origin") + endif() + + _ep_get_tls_version(${name} tls_version) + _ep_get_tls_verify(${name} tls_verify) + set(git_shallow "${_EP_GIT_SHALLOW}") + set(git_progress "${_EP_GIT_PROGRESS}") + set(git_config "${_EP_GIT_CONFIG}") + + # If git supports it, make checkouts quiet when checking out a git hash. + # This avoids the very noisy detached head message. + if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7) + list(PREPEND git_config advice.detachedHead=false) + endif() + + # The command doesn't expose any details, so we need to record additional + # information in the RepositoryInfo.txt file. For the download step, only + # the things specifically affecting the clone operation should be recorded. + # If the repo changes, the clone script should be run again. + # But if only the tag changes, avoid running the clone script again. + # Let the 'always' running update step checkout the new tag. + # + set(extra_repo_info + "repository=${git_repository} +remote=${git_remote_name} +init_submodules=${git_init_submodules} +recurse_submodules=${git_submodules_recurse} +submodules=${git_submodules} +CMP0097=${_EP_CMP0097} + ") + get_filename_component(src_name "${source_dir}" NAME) + get_filename_component(work_dir "${source_dir}" PATH) + + # 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. + # + set(clone_script ${tmp_dir}/${name}-gitclone.cmake) + _ep_write_gitclone_script( + ${clone_script} + ${source_dir} + ${GIT_EXECUTABLE} + ${git_repository} + ${git_tag} + ${git_remote_name} + ${git_init_submodules} + "${git_submodules_recurse}" + "${git_submodules}" + "${git_shallow}" + "${git_progress}" + "${git_config}" + ${src_name} + ${work_dir} + ${stamp_dir}/${name}-gitinfo.txt + ${stamp_dir}/${name}-gitclone-lastrun.txt + "${tls_version}" + "${tls_verify}" + ) + set(comment "Performing download step (git clone) for '${name}'") + set(cmd ${CMAKE_COMMAND} + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P ${clone_script} + ) + + if(arg_SCRIPT_FILE) + set(step_script_contents "include(\"${clone_script}\")") + list(APPEND depends ${clone_script}) + endif() + + elseif(hg_repository) + set(method hg) + find_package(Hg QUIET) + if(NOT HG_EXECUTABLE) + message(FATAL_ERROR "error: could not find hg for clone of ${name}") + endif() + + set(hg_tag "${_EP_HG_TAG}") + if(NOT hg_tag) + set(hg_tag "tip") + endif() + + # The command doesn't expose any details, so we need to record additional + # information in the RepositoryInfo.txt file. For the download step, only + # the things specifically affecting the clone operation should be recorded. + # If the repo changes, the clone script should be run again. + # But if only the tag changes, avoid running the clone script again. + # Let the 'always' running update step checkout the new tag. + # + set(extra_repo_info "repository=${hg_repository}") + get_filename_component(src_name "${source_dir}" NAME) + get_filename_component(work_dir "${source_dir}" PATH) + + # Since hg 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 hg clone. + # + set(clone_script ${tmp_dir}/${name}-hgclone.cmake) + _ep_write_hgclone_script( + ${clone_script} + ${source_dir} + ${HG_EXECUTABLE} + ${hg_repository} + ${hg_tag} + ${src_name} + ${work_dir} + ${stamp_dir}/${name}-hginfo.txt + ${stamp_dir}/${name}-hgclone-lastrun.txt + ) + set(comment "Performing download step (hg clone) for '${name}'") + set(cmd ${CMAKE_COMMAND} + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P ${clone_script} + ) + + if(arg_SCRIPT_FILE) + set(step_script_contents "include(\"${clone_script}\")") + list(APPEND depends ${clone_script}) + endif() + + elseif(url) + set(method url) + get_filename_component(work_dir "${source_dir}" PATH) + set(hash "${_EP_URL_HASH}") + _ep_get_hash_regex(_ep_hash_regex) + if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}") + _ep_get_hash_algos(_ep_hash_algos) + list(JOIN _ep_hash_algos "|" _ep_hash_algos) + message(FATAL_ERROR + "URL_HASH is set to\n" + " ${hash}\n" + "but must be ALGO=value where ALGO is\n" + " ${_ep_hash_algos}\n" + "and value is a hex string." + ) + endif() + set(md5 "${_EP_URL_MD5}") + if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}") + message(FATAL_ERROR + "URL_MD5 is set to\n" + " ${md5}\n" + "but must be a hex string." + ) + endif() + if(md5 AND NOT hash) + set(hash "MD5=${md5}") + endif() + set(extra_repo_info + "url(s)=${url} +hash=${hash} + ") + + list(LENGTH url url_list_length) + if(NOT "${url_list_length}" STREQUAL "1") + foreach(entry IN LISTS url) + if(NOT "${entry}" MATCHES "^[a-z]+://") + message(FATAL_ERROR + "At least one entry of URL is a path (invalid in a list)" + ) + endif() + endforeach() + if("x${fname}" STREQUAL "x") + list(GET url 0 fname) + endif() + endif() + + if(IS_DIRECTORY "${url}") + get_filename_component(abs_dir "${url}" ABSOLUTE) + set(comment "Performing download step (DIR copy) for '${name}'") + set(cmd + ${CMAKE_COMMAND} -E rm -rf ${source_dir} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir} + ) + if(arg_SCRIPT_FILE) + # While it may be tempting to implement the two operations directly + # with file(), the behavior is different. file(COPY) preserves input + # file timestamps, which we don't want. Therefore, still use the same + # external commands so that we get the same behavior. + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + else() + set(no_extract "${_EP_DOWNLOAD_NO_EXTRACT}") + string(APPEND extra_repo_info "no_extract=${no_extract}\n") + set(verify_script "${stamp_dir}/verify-${name}.cmake") + if("${url}" MATCHES "^[a-z]+://") + # TODO: Should download and extraction be different steps? + if("x${fname}" STREQUAL "x") + set(fname "${url}") + endif() + set(ext_regex [[7z|tar|tar\.bz2|tar\.gz|tar\.xz|tbz2|tgz|txz|zip]]) + if("${fname}" MATCHES "([^/\\?#]+(\\.|=)(${ext_regex}))([/?#].*)?$") + set(fname "${CMAKE_MATCH_1}") + elseif(no_extract) + get_filename_component(fname "${fname}" NAME) + else() + # Fall back to a default file name. The actual file name does not + # matter because it is used only internally and our extraction tool + # inspects the file content directly. If it turns out the wrong URL + # was given that will be revealed during the build which is an easier + # place for users to diagnose than an error here anyway. + set(fname "archive.tar") + endif() + string(REPLACE ";" "-" fname "${fname}") + set(file ${download_dir}/${fname}) + set(timeout "${_EP_TIMEOUT}") + set(inactivity_timeout "${_EP_INACTIVITY_TIMEOUT}") + set(no_progress "${_EP_DOWNLOAD_NO_PROGRESS}") + _ep_get_tls_version(${name} tls_version) + _ep_get_tls_verify(${name} tls_verify) + _ep_get_tls_cainfo(${name} tls_cainfo) + _ep_get_netrc(${name} netrc) + _ep_get_netrc_file(${name} netrc_file) + set(http_username "${_EP_HTTP_USERNAME}") + set(http_password "${_EP_HTTP_PASSWORD}") + set(http_headers "${_EP_HTTP_HEADER}") + set(download_script "${stamp_dir}/download-${name}.cmake") + _ep_write_downloadfile_script( + "${download_script}" + "${url}" + "${file}" + "${timeout}" + "${inactivity_timeout}" + "${no_progress}" + "${hash}" + "${tls_version}" + "${tls_verify}" + "${tls_cainfo}" + "${http_username}:${http_password}" + "${http_headers}" + "${netrc}" + "${netrc_file}" + ) + set(cmd + ${CMAKE_COMMAND} + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P "${download_script}" + COMMAND + ) + if(arg_SCRIPT_FILE) + set(step_script_contents "include(\"${download_script}\")\n") + endif() + + if (no_extract) + set(steps "download and verify") + else () + set(steps "download, verify and extract") + endif () + set(comment "Performing download step (${steps}) for '${name}'") + # already verified by 'download_script' + # We use file(CONFIGURE) instead of file(WRITE) to avoid updating the + # timestamp when the file already existed and was empty. + file(CONFIGURE OUTPUT "${verify_script}" CONTENT "") + + # Rather than adding everything to the RepositoryInfo.txt file, it is + # more robust to just depend on the download script. That way, we will + # re-download if any aspect of the download changes. + list(APPEND depends "${download_script}") + else() + set(file "${url}") + if (no_extract) + set(steps "verify") + else () + set(steps "verify and extract") + endif () + set(comment "Performing download step (${steps}) for '${name}'") + _ep_write_verifyfile_script( + "${verify_script}" + "${file}" + "${hash}" + ) + endif() + list(APPEND cmd ${CMAKE_COMMAND} + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P ${verify_script} + ) + if(arg_SCRIPT_FILE) + string(APPEND step_script_contents "include(\"${verify_script}\")\n") + list(APPEND depends ${verify_script}) + endif() + set(extract_timestamp "${_EP_DOWNLOAD_EXTRACT_TIMESTAMP}") + if(no_extract) + if(DEFINED _EP_DOWNLOAD_EXTRACT_TIMESTAMP) + message(FATAL_ERROR + "Cannot specify DOWNLOAD_EXTRACT_TIMESTAMP when using " + "DOWNLOAD_NO_EXTRACT TRUE" + ) + endif() + if(arg_SCRIPT_FILE) + # There's no target to record the location of the downloaded file. + # Instead, we copy it to the source directory within the script, + # which is what FetchContent always does in this situation. + cmake_path(SET safe_file NORMALIZE "${file}") + cmake_path(GET safe_file FILENAME filename) + string(APPEND step_script_contents + "file(COPY_FILE\n" + " \"${file}\"\n" + " \"${source_dir}/${filename}\"\n" + " ONLY_IF_DIFFERENT\n" + " INPUT_MAY_BE_RECENT\n" + ")" + ) + list(APPEND depends ${source_dir}/${filename}) + else() + set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) + endif() + else() + if(NOT DEFINED _EP_DOWNLOAD_EXTRACT_TIMESTAMP) + # Default depends on policy CMP0135 + if(_EP_CMP0135 STREQUAL "") + message(AUTHOR_WARNING + "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 the time of extraction, otherwise " + "code that depends on the extracted contents might not be " + "rebuilt if the URL changes. The OLD behavior preserves the " + "timestamps from the archive instead, but this is usually not " + "what you want. Update your project to the NEW behavior or " + "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of " + "true to avoid this robustness issue." + ) + set(extract_timestamp TRUE) + elseif(_EP_CMP0135 STREQUAL "NEW") + set(extract_timestamp FALSE) + else() + set(extract_timestamp TRUE) + endif() + endif() + if(extract_timestamp) + set(options "") + else() + set(options "--touch") + endif() + set(extract_script "${stamp_dir}/extract-${name}.cmake") + _ep_write_extractfile_script( + "${extract_script}" + "${name}" + "${file}" + "${source_dir}" + "${options}" + ) + list(APPEND cmd + COMMAND ${CMAKE_COMMAND} + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P ${extract_script} + ) + if(arg_SCRIPT_FILE) + string(APPEND step_script_contents "include(\"${extract_script}\")\n") + list(APPEND depends ${extract_script}) + endif() + endif () + endif() + else() + set(method source_dir) + _ep_is_dir_empty("${source_dir}" empty) + if(${empty}) + message(FATAL_ERROR + "No download info given for '${name}' and its source directory:\n" + " ${source_dir}\n" + "is not an existing non-empty directory. Please specify one of:\n" + " * SOURCE_DIR with an existing non-empty directory\n" + " * DOWNLOAD_COMMAND\n" + " * URL\n" + " * GIT_REPOSITORY\n" + " * SVN_REPOSITORY\n" + " * HG_REPOSITORY\n" + " * CVS_REPOSITORY and CVS_MODULE" + ) + endif() + if(arg_SCRIPT_FILE) + set(step_script_contents "message(VERBOSE [[Using SOURCE_DIR as is]])") + endif() + endif() + + # We use configure_file() to write the repo_info_file so that the file's + # timestamp is not updated if we don't change the contents + + set(repo_info_file ${stamp_dir}/${name}-${method}info.txt) + list(APPEND depends ${repo_info_file}) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/RepositoryInfo.txt.in" + "${repo_info_file}" + @ONLY + ) + + if(arg_SCRIPT_FILE) + set(step_name download) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/stepscript.cmake.in" + "${arg_SCRIPT_FILE}" + @ONLY + ) + set(${arg_DEPENDS_VARIABLE} "${depends}" PARENT_SCOPE) + return() + endif() + + # Nothing below this point is applicable when we've been asked to put the + # download step in a script file (which is the FetchContent case). + + if(_EP_LOG_DOWNLOAD) + set(log LOG 1) + else() + set(log "") + endif() + + if(_EP_USES_TERMINAL_DOWNLOAD) + set(uses_terminal USES_TERMINAL 1) + else() + set(uses_terminal "") + endif() + + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(\${name} download + INDEPENDENT TRUE + COMMENT \${comment} + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDS \${depends} + DEPENDEES mkdir + ${log} + ${uses_terminal} + )" + ) +endfunction() + +function(_ep_get_update_disconnected var name) + # Note that the arguments are assumed to have already been parsed and have + # been translated into variables with the prefix _EP_... by a call to + # ep_parse_arguments() or ep_parse_arguments_to_vars(). + if(DEFINED _EP_UPDATE_DISCONNECTED) + set(update_disconnected "${_EP_UPDATE_DISCONNECTED}") + else() + get_property(update_disconnected + DIRECTORY + PROPERTY EP_UPDATE_DISCONNECTED + ) + endif() + set(${var} "${update_disconnected}" PARENT_SCOPE) +endfunction() + +function(_ep_add_update_command name) + set(noValueOptions ) + set(singleValueOptions + SCRIPT_FILE # These should only be used by FetchContent + DEPEND_VARIABLE # + ) + set(multiValueOptions ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}" + ) + + # The various _EP_... variables mentioned here and throughout this function + # are expected to already have been set by the caller via a call to + # _ep_parse_arguments() or ep_parse_arguments_to_vars(). Other variables + # with different names are assigned to for historical reasons only to keep + # the code more readable and minimize change. + + set(source_dir "${_EP_SOURCE_DIR}") + set(stamp_dir "${_EP_STAMP_DIR}") + set(tmp_dir "${_EP_TMP_DIR}") + + set(cmd "${_EP_UPDATE_COMMAND}") + set(cvs_repository "${_EP_CVS_REPOSITORY}") + set(svn_repository "${_EP_SVN_REPOSITORY}") + set(git_repository "${_EP_GIT_REPOSITORY}") + set(hg_repository "${_EP_HG_REPOSITORY}") + + _ep_get_update_disconnected(update_disconnected ${name}) + + set(work_dir) + set(comment) + set(always) + set(file_deps) + + if(DEFINED _EP_UPDATE_COMMAND) + set(work_dir ${source_dir}) + if(NOT "x${cmd}" STREQUAL "x") + set(always 1) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + + elseif(cvs_repository) + if(NOT CVS_EXECUTABLE) + message(FATAL_ERROR "error: could not find cvs for update of ${name}") + endif() + set(work_dir ${source_dir}) + set(comment "Performing update step (CVS update) for '${name}'") + set(cvs_tag "${_EP_CVS_TAG}") + set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag}) + set(always 1) + + if(arg_SCRIPT_FILE) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + + elseif(svn_repository) + if(NOT Subversion_SVN_EXECUTABLE) + message(FATAL_ERROR "error: could not find svn for update of ${name}") + endif() + set(work_dir ${source_dir}) + set(comment "Performing update step (SVN update) for '${name}'") + set(svn_revision "${_EP_SVN_REVISION}") + set(svn_username "${_EP_SVN_USERNAME}") + set(svn_password "${_EP_SVN_PASSWORD}") + set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") + set(uses_terminal "${_EP_USES_TERMINAL_UPDATE}") + # The --trust-server-cert option requires --non-interactive + if(uses_terminal AND NOT svn_trust_cert) + set(svn_interactive_args "") + else() + set(svn_interactive_args "--non-interactive") + endif() + set(svn_user_pw_args "") + if(DEFINED _EP_SVN_USERNAME) + set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") + endif() + if(DEFINED _EP_SVN_PASSWORD) + set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") + endif() + if(svn_trust_cert) + set(svn_trust_cert_args --trust-server-cert) + endif() + set(cmd + ${Subversion_SVN_EXECUTABLE} + up + ${svn_revision} + ${svn_interactive_args} + ${svn_trust_cert_args} + ${svn_user_pw_args} + ) + set(always 1) + + if(arg_SCRIPT_FILE) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + + elseif(git_repository) + # FetchContent gives us these directly, so don't try to recompute them + if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING) + unset(CMAKE_MODULE_PATH) # Use CMake builtin find module + find_package(Git QUIET) + if(NOT GIT_EXECUTABLE) + message(FATAL_ERROR "error: could not find git for fetch of ${name}") + endif() + endif() + set(work_dir ${source_dir}) + set(comment "Performing update step for '${name}'") + set(comment_disconnected "Performing disconnected update step for '${name}'") + + if(update_disconnected) + set(can_fetch_default NO) + else() + set(can_fetch_default YES) + endif() + + set(git_tag "${_EP_GIT_TAG}") + if(NOT git_tag) + set(git_tag "master") + endif() + + set(git_remote_name "${_EP_GIT_REMOTE_NAME}") + if(NOT git_remote_name) + set(git_remote_name "origin") + endif() + + set(git_init_submodules TRUE) + if(DEFINED _EP_GIT_SUBMODULES) + set(git_submodules "${_EP_GIT_SUBMODULES}") + if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW") + set(git_init_submodules FALSE) + endif() + endif() + + set(git_update_strategy "${_EP_GIT_REMOTE_UPDATE_STRATEGY}") + if(NOT git_update_strategy) + set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}") + endif() + if(NOT git_update_strategy) + set(git_update_strategy REBASE) + endif() + set(strategies CHECKOUT REBASE REBASE_CHECKOUT) + if(NOT git_update_strategy IN_LIST strategies) + message(FATAL_ERROR + "'${git_update_strategy}' is not one of the supported strategies: " + "${strategies}" + ) + endif() + + _ep_get_git_submodules_recurse(git_submodules_recurse) + + _ep_get_tls_version(${name} tls_version) + _ep_get_tls_verify(${name} tls_verify) + + set(update_script "${tmp_dir}/${name}-gitupdate.cmake") + list(APPEND file_deps ${update_script}) + _ep_write_gitupdate_script( + "${update_script}" + "${GIT_EXECUTABLE}" + "${git_tag}" + "${git_remote_name}" + "${git_init_submodules}" + "${git_submodules_recurse}" + "${git_submodules}" + "${git_repository}" + "${work_dir}" + "${git_update_strategy}" + "${tls_version}" + "${tls_verify}" + ) + set(cmd ${CMAKE_COMMAND} + -Dcan_fetch=YES + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P ${update_script} + ) + set(cmd_disconnected ${CMAKE_COMMAND} + -Dcan_fetch=NO + -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE + -P ${update_script} + ) + set(always 1) + + if(arg_SCRIPT_FILE) + set(step_script_contents "include(\"${update_script}\")") + endif() + + elseif(hg_repository) + if(NOT HG_EXECUTABLE) + message(FATAL_ERROR "error: could not find hg for pull of ${name}") + endif() + set(work_dir ${source_dir}) + set(comment "Performing update step (hg pull) for '${name}'") + set(comment_disconnected "Performing disconnected update step for '${name}'") + + set(hg_tag "${_EP_HG_TAG}") + if(NOT hg_tag) + set(hg_tag "tip") + endif() + + if("${HG_VERSION_STRING}" STREQUAL "2.1") + set(notesAnchor + "#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X" + ) + message(WARNING + "Mercurial 2.1 does not distinguish an empty pull from a failed pull: + http://mercurial.selenic.com/wiki/UpgradeNotes${notesAnchor} + http://thread.gmane.org/gmane.comp.version-control.mercurial.devel/47656 +Update to Mercurial >= 2.1.1. +") + endif() + + set(cmd + ${HG_EXECUTABLE} pull + COMMAND ${HG_EXECUTABLE} update ${hg_tag} + ) + set(cmd_disconnected ${HG_EXECUTABLE} update ${hg_tag}) + set(always 1) + + if(arg_SCRIPT_FILE) + # These commands are simple, and we know whether updates need to be + # disconnected or not for this case, so write them directly instead of + # forming them from "cmd" and "cmd_disconnected". + if(NOT update_disconnected) + string(APPEND step_script_contents + "execute_process(\n" + " WORKING_DIRECTORY \"${work_dir}\"\n" + " COMMAND_ERROR_IS_FATAL LAST\n" + " COMMAND \"${HG_EXECUTABLE}\" pull\n" + ")" + ) + endif() + string(APPEND step_script_contents + "execute_process(\n" + " WORKING_DIRECTORY \"${work_dir}\"\n" + " COMMAND_ERROR_IS_FATAL LAST\n" + " COMMAND \"${HG_EXECUTABLE}\" update \"${hg_tag}\"\n" + ")" + ) + endif() + endif() + + # We use configure_file() to write the update_info_file so that the file's + # timestamp is not updated if we don't change the contents + if(NOT DEFINED cmd_disconnected) + set(cmd_disconnected "${cmd}") + endif() + set(update_info_file ${stamp_dir}/${name}-update-info.txt) + list(APPEND file_deps ${update_info_file}) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/UpdateInfo.txt.in" + "${update_info_file}" + @ONLY + ) + + if(arg_SCRIPT_FILE) + set(step_name update) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/stepscript.cmake.in" + "${arg_SCRIPT_FILE}" + @ONLY + ) + set(${arg_DEPENDS_VARIABLE} "${file_deps}" PARENT_SCOPE) + return() + endif() + + # Nothing below this point is applicable when we've been asked to put the + # update step in a script file (which is the FetchContent case). + + if(_EP_LOG_UPDATE) + set(log LOG 1) + else() + set(log "") + endif() + + if(_EP_USES_TERMINAL_UPDATE) + set(uses_terminal USES_TERMINAL 1) + else() + set(uses_terminal "") + endif() + + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} update + INDEPENDENT TRUE + COMMENT \${comment} + COMMAND ${__cmdQuoted} + ALWAYS \${always} + EXCLUDE_FROM_MAIN \${update_disconnected} + WORKING_DIRECTORY \${work_dir} + DEPENDEES download + DEPENDS \${file_deps} + ${log} + ${uses_terminal} + )" + ) + if(update_disconnected) + if(NOT DEFINED comment_disconnected) + set(comment_disconnected "${comment}") + endif() + set(__cmdQuoted) + foreach(__item IN LISTS cmd_disconnected) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} update_disconnected + INDEPENDENT TRUE + COMMENT \${comment_disconnected} + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDEES download + DEPENDS \${file_deps} + ${log} + ${uses_terminal} + )" + ) + endif() + +endfunction() + + +function(_ep_add_patch_command name) + set(noValueOptions ) + set(singleValueOptions + SCRIPT_FILE # These should only be used by FetchContent + ) + set(multiValueOptions ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}" + ) + + # The various _EP_... variables mentioned here and throughout this function + # are expected to already have been set by the caller via a call to + # _ep_parse_arguments() or ep_parse_arguments_to_vars(). Other variables + # with different names are assigned to for historical reasons only to keep + # the code more readable and minimize change. + + set(source_dir "${_EP_SOURCE_DIR}") + set(stamp_dir "${_EP_STAMP_DIR}") + + set(cmd "${_EP_PATCH_COMMAND}") + set(step_script_contents "") + + set(work_dir) + if(DEFINED _EP_PATCH_COMMAND) + set(work_dir ${source_dir}) + if(arg_SCRIPT_FILE) + _ep_add_script_commands( + step_script_contents + "${work_dir}" + "${cmd}" # Must be a single quoted argument + ) + endif() + endif() + + # We use configure_file() to write the patch_info_file so that the file's + # timestamp is not updated if we don't change the contents + set(patch_info_file ${stamp_dir}/${name}-patch-info.txt) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/PatchInfo.txt.in" + "${patch_info_file}" + @ONLY + ) + + if(arg_SCRIPT_FILE) + set(step_name patch) + configure_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/stepscript.cmake.in" + "${arg_SCRIPT_FILE}" + @ONLY + ) + return() + endif() + + # Nothing below this point is applicable when we've been asked to put the + # patch step in a script file (which is the FetchContent case). + + if(_EP_LOG_PATCH) + set(log LOG 1) + else() + set(log "") + endif() + + if(_EP_USES_TERMINAL_PATCH) + set(uses_terminal USES_TERMINAL 1) + else() + set(uses_terminal "") + endif() + + _ep_get_update_disconnected(update_disconnected ${name}) + + set(__cmdQuoted) + foreach(__item IN LISTS cmd) + string(APPEND __cmdQuoted " [==[${__item}]==]") + endforeach() + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} patch + INDEPENDENT TRUE + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + EXCLUDE_FROM_MAIN \${update_disconnected} + DEPENDEES update + DEPENDS \${patch_info_file} + ${log} + ${uses_terminal} + )" + ) + + if(update_disconnected) + cmake_language(EVAL CODE " + ExternalProject_Add_Step(${name} patch_disconnected + INDEPENDENT TRUE + COMMAND ${__cmdQuoted} + WORKING_DIRECTORY \${work_dir} + DEPENDEES update_disconnected + DEPENDS \${patch_info_file} + ${log} + ${uses_terminal} + )" + ) + endif() + +endfunction() + + +macro(_ep_get_add_keywords out_var) + set(${out_var} + # + # Directory options + # + PREFIX + TMP_DIR + STAMP_DIR + LOG_DIR + DOWNLOAD_DIR + SOURCE_DIR + BINARY_DIR + INSTALL_DIR + # + # Download step options + # + DOWNLOAD_COMMAND + # + URL + URL_HASH + URL_MD5 + DOWNLOAD_NAME + DOWNLOAD_EXTRACT_TIMESTAMP + DOWNLOAD_NO_EXTRACT + DOWNLOAD_NO_PROGRESS + TIMEOUT + INACTIVITY_TIMEOUT + HTTP_USERNAME + HTTP_PASSWORD + HTTP_HEADER + TLS_VERSION # Also used for git clone operations + TLS_VERIFY # Also used for git clone operations + TLS_CAINFO + NETRC + NETRC_FILE + # + GIT_REPOSITORY + GIT_TAG + GIT_REMOTE_NAME + GIT_SUBMODULES + GIT_SUBMODULES_RECURSE + GIT_SHALLOW + GIT_PROGRESS + GIT_CONFIG + GIT_REMOTE_UPDATE_STRATEGY + # + SVN_REPOSITORY + SVN_REVISION + SVN_USERNAME + SVN_PASSWORD + SVN_TRUST_CERT + # + HG_REPOSITORY + HG_TAG + # + CVS_REPOSITORY + CVS_MODULE + CVS_TAG + # + # Update step options + # + UPDATE_COMMAND + UPDATE_DISCONNECTED + # + # Patch step options + # + PATCH_COMMAND + # + # Configure step options + # + CONFIGURE_COMMAND + CMAKE_COMMAND + CMAKE_GENERATOR + CMAKE_GENERATOR_PLATFORM + CMAKE_GENERATOR_TOOLSET + CMAKE_GENERATOR_INSTANCE + CMAKE_ARGS + CMAKE_CACHE_ARGS + CMAKE_CACHE_DEFAULT_ARGS + SOURCE_SUBDIR + CONFIGURE_HANDLED_BY_BUILD + # + # Build step options + # + BUILD_COMMAND + BUILD_IN_SOURCE + BUILD_ALWAYS + BUILD_BYPRODUCTS + BUILD_JOB_SERVER_AWARE + # + # Install step options + # + INSTALL_COMMAND + INSTALL_BYPRODUCTS + # + # Test step options + # + TEST_COMMAND + TEST_BEFORE_INSTALL + TEST_AFTER_INSTALL + TEST_EXCLUDE_FROM_MAIN + # + # Logging options + # + LOG_DOWNLOAD + LOG_UPDATE + LOG_PATCH + LOG_CONFIGURE + LOG_BUILD + LOG_INSTALL + LOG_TEST + LOG_MERGED_STDOUTERR + LOG_OUTPUT_ON_FAILURE + # + # Terminal access options + # + USES_TERMINAL_DOWNLOAD + USES_TERMINAL_UPDATE + USES_TERMINAL_PATCH + USES_TERMINAL_CONFIGURE + USES_TERMINAL_BUILD + USES_TERMINAL_INSTALL + USES_TERMINAL_TEST + # + # Target options + # + DEPENDS + EXCLUDE_FROM_ALL + STEP_TARGETS + INDEPENDENT_STEP_TARGETS + # + # Miscellaneous options + # + LIST_SEPARATOR + # + # Internal options (undocumented) + # + EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR + ) +endmacro()
diff --git a/Modules/ExternalProject/stepscript.cmake.in b/Modules/ExternalProject/stepscript.cmake.in new file mode 100644 index 0000000..12e157e --- /dev/null +++ b/Modules/ExternalProject/stepscript.cmake.in
@@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.29) + +message(VERBOSE "Executing @step_name@ step for @name@") + +block(SCOPE_FOR VARIABLES) + +@step_script_contents@ + +endblock()
diff --git a/Modules/ExternalProject/verify.cmake.in b/Modules/ExternalProject/verify.cmake.in index c06da4e..30d0487 100644 --- a/Modules/ExternalProject/verify.cmake.in +++ b/Modules/ExternalProject/verify.cmake.in
@@ -12,7 +12,7 @@ endif() if("@ALGO@" STREQUAL "") - message(WARNING "File will not be verified since no URL_HASH specified") + message(WARNING "File cannot be verified since no URL_HASH specified") return() endif() @@ -20,7 +20,7 @@ message(FATAL_ERROR "EXPECT_VALUE can't be empty") endif() -message(STATUS "verifying file... +message(VERBOSE "verifying file... file='@LOCAL@'") file("@ALGO@" "@LOCAL@" actual_value) @@ -34,4 +34,4 @@ ") endif() -message(STATUS "verifying file... done") +message(VERBOSE "verifying file... done")
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index fae51cf..8488c12 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake
@@ -52,7 +52,7 @@ FetchContent_MakeAvailable(googletest myCompanyIcons) The :command:`FetchContent_MakeAvailable` command ensures the named -dependencies have been populated, either by an earlier call or by populating +dependencies have been populated, either by an earlier call, or by populating them itself. When performing the population, it will also add them to the main build, if possible, so that the main build can use the populated projects' targets, etc. See the command's documentation for how these steps @@ -68,37 +68,11 @@ See the :ref:`Examples <fetch-content-examples>` which demonstrate this scenario. -In some cases, the main project may need to have more precise control over -the population, or it may be required to explicitly define the population -steps in a way that cannot be captured by the declared details alone. -For such situations, the lower level :command:`FetchContent_GetProperties` and -:command:`FetchContent_Populate` commands can be used. These lack the richer -features provided by :command:`FetchContent_MakeAvailable` though, so their -direct use should be considered a last resort. The typical pattern of such -custom steps looks like this: - -.. code-block:: cmake - - # NOTE: Where possible, prefer to use FetchContent_MakeAvailable() - # instead of custom logic like this - - # Check if population has already been performed - FetchContent_GetProperties(depname) - if(NOT depname_POPULATED) - # Fetch the content using previously declared details - FetchContent_Populate(depname) - - # Set custom variables, policies, etc. - # ... - - # Bring the populated content into the build - add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR}) - endif() - The ``FetchContent`` module also supports defining and populating content in a single call, with no check for whether the content has been populated elsewhere already. This should not be done in projects, but may -be appropriate for populating content in CMake's script mode. +be appropriate for populating content in +:ref:`CMake script mode <Script Processing Mode>`. See :command:`FetchContent_Populate` for details. Commands @@ -125,22 +99,27 @@ projects to have parent projects override content details of child projects. The content ``<name>`` can be any string without spaces, but good practice - would be to use only letters, numbers and underscores. The name will be - treated case-insensitively and it should be obvious for the content it - represents, often being the name of the child project or the value given + would be to use only letters, numbers, and underscores. The name will be + treated case-insensitively, and it should be obvious for the content it + represents. It is often the name of the child project, or the value given to its top level :command:`project` command (if it is a CMake project). For well-known public projects, the name should generally be the official name of the project. Choosing an unusual name makes it unlikely that other projects needing that same content will use the same name, leading to the content being populated multiple times. - The ``<contentOptions>`` can be any of the download, update or patch options + The ``<contentOptions>`` can be any of the download, update, or patch options that the :command:`ExternalProject_Add` command understands. The configure, - build, install and test steps are explicitly disabled and therefore options - related to them will be ignored. The ``SOURCE_SUBDIR`` option is an + build, install, and test steps are explicitly disabled, so options related + to those steps will be ignored. The ``SOURCE_SUBDIR`` option is an exception, see :command:`FetchContent_MakeAvailable` for details on how that affects behavior. + .. versionchanged:: 3.30 + When policy :policy:`CMP0168` is set to ``NEW``, some output-related and + directory-related options are ignored. See the policy documentation for + details. + In most cases, ``<contentOptions>`` will just be a couple of options defining the download method and method-specific details like a commit tag or archive hash. For example: @@ -171,13 +150,13 @@ confirm that the downloaded contents are what you expected. .. versionchanged:: 3.14 - Commands for the download, update or patch steps can access the terminal. + Commands for the download, update, or patch steps can access the terminal. This may be needed for things like password prompts or real-time display of command progress. .. versionadded:: 3.22 The :variable:`CMAKE_TLS_VERIFY`, :variable:`CMAKE_TLS_CAINFO`, - :variable:`CMAKE_NETRC` and :variable:`CMAKE_NETRC_FILE` variables now + :variable:`CMAKE_NETRC`, and :variable:`CMAKE_NETRC_FILE` variables now provide the defaults for their corresponding content options, just like they do for :command:`ExternalProject_Add`. Previously, these variables were ignored by the ``FetchContent`` module. @@ -193,7 +172,7 @@ appended after the ``<name>``. ``FIND_PACKAGE_ARGS`` can also be given with nothing after it, which indicates that :command:`find_package` can still be called if :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` is - set to ``OPT_IN`` or is not set. + set to ``OPT_IN``, or is not set. It would not normally be appropriate to specify ``REQUIRED`` as one of the additional arguments after ``FIND_PACKAGE_ARGS``. Doing so would @@ -253,8 +232,8 @@ If the ``EXCLUDE_FROM_ALL`` argument is provided, then targets in the subdirectory added by :command:`FetchContent_MakeAvailable` will not be included in the ``ALL`` target by default, and may be excluded from IDE - project files. See the :command:`add_subdirectory` ``EXCLUDE_FROM_ALL`` - argument documentation for a more detailed discussion of the effects. + project files. See the documentation for the directory property + :prop_dir:`EXCLUDE_FROM_ALL` for a detailed discussion of the effects. .. command:: FetchContent_MakeAvailable @@ -283,7 +262,7 @@ when the details were declared, any ``FIND_PACKAGE_ARGS`` will be omitted. The ``OVERRIDE_FIND_PACKAGE`` keyword is also always omitted. If the provider fulfilled the request, ``FetchContent_MakeAvailable()`` - will consider that dependency handled, skip the remaining steps below + will consider that dependency handled, skip the remaining steps below, and move on to the next dependency in the list. * .. versionadded:: 3.24 @@ -305,13 +284,13 @@ the following logic to make the dependency available: * If the dependency has already been populated earlier in this run, set - the ``<lowercaseName>_POPULATED``, ``<lowercaseName>_SOURCE_DIR`` and + the ``<lowercaseName>_POPULATED``, ``<lowercaseName>_SOURCE_DIR``, and ``<lowercaseName>_BINARY_DIR`` variables in the same way as a call to :command:`FetchContent_GetProperties`, then skip the remaining steps below and move on to the next dependency in the list. - * Call :command:`FetchContent_Populate` to populate the dependency using - the details recorded by an earlier call to :command:`FetchContent_Declare`. + * Populate the dependency using the details recorded by an earlier call + to :command:`FetchContent_Declare`. Halt with a fatal error if no such details have been recorded. :variable:`FETCHCONTENT_SOURCE_DIR_<uppercaseName>` can be used to override the declared details and use content provided at the specified location @@ -321,14 +300,14 @@ Ensure the :variable:`CMAKE_FIND_PACKAGE_REDIRECTS_DIR` directory contains a ``<lowercaseName>-config.cmake`` and a - ``<lowercaseName>-config-version.cmake`` file (or equivalently + ``<lowercaseName>-config-version.cmake`` file (or equivalently, ``<name>Config.cmake`` and ``<name>ConfigVersion.cmake``). The directory that the :variable:`CMAKE_FIND_PACKAGE_REDIRECTS_DIR` variable points to is cleared at the start of every CMake run. - If no config file exists when :command:`FetchContent_Populate` returns, - a minimal one will be written which :command:`includes <include>` any - ``<lowercaseName>-extra.cmake`` or ``<name>Extra.cmake`` file with the - ``OPTIONAL`` flag (so the files can be missing and won't generate a + If no config file exists after populating the dependency in the previous + step, a minimal one will be written which :command:`includes <include>` + any ``<lowercaseName>-extra.cmake`` or ``<name>Extra.cmake`` file with + the ``OPTIONAL`` flag (so the files can be missing and won't generate a warning). Similarly, if no config version file exists, a very simple one will be written which sets ``PACKAGE_VERSION_COMPATIBLE`` and ``PACKAGE_VERSION_EXACT`` to true. This ensures all future calls to @@ -341,7 +320,7 @@ in :variable:`CMAKE_FIND_PACKAGE_REDIRECTS_DIR` with one that also sets ``PACKAGE_VERSION``. The dependency may also write a ``<lowercaseName>-extra.cmake`` or - ``<name>Extra.cmake`` file to perform custom processing or define any + ``<name>Extra.cmake`` file to perform custom processing, or define any variables that their normal (installed) package config file would otherwise usually define (many projects don't do any custom processing or set any variables and therefore have no need to do this). @@ -362,9 +341,9 @@ The ``SOURCE_SUBDIR`` option can be given in the declared details to look somewhere below the top directory instead (i.e. the same way that ``SOURCE_SUBDIR`` is used by the :command:`ExternalProject_Add` - command). The path provided with ``SOURCE_SUBDIR`` must be relative - and will be treated as relative to the top directory. It can also - point to a directory that does not contain a ``CMakeLists.txt`` file + command). The path provided with ``SOURCE_SUBDIR`` must be relative, + and it will be treated as relative to the top directory. It can also + point to a directory that does not contain a ``CMakeLists.txt`` file, or even to a directory that doesn't exist. This can be used to avoid adding a project that contains a ``CMakeLists.txt`` file in its top directory. @@ -372,12 +351,12 @@ .. versionadded:: 3.25 If the ``SYSTEM`` keyword was included in the call to :command:`FetchContent_Declare`, the ``SYSTEM`` keyword will be - added to the :command:`add_subdirectory` command as well. + added to the :command:`add_subdirectory` command. .. versionadded:: 3.28 If the ``EXCLUDE_FROM_ALL`` keyword was included in the call to :command:`FetchContent_Declare`, the ``EXCLUDE_FROM_ALL`` keyword will - be added to the :command:`add_subdirectory` command as well. + be added to the :command:`add_subdirectory` command. .. versionadded:: 3.29 :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` is set to the dependency name @@ -422,67 +401,13 @@ .. command:: FetchContent_Populate - .. note:: - Where possible, prefer to use :command:`FetchContent_MakeAvailable` - instead of implementing population manually with this command. - - .. code-block:: cmake - - FetchContent_Populate(<name>) - - In most cases, the only argument given to ``FetchContent_Populate()`` is the - ``<name>``. When used this way, the command assumes the content details have - been recorded by an earlier call to :command:`FetchContent_Declare`. The - details are stored in a global property, so they are unaffected by things - like variable or directory scope. Therefore, it doesn't matter where in the - project the details were previously declared, as long as they have been - declared before the call to ``FetchContent_Populate()``. Those saved details - are then used to construct a call to :command:`ExternalProject_Add` in a - private sub-build to perform the content population immediately. The - implementation of ``ExternalProject_Add()`` ensures that if the content has - already been populated in a previous CMake run, that content will be reused - rather than repopulating them again. For the common case where population - involves downloading content, the cost of the download is only paid once. - - An internal global property records when a particular content population - request has been processed. If ``FetchContent_Populate()`` is called more - than once for the same content name within a configure run, the second call - will halt with an error. Projects can and should check whether content - population has already been processed with the - :command:`FetchContent_GetProperties` command before calling - ``FetchContent_Populate()``. - - ``FetchContent_Populate()`` will set three variables in the scope of the - caller: - - ``<lowercaseName>_POPULATED`` - This will always be set to ``TRUE`` by the call. - - ``<lowercaseName>_SOURCE_DIR`` - The location where the populated content can be found upon return. - - ``<lowercaseName>_BINARY_DIR`` - A directory intended for use as a corresponding build directory. - - The main use case for the ``<lowercaseName>_SOURCE_DIR`` and - ``<lowercaseName>_BINARY_DIR`` variables is to call - :command:`add_subdirectory` immediately after population: - - .. code-block:: cmake - - FetchContent_Populate(FooBar) - add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) - - The values of the three variables can also be retrieved from anywhere in the - project hierarchy using the :command:`FetchContent_GetProperties` command. - - The ``FetchContent_Populate()`` command also supports a syntax allowing the - content details to be specified directly rather than using any saved - details. This is more low-level and use of this form is generally to be - avoided in favor of using saved content details as outlined above. - Nevertheless, in certain situations it can be useful to invoke the content - population as an isolated operation (typically as part of implementing some - other higher level feature or when using CMake in script mode): + The ``FetchContent_Populate()`` command is a self-contained call which can + be used to perform content population as an isolated operation. + It is rarely the right command to use, projects should almost always use + :command:`FetchContent_Declare` and :command:`FetchContent_MakeAvailable` + instead. The main use case for ``FetchContent_Populate()`` is in + :ref:`CMake script mode <Script Processing Mode>` as part of implementing + some other higher level custom feature. .. code-block:: cmake @@ -495,61 +420,62 @@ ... ) - This form has a number of key differences to that where only ``<name>`` is - provided: - - - All required population details are assumed to have been provided directly - in the call to ``FetchContent_Populate()``. Any saved details for - ``<name>`` are ignored. - - No check is made for whether content for ``<name>`` has already been - populated. - - No global property is set to record that the population has occurred. - - No global properties record the source or binary directories used for the - populated content. - - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and - ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored. - - The ``<lowercaseName>_SOURCE_DIR`` and ``<lowercaseName>_BINARY_DIR`` - variables are still returned to the caller, but since these locations are - not stored as global properties when this form is used, they are only - available to the calling scope and below rather than the entire project - hierarchy. No ``<lowercaseName>_POPULATED`` variable is set in the caller's - scope with this form. - + At least one option must be specified after `<name>`, otherwise the call + is interpreted differently (see :ref:`below <FetchContent_Populate-depName>`). The supported options for ``FetchContent_Populate()`` are the same as those - for :command:`FetchContent_Declare()`. Those few options shown just - above are either specific to ``FetchContent_Populate()`` or their behavior is - slightly modified from how :command:`ExternalProject_Add` treats them: + for :command:`FetchContent_Declare()`, with a few exceptions. The following + do not relate to populating content with ``FetchContent_Populate()`` and + therefore are not supported: + + * ``EXCLUDE_FROM_ALL`` + * ``SYSTEM`` + * ``OVERRIDE_FIND_PACKAGE`` + * ``FIND_PACKAGE_ARGS`` + + The few options shown in the signature above are either specific to + ``FetchContent_Populate()``, or their behavior is slightly modified from how + :command:`ExternalProject_Add` treats them: ``QUIET`` The ``QUIET`` option can be given to hide the output associated with populating the specified content. If the population fails, the output will be shown regardless of whether this option was given or not so that the - cause of the failure can be diagnosed. The global ``FETCHCONTENT_QUIET`` - cache variable has no effect on ``FetchContent_Populate()`` calls where the - content details are provided directly. + cause of the failure can be diagnosed. The :variable:`FETCHCONTENT_QUIET` + variable has no effect on ``FetchContent_Populate()`` calls of this form + where the content details are provided directly. + + .. versionchanged:: 3.30 + The ``QUIET`` option and :variable:`FETCHCONTENT_QUIET` variable have no + effect when policy :policy:`CMP0168` is set to ``NEW``. The output is + still quiet by default in that case, but verbosity is controlled by the + message logging level (see :variable:`CMAKE_MESSAGE_LOG_LEVEL` and + :option:`--log-level <cmake --log-level>`). ``SUBBUILD_DIR`` The ``SUBBUILD_DIR`` argument can be provided to change the location of the sub-build created to perform the population. The default value is - ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-subbuild`` and it would be + ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-subbuild``, and it would be unusual to need to override this default. If a relative path is specified, it will be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. - This option should not be confused with the ``SOURCE_SUBDIR`` option which + This option should not be confused with the ``SOURCE_SUBDIR`` option, which only affects the :command:`FetchContent_MakeAvailable` command. + .. versionchanged:: 3.30 + ``SUBBUILD_DIR`` is ignored when policy :policy:`CMP0168` is set to + ``NEW``, since there is no sub-build in that case. + ``SOURCE_DIR``, ``BINARY_DIR`` The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by :command:`ExternalProject_Add`, but different default values are used by ``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to - ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-src`` and ``BINARY_DIR`` + ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-src``, and ``BINARY_DIR`` defaults to ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-build``. If a relative path is specified, it will be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. In addition to the above explicit options, any other unrecognized options are - passed through unmodified to :command:`ExternalProject_Add` to perform the - download, patch and update steps. The following options are explicitly + passed through unmodified to :command:`ExternalProject_Add` to set up the + download, patch, and update steps. The following options are explicitly prohibited (they are disabled by the ``FetchContent_Populate()`` command): - ``CONFIGURE_COMMAND`` @@ -557,23 +483,92 @@ - ``INSTALL_COMMAND`` - ``TEST_COMMAND`` - If using ``FetchContent_Populate()`` within CMake's script mode, be aware - that the implementation sets up a sub-build which therefore requires a CMake + With this form, the :variable:`FETCHCONTENT_FULLY_DISCONNECTED` and + :variable:`FETCHCONTENT_UPDATES_DISCONNECTED` variables and policy + :policy:`CMP0170` are ignored. + + When this form of ``FetchContent_Populate()`` returns, the following + variables will be set in the scope of the caller: + + ``<lowercaseName>_SOURCE_DIR`` + The location where the populated content can be found upon return. + + ``<lowercaseName>_BINARY_DIR`` + A directory originally intended for use as a corresponding build directory, + but is unlikely to be relevant when using this form of the command. + + If using ``FetchContent_Populate()`` within + :ref:`CMake script mode <Script Processing Mode>`, be aware that the + implementation sets up a sub-build which therefore requires a CMake generator and build tool to be available. If these cannot be found by - default, then the :variable:`CMAKE_GENERATOR` and/or + default, then the :variable:`CMAKE_GENERATOR` and potentially the :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately on the command line invoking the script. + .. versionchanged:: 3.30 + If policy :policy:`CMP0168` is set to ``NEW``, no sub-build is used. + Within :ref:`CMake script mode <Script Processing Mode>`, that allows + ``FetchContent_Populate()`` to be called without any build tool or + CMake generator. + .. versionadded:: 3.18 Added support for the ``DOWNLOAD_NO_EXTRACT`` option. +.. _`FetchContent_Populate-depName`: + + The command supports another form, although it should no longer be used: + + .. code-block:: cmake + + FetchContent_Populate(<name>) + + .. versionchanged:: 3.30 + This form is deprecated. Policy :policy:`CMP0169` provides backward + compatibility for projects that still need to use this form, but projects + should be updated to use :command:`FetchContent_MakeAvailable` instead. + + In this form, the only argument given to ``FetchContent_Populate()`` is the + ``<name>``. When used this way, the command assumes the content details have + been recorded by an earlier call to :command:`FetchContent_Declare`. The + details are stored in a global property, so they are unaffected by things + like variable or directory scope. Therefore, it doesn't matter where in the + project the details were previously declared, as long as they have been + declared before the call to ``FetchContent_Populate()``. Those saved details + are then used to populate the content using a method based on + :command:`ExternalProject_Add` (see policy :policy:`CMP0168` for important + behavioral aspects of how that is done). + + When this form of ``FetchContent_Populate()`` returns, the following + variables will be set in the scope of the caller: + + ``<lowercaseName>_POPULATED`` + This will always be set to ``TRUE`` by the call. + + ``<lowercaseName>_SOURCE_DIR`` + The location where the populated content can be found upon return. + + ``<lowercaseName>_BINARY_DIR`` + A directory intended for use as a corresponding build directory. + + The values of the three variables can also be retrieved from anywhere in the + project hierarchy using the :command:`FetchContent_GetProperties` command. + + The implementation ensures that if the content has already been populated + in a previous CMake run, that content will be reused rather than repopulating + again. For the common case where population involves downloading content, + the cost of the download is only paid once. But note that it is an error to + call ``FetchContent_Populate(<name>)`` with the same ``<name>`` more than + once within a single CMake run. See :command:`FetchContent_GetProperties` + for how to determine if population of a ``<name>`` has already been + performed in the current run. + .. command:: FetchContent_GetProperties When using saved content details, a call to :command:`FetchContent_MakeAvailable` or :command:`FetchContent_Populate` records information in global properties which can be queried at any time. This information may include the source and binary directories associated with - the content and also whether or not the content population has been processed + the content, and also whether or not the content population has been processed during the current configure run. .. code-block:: cmake @@ -585,7 +580,7 @@ [POPULATED <doneVar>] ) - The ``SOURCE_DIR``, ``BINARY_DIR`` and ``POPULATED`` options can be used to + The ``SOURCE_DIR``, ``BINARY_DIR``, and ``POPULATED`` options can be used to specify which properties should be retrieved. Each option accepts a value which is the name of the variable in which to store that property. Most of the time though, only ``<name>`` is given, in which case the call will then @@ -597,13 +592,15 @@ This command is rarely needed when using :command:`FetchContent_MakeAvailable`. It is more commonly used as part of - implementing the following pattern with :command:`FetchContent_Populate`, + implementing the deprecated pattern with :command:`FetchContent_Populate`, which ensures that the relevant variables will always be defined regardless of whether or not the population has been performed elsewhere in the project already: .. code-block:: cmake + # WARNING: This pattern is deprecated, don't use it! + # # Check if population has already been performed FetchContent_GetProperties(depname) if(NOT depname_POPULATED) @@ -657,7 +654,7 @@ .. variable:: FETCHCONTENT_BASE_DIR In most cases, the saved details do not specify any options relating to the - directories to use for the internal sub-build, final source and build areas. + directories to use for the internal sub-build, final source, and build areas. It is generally best to leave these decisions up to the ``FetchContent`` module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR`` cache variable controls the point under which all content population @@ -675,14 +672,21 @@ problems with hung downloads, temporarily switching this option off may help diagnose which content population is causing the issue. + .. versionchanged:: 3.30 + ``FETCHCONTENT_QUIET`` is ignored if policy :policy:`CMP0168` is set to + ``NEW``. The output is still quiet by default in that case, but verbosity + is controlled by the message logging level (see + :variable:`CMAKE_MESSAGE_LOG_LEVEL` and + :option:`--log-level <cmake --log-level>`). + .. variable:: FETCHCONTENT_FULLY_DISCONNECTED When this option is enabled, no attempt is made to download or update any content. It is assumed that all content has already been populated in - a previous run or the source directories have been pointed at existing + a previous run, or the source directories have been pointed at existing contents the developer has provided manually (using options described further below). When the developer knows that no changes have been made to - any content details, turning this option ``ON`` can significantly speed up + any content details, turning this option ``ON`` can speed up the configure stage. It is ``OFF`` by default. .. note:: @@ -696,6 +700,11 @@ :ref:`dependency provider <dependency_providers>` and populate the dependency from local content instead. + .. versionchanged:: 3.30 + The constraint that the source directory has already been populated when + ``FETCHCONTENT_FULLY_DISCONNECTED`` is true is now enforced. + See policy :policy:`CMP0170`. + .. variable:: FETCHCONTENT_UPDATES_DISCONNECTED This is a less severe download/update control compared to @@ -1028,16 +1037,7 @@ GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a ) - # This particular version of projD requires workarounds - FetchContent_GetProperties(projD) - if(NOT projd_POPULATED) - FetchContent_Populate(projD) - - # Copy an additional/replacement file into the populated source - file(COPY someFile.c DESTINATION ${projd_SOURCE_DIR}/src) - - add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) - endif() + FetchContent_MakeAvailable(projD) A few key points should be noted in the above: @@ -1049,15 +1049,20 @@ it is up to the higher level project to ensure that the details it does define still make sense for the child projects. - In the ``projA`` call to :command:`FetchContent_MakeAvailable`, ``projD`` - is listed ahead of ``projB`` and ``projC`` to ensure that ``projA`` is in - control of how ``projD`` is populated. + is listed ahead of ``projB`` and ``projC``, so it will be populated before + either ``projB`` or ``projC``. It isn't required for ``projA`` to do this, + doing so ensures that ``projA`` fully controls the environment in which + ``projD`` is brought into the build (directory properties are particularly + relevant). - While ``projA`` defines content details for ``projE``, it does not need to explicitly call ``FetchContent_MakeAvailable(projE)`` or ``FetchContent_Populate(projD)`` itself. Instead, it leaves that to the child ``projB``. For higher level projects, it is often enough to just define the override content details and leave the actual population to the child projects. This saves repeating the same thing at each level of the - project hierarchy unnecessarily. + project hierarchy unnecessarily, but it should only be done if directory + properties set by dependencies are not expected to influence the population + of the shared dependency (``projE`` in this case). Populating Content Without Adding It To The Build """"""""""""""""""""""""""""""""""""""""""""""""" @@ -1100,9 +1105,9 @@ """"""""""""""""""""""""""""""""""""""" This last example demonstrates how one might download and unpack a -firmware tarball using CMake's :manual:`script mode <cmake(1)>`. The call to -:command:`FetchContent_Populate` specifies all the content details and the -unpacked firmware will be placed in a ``firmware`` directory below the +firmware tarball using CMake's :ref:`script mode <Script Processing Mode>`. +The call to :command:`FetchContent_Populate` specifies all the content details +and the unpacked firmware will be placed in a ``firmware`` directory below the current working directory. .. code-block:: cmake @@ -1119,6 +1124,12 @@ #]=======================================================================] +# Control policies for most of the things defined by this module. Only a few +# FetchContent_MakeAvailable() implementation details are excluded for +# backward compatibility reasons (see just after the endblock()). +block(SCOPE_FOR POLICIES) +cmake_policy(VERSION 3.29) + include(${CMAKE_CURRENT_LIST_DIR}/ExternalProject/shared_internal_commands.cmake) #======================================================================= @@ -1167,7 +1178,13 @@ set(__findPackageArgs) set(__sawQuietKeyword NO) set(__sawGlobalKeyword NO) + set(__direct_population NO) foreach(__item IN LISTS ARGN) + if(__item STREQUAL "__DIRECT_POPULATION") + set(__direct_population YES) + continue() + endif() + if(DEFINED __findPackageArgs) # All remaining args are for find_package() string(APPEND __findPackageArgs " [==[${__item}]==]") @@ -1206,6 +1223,10 @@ string(APPEND __cmdArgs " [==[${__item}]==]") endforeach() + set_property(GLOBAL PROPERTY + "_FetchContent_${contentNameLower}_direct_population" ${__direct_population} + ) + define_property(GLOBAL PROPERTY ${savedDetailsPropertyName}) cmake_language(EVAL CODE "set_property(GLOBAL PROPERTY ${savedDetailsPropertyName} ${__cmdArgs})" @@ -1372,16 +1393,24 @@ endif() # Add back in the keyword args we pulled out and potentially tweaked/added + set(forward_args "${ARG_UNPARSED_ARGUMENTS}") set(sep EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR) foreach(key IN LISTS oneValueArgs) if(DEFINED ARG_${key}) - list(PREPEND ARG_UNPARSED_ARGUMENTS ${key} "${ARG_${key}}" ${sep}) + list(PREPEND forward_args ${key} "${ARG_${key}}" ${sep}) set(sep "") endif() endforeach() + cmake_policy(GET CMP0168 cmp0168 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(cmp0168 STREQUAL "NEW") + list(PREPEND forward_args __DIRECT_POPULATION ${sep}) + endif() + set(__argsQuoted) - foreach(__item IN LISTS ARG_UNPARSED_ARGUMENTS) + foreach(__item IN LISTS forward_args) string(APPEND __argsQuoted " [==[${__item}]==]") endforeach() cmake_language(EVAL CODE @@ -1498,7 +1527,7 @@ # The value of contentName will always have been lowercased by the caller. # All other arguments are assumed to be options that are understood by # ExternalProject_Add(), except for QUIET and SUBBUILD_DIR. -function(__FetchContent_directPopulate contentName) +function(__FetchContent_doPopulation contentName) set(options QUIET @@ -1527,14 +1556,30 @@ USES_TERMINAL_DOWNLOAD USES_TERMINAL_UPDATE USES_TERMINAL_PATCH + # Internal options, may change at any time + __DIRECT_POPULATION ) set(multiValueArgs "") cmake_parse_arguments(PARSE_ARGV 1 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + if(DEFINED ARG___DIRECT_POPULATION) + # Direct call to FetchContent_Populate() with full details. The policy + # setting of its caller is included in the function arguments. + set(direct_population ${ARG___DIRECT_POPULATION}) + else() + # FetchContent_Populate() called with only the name of a dependency. + # We need the policy setting of the corresponding FetchContent_Declare(). + get_property(direct_population GLOBAL PROPERTY + "_FetchContent_${contentNameLower}_direct_population" + ) + endif() + if(NOT ARG_SUBBUILD_DIR) - message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set") + if(NOT direct_population) + message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set") + endif() elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}") set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}") endif() @@ -1558,6 +1603,164 @@ set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE) set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE) + if(direct_population) + __FetchContent_populateDirect() + else() + __FetchContent_populateSubbuild() + endif() +endfunction() + + +function(__FetchContent_populateDirect) + # Policies CMP0097, CMP0135 and CMP0150 are handled in FetchContent_Declare() + # and the stored arguments already account for them. + # For CMP0097, the arguments will always assume NEW behavior by the time + # we get to here, so ensure ExternalProject sees that. + set(_EP_CMP0097 NEW) + + set(args_to_parse + "${ARG_UNPARSED_ARGUMENTS}" + SOURCE_DIR "${ARG_SOURCE_DIR}" + BINARY_DIR "${ARG_BINARY_DIR}" + ) + if(ARG_DOWNLOAD_NO_EXTRACT) + list(APPEND args_to_parse DOWNLOAD_NO_EXTRACT YES) + endif() + + get_property(cmake_role GLOBAL PROPERTY CMAKE_ROLE) + if(cmake_role STREQUAL "PROJECT") + # We don't support direct population where a project makes a direct call + # to FetchContent_Populate(). That always goes through ExternalProject and + # will soon be deprecated anyway. + set(function_for_args FetchContent_Declare) + elseif(cmake_role STREQUAL "SCRIPT") + set(function_for_args FetchContent_Populate) + else() + message(FATAL_ERROR "Unsupported context for direct population") + endif() + + _ep_get_add_keywords(keywords) + _ep_parse_arguments_to_vars( + ${function_for_args} + "${keywords}" + ${contentName} + _EP_ + "${args_to_parse}" + ) + + # We use a simplified set of directories here. We do not need the full set + # of directories that ExternalProject supports, and we don't need the + # extensive customization options it supports either. Note that + # _EP_SOURCE_DIR and _EP_BINARY_DIR are always included in the saved args, + # so we must not set them here. + if(cmake_role STREQUAL "PROJECT") + # Put these under CMakeFiles so that they are removed by "cmake --fresh", + # which will cause the steps to re-run. + set(_EP_STAMP_DIR "${CMAKE_BINARY_DIR}/CMakeFiles/fc-stamp/${contentNameLower}") + set(_EP_TMP_DIR "${CMAKE_BINARY_DIR}/CMakeFiles/fc-tmp/${contentNameLower}") + else() + # We have no CMakeFiles in script mode, so keep everything together. + set(_EP_STAMP_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-stamp") + set(_EP_TMP_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-tmp") + endif() + # Always put downloaded things under FETCHCONTENT_BASE_DIR so that we can + # reuse previously downloaded content, even after a "cmake --fresh". + set(_EP_DOWNLOAD_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-tmp") + + # If CMAKE_DISABLE_SOURCE_CHANGES is set to true and _EP_SOURCE_DIR is an + # existing directory in our source tree, calling file(MAKE_DIRECTORY) on it + # would cause a fatal error, even though it would be a no-op. + if(NOT EXISTS "${_EP_SOURCE_DIR}") + file(MAKE_DIRECTORY "${_EP_SOURCE_DIR}") + endif() + file(MAKE_DIRECTORY + "${_EP_BINARY_DIR}" + "${_EP_STAMP_DIR}" + "${_EP_TMP_DIR}" + "${_EP_DOWNLOAD_DIR}" + ) + + # We take over the stamp files and use our own for detecting whether each + # step is up-to-date. The method used by ExternalProject is specific to + # using a sub-build and is not appropriate for us here. + + set(download_script ${_EP_TMP_DIR}/download.cmake) + set(update_script ${_EP_TMP_DIR}/update.cmake) + set(patch_script ${_EP_TMP_DIR}/patch.cmake) + _ep_add_download_command(${contentName} + SCRIPT_FILE ${download_script} + DEPENDS_VARIABLE download_depends + ) + _ep_add_update_command(${contentName} + SCRIPT_FILE ${update_script} + DEPENDS_VARIABLE update_depends + ) + _ep_add_patch_command(${contentName} + SCRIPT_FILE ${patch_script} + # No additional dependencies for the patch step + ) + + set(download_stamp ${_EP_STAMP_DIR}/download.stamp) + set(update_stamp ${_EP_STAMP_DIR}/update.stamp) + set(patch_stamp ${_EP_STAMP_DIR}/patch.stamp) + __FetchContent_doStepDirect( + SCRIPT_FILE ${download_script} + STAMP_FILE ${download_stamp} + DEPENDS ${download_depends} + ) + __FetchContent_doStepDirect( + SCRIPT_FILE ${update_script} + STAMP_FILE ${update_stamp} + DEPENDS ${update_depends} ${download_stamp} + ) + __FetchContent_doStepDirect( + SCRIPT_FILE ${patch_script} + STAMP_FILE ${patch_stamp} + DEPENDS ${update_stamp} + ) + +endfunction() + + +function(__FetchContent_doStepDirect) + set(noValueOptions ) + set(singleValueOptions + SCRIPT_FILE + STAMP_FILE + ) + set(multiValueOptions + DEPENDS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg + "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}" + ) + + if(NOT EXISTS ${arg_STAMP_FILE}) + set(do_step YES) + else() + set(do_step NO) + foreach(dep_file IN LISTS arg_DEPENDS arg_SCRIPT_FILE) + if(NOT EXISTS "${arg_STAMP_FILE}" OR + NOT EXISTS "${dep_file}" OR + NOT "${arg_STAMP_FILE}" IS_NEWER_THAN "${dep_file}") + set(do_step YES) + break() + endif() + endforeach() + endif() + + if(do_step) + include(${arg_SCRIPT_FILE}) + file(TOUCH "${arg_STAMP_FILE}") + endif() +endfunction() + + +function(__FetchContent_populateSubbuild) + # All argument parsing is done in __FetchContent_doPopulate(), since it is + # common to both the subbuild and direct population strategies. + # Parsed arguments are in ARG_... variables. + # The unparsed arguments may contain spaces, so build up ARG_EXTRA # in such a way that it correctly substitutes into the generated # CMakeLists.txt file with each argument quoted. @@ -1650,6 +1853,7 @@ set(__FETCHCONTENT_CACHED_INFO "") set(__passthrough_vars CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY + CMAKE_TLS_VERSION CMAKE_TLS_VERIFY CMAKE_TLS_CAINFO CMAKE_NETRC @@ -1730,12 +1934,67 @@ message(FATAL_ERROR "Empty contentName not allowed for FetchContent_Populate()") endif() + if(ARGC EQUAL 1) + cmake_policy(GET CMP0169 cmp0169 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(NOT cmp0169 STREQUAL "OLD") + string(CONCAT msg + "Calling FetchContent_Populate(${contentName}) is deprecated, call " + "FetchContent_MakeAvailable(${contentName}) instead. " + "Policy CMP0169 can be set to OLD to allow " + "FetchContent_Populate(${contentName}) to be called directly for now, " + "but the ability to call it with declared details will be removed " + "completely in a future version." + ) + if(cmp0169 STREQUAL "NEW") + message(FATAL_ERROR "${msg}") + else() + message(AUTHOR_WARNING "${msg}") + endif() + endif() + set(__doDirectArgs) + else() + cmake_policy(GET CMP0168 cmp0168 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(cmp0168 STREQUAL "NEW") + set(__doDirectArgs __DIRECT_POPULATION YES) + else() + set(__doDirectArgs __DIRECT_POPULATION NO) + endif() + endif() + + cmake_policy(GET CMP0170 cmp0170 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + + cmake_parse_arguments(PARSE_ARGV 1 __arg "" "" "") + set(__argsQuoted "[==[${contentName}]==] [==[${cmp0170}]==]") + foreach(__item IN LISTS __arg_UNPARSED_ARGUMENTS __doDirectArgs) + string(APPEND __argsQuoted " [==[${__item}]==]") + endforeach() + + cmake_language(EVAL CODE "__FetchContent_Populate(${__argsQuoted})") + + string(TOLOWER ${contentName} contentNameLower) + foreach(var IN ITEMS SOURCE_DIR BINARY_DIR POPULATED) + set(var "${contentNameLower}_${var}") + if(DEFINED ${var}) + set(${var} "${${var}}" PARENT_SCOPE) + endif() + endforeach() + +endfunction() + +function(__FetchContent_Populate contentName cmp0170) + string(TOLOWER ${contentName} contentNameLower) if(ARGN) # This is the direct population form with details fully specified # as part of the call, so we already have everything we need - __FetchContent_directPopulate( + __FetchContent_doPopulation( ${contentNameLower} SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild" SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src" @@ -1820,6 +2079,38 @@ else() set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src") endif() + if(NOT IS_ABSOLUTE "${${contentNameLower}_SOURCE_DIR}") + message(WARNING + "Relative source directory specified. This is not safe, as it depends " + "on the calling directory scope.\n" + " ${${contentNameLower}_SOURCE_DIR}" + ) + set(${contentNameLower}_SOURCE_DIR + "${CMAKE_CURRENT_BINARY_DIR}/${${contentNameLower}_SOURCE_DIR}" + ) + endif() + if(NOT EXISTS "${${contentNameLower}_SOURCE_DIR}") + if(cmp0170 STREQUAL "") + set(cmp0170 WARN) + endif() + string(CONCAT msg + "FETCHCONTENT_FULLY_DISCONNECTED is set to true, which requires the " + "source directory for dependency ${contentName} to already be populated. " + "This generally means it must not be set to true the first time CMake " + "is run in a build directory. The following source directory should " + "already be populated, but it doesn't exist:\n" + " ${${contentNameLower}_SOURCE_DIR}\n" + "Policy CMP0170 controls enforcement of this requirement." + ) + if(cmp0170 STREQUAL "NEW") + message(FATAL_ERROR "${msg}") + elseif(NOT cmp0170 STREQUAL "OLD") + # Note that this is a user warning, not a project author warning. + # The user has set FETCHCONTENT_FULLY_DISCONNECTED in a scenario + # where that is not allowed. + message(WARNING "${msg}") + endif() + endif() if(savedDetails_BINARY_DIR) set(${contentNameLower}_BINARY_DIR ${savedDetails_BINARY_DIR}) @@ -1852,7 +2143,7 @@ endif() endforeach() cmake_language(EVAL CODE " - __FetchContent_directPopulate( + __FetchContent_doPopulation( ${contentNameLower} ${quietFlag} UPDATE_DISCONNECTED ${disconnectUpdates} @@ -2014,7 +2305,13 @@ ) set(__cmake_fcProvider_${__cmake_contentNameLower} YES) - cmake_language(EVAL CODE "${__cmake_providerCommand}(${__cmake_providerArgs})") + + # The provider needs to see policies from our caller, so we need a + # helper macro defined outside our policy block. We pass through a + # variable name rather than variable contents to avoid any potential + # problems with parsing macro arguments. + set(__cmake_fcCode "${__cmake_providerCommand}(${__cmake_providerArgs})") + __FetchContent_MakeAvailable_eval_code(__cmake_fcCode) list(POP_BACK __cmake_fcCurrentVarsStack __cmake_contentNameLower @@ -2025,6 +2322,7 @@ unset(CMAKE_EXPORT_FIND_PACKAGE_NAME) endif() + unset(__cmake_fcCode) unset(__cmake_fcProvider_${__cmake_contentNameLower}) unset(__cmake_providerArgs) unset(__cmake_addfpargs) @@ -2059,7 +2357,9 @@ ${__cmake_contentName} ${__cmake_contentNameLower} ) - find_package(${__cmake_contentName} ${__cmake_fpArgs}) + # We pass variable names rather than their contents so as to avoid any + # potential problems with macro argument parsing + __FetchContent_MakeAvailable_find_package(__cmake_contentName __cmake_fpArgs) list(POP_BACK __cmake_fcCurrentNameStack __cmake_contentNameLower __cmake_contentName @@ -2078,7 +2378,11 @@ FetchContent_GetProperties(${__cmake_contentName}) if(NOT ${__cmake_contentNameLower}_POPULATED) - FetchContent_Populate(${__cmake_contentName}) + cmake_policy(GET CMP0170 __cmake_fc_cmp0170 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + __FetchContent_Populate(${__cmake_contentName} "${__cmake_fc_cmp0170}") + unset(__cmake_fc_cmp0170) __FetchContent_setupFindPackageRedirection(${__cmake_contentName}) # Only try to call add_subdirectory() if the populated content @@ -2116,7 +2420,12 @@ if(__cmake_arg_SYSTEM) list(APPEND __cmake_add_subdirectory_args SYSTEM) endif() - add_subdirectory(${__cmake_add_subdirectory_args}) + + # We pass a variable name rather than its contents so as to avoid any + # potential problems with macro argument parsing. It's highly unlikely + # in this case, but still theoretically possible someone might try to + # use a directory name that looks like a CMake variable evaluation. + __FetchContent_MakeAvailable_add_subdirectory(__cmake_add_subdirectory_args) list(POP_BACK __cmake_fcCurrentVarsStack CMAKE_EXPORT_FIND_PACKAGE_NAME) if(CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "<<::VAR_NOT_SET::>>") @@ -2148,3 +2457,21 @@ unset(__cmake_original_verify_setting) endmacro() + +endblock() # End of FetchContent module's policy scope + +# These are factored out here outside our policies block to preserve policy +# settings of the scope from which FetchContent was included. Any project or +# user code that actually relies on this is fragile and should enforce its own +# policies instead, but we keep these here to preserve backward compatibility. +macro(__FetchContent_MakeAvailable_eval_code code_var) + cmake_language(EVAL CODE "${${code_var}}") +endmacro() + +macro(__FetchContent_MakeAvailable_find_package first_arg_var remaining_args_var) + find_package(${${first_arg_var}} ${${remaining_args_var}}) +endmacro() + +macro(__FetchContent_MakeAvailable_add_subdirectory args_var) + add_subdirectory(${${args_var}}) +endmacro()
diff --git a/Modules/FindALSA.cmake b/Modules/FindALSA.cmake index 627866a..e332c14 100644 --- a/Modules/FindALSA.cmake +++ b/Modules/FindALSA.cmake
@@ -43,6 +43,9 @@ the absolute path of the asound library #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(ALSA_INCLUDE_DIR NAMES alsa/asoundlib.h DOC "The ALSA (asound) include directory" ) @@ -74,3 +77,5 @@ endif() mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY) + +cmake_policy(POP)
diff --git a/Modules/FindArmadillo.cmake b/Modules/FindArmadillo.cmake index f959356..8faa653 100644 --- a/Modules/FindArmadillo.cmake +++ b/Modules/FindArmadillo.cmake
@@ -34,6 +34,9 @@ ARMADILLO_VERSION_NAME - name of the version (ex: "Antipodean Antileech") #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(ARMADILLO_INCLUDE_DIR NAMES armadillo PATHS "$ENV{ProgramFiles}/Armadillo/include" @@ -133,3 +136,5 @@ unset(_ARMA_USE_HDF5) unset(_ARMA_CONFIG_CONTENTS) unset(_ARMA_HEADER_CONTENTS) + +cmake_policy(POP)
diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index e9b118f..2fc01d0 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake
@@ -164,6 +164,11 @@ Intel MKL v10+ 64 bit, single dynamic library +``libblastrampoline`` + .. versionadded:: 3.30 + + A BLAS/LAPACK demuxing library using PLT trampolines + ``NVHPC`` .. versionadded:: 3.21 @@ -1352,6 +1357,31 @@ unset(_blas_nvhpc_lib) endif() +# libblastrampoline? (https://github.com/JuliaLinearAlgebra/libblastrampoline/tree/main) +if(BLA_VENDOR STREQUAL "libblastrampoline" OR BLA_VENDOR STREQUAL "All") + set(_blas_libblastrampoline_lib "blastrampoline") + + if(WIN32) + # Windows appends the version number to the library + string(APPEND _blas_libblastrampoline_lib "-5") + endif() + + if(NOT BLAS_LIBRARIES) + check_blas_libraries( + BLAS_LIBRARIES + BLAS + sgemm + "" + "${_blas_libblastrampoline_lib}" + "" + "" + "" + ) + endif() + + unset(_blas_libblastrampoline_lib) +endif() + # Generic BLAS library? if(BLA_VENDOR STREQUAL "Generic" OR BLA_VENDOR STREQUAL "All")
diff --git a/Modules/FindBZip2.cmake b/Modules/FindBZip2.cmake index 326e700..4714dfc 100644 --- a/Modules/FindBZip2.cmake +++ b/Modules/FindBZip2.cmake
@@ -55,6 +55,9 @@ Superseded by ``BZIP2_VERSION``. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + set(_BZIP2_PATHS PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Bzip2;InstallPath]" ) @@ -120,3 +123,5 @@ endif () mark_as_advanced(BZIP2_INCLUDE_DIR) + +cmake_policy(POP)
diff --git a/Modules/FindBacktrace.cmake b/Modules/FindBacktrace.cmake index 46b62d2..4d3186c 100644 --- a/Modules/FindBacktrace.cmake +++ b/Modules/FindBacktrace.cmake
@@ -36,6 +36,17 @@ #endif And then reference that generated header file in actual source. + +Imported Targets +^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.30 + +This module defines the following :prop_tgt:`IMPORTED` targets: + +``Backtrace::Backtrace`` + An interface library providing usage requirements for the found components. + #]=======================================================================] include(CMakePushCheckState) @@ -89,3 +100,14 @@ find_package_handle_standard_args(Backtrace FOUND_VAR Backtrace_FOUND REQUIRED_VARS ${_Backtrace_STD_ARGS}) mark_as_advanced(Backtrace_HEADER Backtrace_INCLUDE_DIR Backtrace_LIBRARY) + +if(Backtrace_FOUND AND NOT TARGET Backtrace::Backtrace) + if(Backtrace_LIBRARY) + add_library(Backtrace::Backtrace UNKNOWN IMPORTED) + set_property(TARGET Backtrace::Backtrace PROPERTY IMPORTED_LOCATION "${Backtrace_LIBRARY}") + else() + add_library(Backtrace::Backtrace INTERFACE IMPORTED) + target_link_libraries(Backtrace::Backtrace INTERFACE ${Backtrace_LIBRARIES}) + endif() + target_include_directories(Backtrace::Backtrace INTERFACE ${Backtrace_INCLUDE_DIRS}) +endif()
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index 54d1a78..aed1854 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake
@@ -5,6 +5,11 @@ FindBoost --------- +.. versionchanged:: 3.30 + This module is available only if policy :policy:`CMP0167` is not set to + ``NEW``. Port projects to upstream Boost's ``BoostConfig.cmake`` package + configuration file, for which ``find_package(Boost)`` now searches. + Find Boost include dirs and libraries Use this module by invoking :command:`find_package` with the form: @@ -379,6 +384,16 @@ Set ``Boost_NO_BOOST_CMAKE`` to ``ON``, to disable the search for boost-cmake. #]=======================================================================] +cmake_policy(GET CMP0167 _FindBoost_CMP0167) +if(_FindBoost_CMP0167 STREQUAL "NEW") + message(FATAL_ERROR "The FindBoost module has been removed by policy CMP0167.") +endif() + +if(_FindBoost_testing) + set(_FindBoost_included TRUE) + return() +endif() + # The FPHSA helper provides standard way of reporting final search results to # the user including the version and component checks. include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) @@ -387,6 +402,7 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST cmake_policy(SET CMP0102 NEW) # if mark_as_advanced(non_cache_var) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> function(_boost_get_existing_target component target_var) set(names "${component}") @@ -1394,7 +1410,7 @@ set(_Boost_THREAD_DEPENDENCIES chrono atomic) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.86.0 AND NOT Boost_NO_WARN_NEW_VERSIONS) + if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.87.0 AND NOT Boost_NO_WARN_NEW_VERSIONS) message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") endif() endif() @@ -1669,7 +1685,7 @@ # _Boost_COMPONENT_HEADERS. See the instructions at the top of # _Boost_COMPONENT_DEPENDENCIES. set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} - "1.85.0" "1.85" "1.84.0" "1.84" + "1.86.0" "1.86" "1.85.0" "1.85" "1.84.0" "1.84" "1.83.0" "1.83" "1.82.0" "1.82" "1.81.0" "1.81" "1.80.0" "1.80" "1.79.0" "1.79" "1.78.0" "1.78" "1.77.0" "1.77" "1.76.0" "1.76" "1.75.0" "1.75" "1.74.0" "1.74" "1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69"
diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index 203a473..130b239 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake
@@ -891,26 +891,29 @@ SET (CUDA_TOOLKIT_ROOT $ENV{CUDA_TOOLKIT_ROOT}) if(CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a") # Support for NVPACK - set (CUDA_TOOLKIT_TARGET_NAME "armv7-linux-androideabi") + set (CUDA_TOOLKIT_TARGET_NAMES "armv7-linux-androideabi") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") # Support for arm cross compilation - set(CUDA_TOOLKIT_TARGET_NAME "armv7-linux-gnueabihf") + set(CUDA_TOOLKIT_TARGET_NAMES "armv7-linux-gnueabihf") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") # Support for aarch64 cross compilation if (ANDROID_ARCH_NAME STREQUAL "arm64") - set(CUDA_TOOLKIT_TARGET_NAME "aarch64-linux-androideabi") + set(CUDA_TOOLKIT_TARGET_NAMES "aarch64-linux-androideabi") elseif (CMAKE_SYSTEM_NAME STREQUAL "QNX") - set(CUDA_TOOLKIT_TARGET_NAME "aarch64-qnx") + set(CUDA_TOOLKIT_TARGET_NAMES "aarch64-qnx") else() - set(CUDA_TOOLKIT_TARGET_NAME "aarch64-linux") + set(CUDA_TOOLKIT_TARGET_NAMES "aarch64-linux" "sbsa-linux") endif (ANDROID_ARCH_NAME STREQUAL "arm64") endif() - if (EXISTS "${CUDA_TOOLKIT_ROOT}/targets/${CUDA_TOOLKIT_TARGET_NAME}") - set(CUDA_TOOLKIT_TARGET_DIR "${CUDA_TOOLKIT_ROOT}/targets/${CUDA_TOOLKIT_TARGET_NAME}" CACHE PATH "CUDA Toolkit target location.") - SET (CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT}) - mark_as_advanced(CUDA_TOOLKIT_TARGET_DIR) - endif() + foreach(CUDA_TOOLKIT_TARGET_NAME IN LISTS CUDA_TOOLKIT_TARGET_NAMES) + if (EXISTS "${CUDA_TOOLKIT_ROOT}/targets/${CUDA_TOOLKIT_TARGET_NAME}") + set(CUDA_TOOLKIT_TARGET_DIR "${CUDA_TOOLKIT_ROOT}/targets/${CUDA_TOOLKIT_TARGET_NAME}" CACHE PATH "CUDA Toolkit target location.") + SET (CUDA_TOOLKIT_ROOT_DIR ${CUDA_TOOLKIT_ROOT} CACHE PATH "Toolkit location." FORCE) + mark_as_advanced(CUDA_TOOLKIT_TARGET_DIR) + break() + endif() + endforeach() # add known CUDA targetr root path to the set of directories we search for programs, libraries and headers set( CMAKE_FIND_ROOT_PATH "${CUDA_TOOLKIT_TARGET_DIR};${CMAKE_FIND_ROOT_PATH}")
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index 2229de6..da33be2 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake
@@ -22,7 +22,11 @@ 1. If the ``CUDA`` language has been enabled we will use the directory containing the compiler as the first search location for ``nvcc``. -2. If the ``CUDAToolkit_ROOT`` cmake configuration variable (e.g., +2. If the variable :variable:`CMAKE_CUDA_COMPILER <CMAKE_<LANG>_COMPILER>` or + the environment variable :envvar:`CUDACXX` is defined, it will be used + as the path to the ``nvcc`` executable. + +3. If the ``CUDAToolkit_ROOT`` cmake configuration variable (e.g., ``-DCUDAToolkit_ROOT=/some/path``) *or* environment variable is defined, it will be searched. If both an environment variable **and** a configuration variable are specified, the *configuration* variable takes @@ -32,19 +36,19 @@ the appropriate ``version.txt`` or ``version.json`` file can be found underneath the specified directory. -3. If the CUDA_PATH environment variable is defined, it will be searched +4. If the CUDA_PATH environment variable is defined, it will be searched for ``nvcc``. -4. The user's path is searched for ``nvcc`` using :command:`find_program`. If +5. The user's path is searched for ``nvcc`` using :command:`find_program`. If this is found, no subsequent search attempts are performed. Users are responsible for ensuring that the first ``nvcc`` to show up in the path is the desired path in the event that multiple CUDA Toolkits are installed. -5. On Unix systems, if the symbolic link ``/usr/local/cuda`` exists, this is +6. On Unix systems, if the symbolic link ``/usr/local/cuda`` exists, this is used. No subsequent search attempts are performed. No default symbolic link location exists for the Windows platform. -6. The platform specific default install locations are searched. If exactly one +7. The platform specific default install locations are searched. If exactly one candidate is found, this is used. The default CUDA Toolkit install locations searched are: @@ -123,6 +127,8 @@ - :ref:`nvidia-ML<cuda_toolkit_nvML>` - :ref:`nvPTX Compiler<cuda_toolkit_nvptx>` - :ref:`nvRTC<cuda_toolkit_nvRTC>` +- :ref:`nvJitLink<cuda_toolkit_nvJitLink>` +- :ref:`nvFatBin<cuda_toolkit_nvfatbin>` - :ref:`nvToolsExt<cuda_toolkit_nvToolsExt>` - :ref:`nvtx3<cuda_toolkit_nvtx3>` - :ref:`OpenCL<cuda_toolkit_opencl>` @@ -158,7 +164,7 @@ cuBLAS """""" -The `cuBLAS <https://docs.nvidia.com/cuda/cublas/index.html>`_ library. +The `cuBLAS <https://docs.nvidia.com/cuda/cublas>`_ library. Targets Created: @@ -174,7 +180,7 @@ .. versionadded:: 3.27 -The NVIDIA Tegra Deep Learning Accelerator `cuDLA <https://docs.nvidia.com/cuda/cublas/index.html>`_ library. +The NVIDIA Tegra Deep Learning Accelerator `cuDLA <https://docs.nvidia.com/cuda/cublas>`_ library. Targets Created: @@ -187,7 +193,7 @@ .. versionadded:: 3.25 -The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/gpudirect-storage/api-reference-guide/index.html>`_ library. +The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/gpudirect-storage/api-reference-guide>`_ library. Targets Created: @@ -201,7 +207,7 @@ cuFFT """"" -The `cuFFT <https://docs.nvidia.com/cuda/cufft/index.html>`_ library. +The `cuFFT <https://docs.nvidia.com/cuda/cufft>`_ library. Targets Created: @@ -214,7 +220,7 @@ cuRAND """""" -The `cuRAND <https://docs.nvidia.com/cuda/curand/index.html>`_ library. +The `cuRAND <https://docs.nvidia.com/cuda/curand>`_ library. Targets Created: @@ -226,7 +232,7 @@ cuSOLVER """""""" -The `cuSOLVER <https://docs.nvidia.com/cuda/cusolver/index.html>`_ library. +The `cuSOLVER <https://docs.nvidia.com/cuda/cusolver>`_ library. Targets Created: @@ -238,7 +244,7 @@ cuSPARSE """""""" -The `cuSPARSE <https://docs.nvidia.com/cuda/cusparse/index.html>`_ library. +The `cuSPARSE <https://docs.nvidia.com/cuda/cusparse>`_ library. Targets Created: @@ -269,7 +275,7 @@ NPP """ -The `NPP <https://docs.nvidia.com/cuda/npp/index.html>`_ libraries. +The `NPP <https://docs.nvidia.com/cuda/npp>`_ libraries. Targets Created: @@ -339,7 +345,7 @@ nvBLAS """""" -The `nvBLAS <https://docs.nvidia.com/cuda/nvblas/index.html>`_ libraries. +The `nvBLAS <https://docs.nvidia.com/cuda/nvblas>`_ libraries. This is a shared library only. Targets Created: @@ -365,7 +371,7 @@ nvJPEG """""" -The `nvJPEG <https://docs.nvidia.com/cuda/nvjpeg/index.html>`_ library. +The `nvJPEG <https://docs.nvidia.com/cuda/nvjpeg>`_ library. Introduced in CUDA 10. Targets Created: @@ -380,7 +386,7 @@ .. versionadded:: 3.25 -The `nvPTX <https://docs.nvidia.com/cuda/ptx-compiler-api/index.html>`_ (PTX Compilation) library. +The `nvPTX <https://docs.nvidia.com/cuda/ptx-compiler-api>`_ (PTX Compilation) library. The PTX Compiler APIs are a set of APIs which can be used to compile a PTX program into GPU assembly code. Introduced in CUDA 11.1 This is a static library only. @@ -394,7 +400,7 @@ nvRTC """"" -The `nvRTC <https://docs.nvidia.com/cuda/nvrtc/index.html>`_ (Runtime Compilation) library. +The `nvRTC <https://docs.nvidia.com/cuda/nvrtc>`_ (Runtime Compilation) library. Targets Created: @@ -418,12 +424,26 @@ - ``CUDA::nvJitLink`` starting in CUDA 12.0 - ``CUDA::nvJitLink_static`` starting in CUDA 12.0 +.. _`cuda_toolkit_nvfatbin`: + +nvFatBin +""""""""" + +.. versionadded:: 3.30 + +The `nvFatBin <https://docs.nvidia.com/cuda/>`_ (Runtime fatbin creation) library. + +Targets Created: + +- ``CUDA::nvfatbin`` starting in CUDA 12.4 +- ``CUDA::nvfatbin_static`` starting in CUDA 12.4 + .. _`cuda_toolkit_nvml`: nvidia-ML """"""""" -The `NVIDIA Management Library <https://developer.nvidia.com/nvidia-management-library-nvml>`_. +The `NVIDIA Management Library <https://developer.nvidia.com/management-library-nvml>`_. This is a shared library only. Targets Created: @@ -451,7 +471,7 @@ .. versionadded:: 3.25 -The header-only `NVIDIA Tools Extension Library <https://nvidia.github.io/NVTX/doxygen/index.html>`_. +The header-only `NVIDIA Tools Extension Library <https://nvidia.github.io/NVTX/doxygen>`_. Introduced in CUDA 10.0. Targets created: @@ -612,9 +632,27 @@ endif() else() function(_CUDAToolkit_find_root_dir ) - cmake_parse_arguments(arg "" "" "SEARCH_PATHS;FIND_FLAGS" ${ARGN}) + cmake_parse_arguments(arg "COMPILER_PATHS" "" "SEARCH_PATHS;FIND_FLAGS" ${ARGN}) if(NOT CUDAToolkit_BIN_DIR) + if(arg_COMPILER_PATHS) + # need to find parent dir, since this could clang and not nvcc + if(EXISTS "${CMAKE_CUDA_COMPILER}") + get_filename_component(possible_nvcc_path "${CMAKE_CUDA_COMPILER}" PROGRAM PROGRAM_ARGS CUDAToolkit_compiler_args) + get_filename_component(possible_nvcc_path "${possible_nvcc_path}" DIRECTORY) + elseif(EXISTS "$ENV{CUDACXX}") + get_filename_component(possible_nvcc_path "$ENV{CUDACXX}" PROGRAM PROGRAM_ARGS CUDAToolkit_compiler_args) + get_filename_component(possible_nvcc_path "${possible_nvcc_path}" DIRECTORY) + endif() + if(possible_nvcc_path) + find_program(CUDAToolkit_NVCC_EXECUTABLE + NAMES nvcc nvcc.exe + NO_DEFAULT_PATH + PATHS ${possible_nvcc_path} + ) + endif() + endif() + if(NOT CUDAToolkit_SENTINEL_FILE) find_program(CUDAToolkit_NVCC_EXECUTABLE NAMES nvcc nvcc.exe @@ -759,6 +797,7 @@ endif() # Try user provided path + _CUDAToolkit_find_root_dir(COMPILER_PATHS) if(NOT CUDAToolkit_ROOT_DIR AND CUDAToolkit_ROOT) _CUDAToolkit_find_root_dir(SEARCH_PATHS "${CUDAToolkit_ROOT}" FIND_FLAGS PATH_SUFFIXES bin NO_DEFAULT_PATH) endif() @@ -1168,6 +1207,11 @@ _CUDAToolkit_find_and_add_import_lib(nvJitLink_static DEPS cudart_static_deps) endif() + if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.4.0) + _CUDAToolkit_find_and_add_import_lib(nvfatbin DEPS cudart_static_deps) + _CUDAToolkit_find_and_add_import_lib(nvfatbin_static DEPS cudart_static_deps) + endif() + _CUDAToolkit_find_and_add_import_lib(culibos) # it's a static library foreach (cuda_lib cublasLt cufft nvjpeg) _CUDAToolkit_find_and_add_import_lib(${cuda_lib}) @@ -1183,7 +1227,7 @@ if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.0.0) # cublas depends on cublasLt - # https://docs.nvidia.com/cuda/archive/11.0/cublas/index.html#static-library + # https://docs.nvidia.com/cuda/archive/11.0/cublas#static-library _CUDAToolkit_find_and_add_import_lib(cublas DEPS cublasLt culibos) _CUDAToolkit_find_and_add_import_lib(cublas_static DEPS cublasLt_static culibos) else() @@ -1216,14 +1260,14 @@ set(cusolver_static_deps cublas_static cusparse_static culibos) if(CUDAToolkit_VERSION VERSION_GREATER 11.2.1) # cusolver depends on libcusolver_metis and cublasLt - # https://docs.nvidia.com/cuda/archive/11.2.2/cusolver/index.html#link-dependency + # https://docs.nvidia.com/cuda/archive/11.2.2/cusolver#link-dependency list(APPEND cusolver_deps cublasLt) _CUDAToolkit_find_and_add_import_lib(cusolver_metis_static ALT metis_static) # implementation detail static lib list(APPEND cusolver_static_deps cusolver_metis_static cublasLt_static) endif() if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.1.2) # cusolver depends on liblapack_static.a starting with CUDA 10.1 update 2, - # https://docs.nvidia.com/cuda/archive/11.5.0/cusolver/index.html#static-link-lapack + # https://docs.nvidia.com/cuda/archive/11.5.0/cusolver#static-link-lapack _CUDAToolkit_find_and_add_import_lib(cusolver_lapack_static ALT lapack_static) # implementation detail static lib list(APPEND cusolver_static_deps cusolver_lapack_static) endif() @@ -1281,17 +1325,17 @@ if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.1.0) if(NOT TARGET CUDA::nvptxcompiler_static) - _CUDAToolkit_find_and_add_import_lib(nvptxcompiler_static DEPS cuda_driver) + _CUDAToolkit_find_and_add_import_lib(nvptxcompiler_static) if(TARGET CUDA::nvptxcompiler_static) target_link_libraries(CUDA::nvptxcompiler_static INTERFACE CUDA::cudart_static_deps) endif() endif() endif() - _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins ALT nvrtc-builtins DEPS cuda_driver) + _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins ALT nvrtc-builtins) _CUDAToolkit_find_and_add_import_lib(nvrtc DEPS nvrtc_builtins nvJitLink) if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.5.0) - _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins_static ALT nvrtc-builtins_static DEPS cuda_driver) + _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins_static ALT nvrtc-builtins_static) if(NOT TARGET CUDA::nvrtc_static) _CUDAToolkit_find_and_add_import_lib(nvrtc_static DEPS nvrtc_builtins_static nvptxcompiler_static nvJitLink_static) if(TARGET CUDA::nvrtc_static AND WIN32 AND NOT (BORLAND OR MINGW OR CYGWIN))
diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake index 5e5f1f8..5ce8a90 100644 --- a/Modules/FindCURL.cmake +++ b/Modules/FindCURL.cmake
@@ -72,6 +72,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) if(NOT CURL_NO_CURL_CMAKE) @@ -86,6 +89,8 @@ find_package_handle_standard_args(CURL HANDLE_COMPONENTS CONFIG_MODE) # The upstream curl package sets CURL_VERSION, not CURL_VERSION_STRING. set(CURL_VERSION_STRING "${CURL_VERSION}") + + cmake_policy(POP) return() endif() endif() @@ -117,6 +122,8 @@ curllib_static # Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip): libcurl + # Some Windows prebuilt versions distribute `libcurl_a.lib` instead of `libcurl.lib` + libcurl_a NAMES_PER_DIR HINTS ${PC_CURL_LIBRARY_DIRS} ) @@ -239,3 +246,5 @@ endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindCups.cmake b/Modules/FindCups.cmake index cf0d341..ceed009 100644 --- a/Modules/FindCups.cmake +++ b/Modules/FindCups.cmake
@@ -41,6 +41,9 @@ the directory containing the Cups headers #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(CUPS_INCLUDE_DIR cups/cups.h ) find_library(CUPS_LIBRARIES NAMES cups ) @@ -98,3 +101,5 @@ INTERFACE_INCLUDE_DIRECTORIES "${CUPS_INCLUDE_DIR}") endif () endif () + +cmake_policy(POP)
diff --git a/Modules/FindEXPAT.cmake b/Modules/FindEXPAT.cmake index 762931e..398a415 100644 --- a/Modules/FindEXPAT.cmake +++ b/Modules/FindEXPAT.cmake
@@ -41,6 +41,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_package(PkgConfig QUIET) pkg_check_modules(PC_EXPAT QUIET expat) @@ -156,3 +159,5 @@ endif() mark_as_advanced(EXPAT_INCLUDE_DIR EXPAT_LIBRARY) + +cmake_policy(POP)
diff --git a/Modules/FindFontconfig.cmake b/Modules/FindFontconfig.cmake index 5228831..9cce952 100644 --- a/Modules/FindFontconfig.cmake +++ b/Modules/FindFontconfig.cmake
@@ -34,6 +34,8 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls @@ -101,3 +103,5 @@ set(Fontconfig_LIBRARIES ${Fontconfig_LIBRARY}) set(Fontconfig_INCLUDE_DIRS ${Fontconfig_INCLUDE_DIR}) endif() + +cmake_policy(POP)
diff --git a/Modules/FindFreetype.cmake b/Modules/FindFreetype.cmake index 82885cb..6d0ed68 100644 --- a/Modules/FindFreetype.cmake +++ b/Modules/FindFreetype.cmake
@@ -47,6 +47,9 @@ directory of a Freetype installation. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # Created by Eric Wing. # Modifications by Alexander Neundorf. # This file has been renamed to "FindFreetype.cmake" instead of the correct @@ -202,3 +205,5 @@ endif() endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindGDAL.cmake b/Modules/FindGDAL.cmake index e6eac30..a9c5740 100644 --- a/Modules/FindGDAL.cmake +++ b/Modules/FindGDAL.cmake
@@ -72,6 +72,9 @@ # #include "gdal.h" +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(GDAL_INCLUDE_DIR gdal.h HINTS ENV GDAL_DIR @@ -197,3 +200,5 @@ INTERFACE_INCLUDE_DIRECTORIES "${GDAL_INCLUDE_DIR}") endif () endif () + +cmake_policy(POP)
diff --git a/Modules/FindGIF.cmake b/Modules/FindGIF.cmake index cea9cd8..9a11b88 100644 --- a/Modules/FindGIF.cmake +++ b/Modules/FindGIF.cmake
@@ -46,6 +46,9 @@ ``./configure --prefix=$GIF_DIR``. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # Created by Eric Wing. # Modifications by Alexander Neundorf, Ben Campbell @@ -126,3 +129,5 @@ endif() mark_as_advanced(GIF_INCLUDE_DIR GIF_LIBRARY) + +cmake_policy(POP)
diff --git a/Modules/FindGLEW.cmake b/Modules/FindGLEW.cmake index dff53e1..f6b7b1a 100644 --- a/Modules/FindGLEW.cmake +++ b/Modules/FindGLEW.cmake
@@ -223,7 +223,10 @@ # Read version from GL/glew.h file if(EXISTS "${GLEW_INCLUDE_DIR}/GL/glew.h") + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${GLEW_INCLUDE_DIR}/GL/glew.h" _contents REGEX "^VERSION_.+ [0-9]+") + cmake_policy(POP) if(_contents) string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" GLEW_VERSION_MAJOR "${_contents}") string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" GLEW_VERSION_MINOR "${_contents}")
diff --git a/Modules/FindGSL.cmake b/Modules/FindGSL.cmake index 1943847..e9d8109 100644 --- a/Modules/FindGSL.cmake +++ b/Modules/FindGSL.cmake
@@ -59,6 +59,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) #============================================================================= @@ -229,3 +232,5 @@ INTERFACE_LINK_LIBRARIES GSL::gslcblas ) endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindGTK2.cmake b/Modules/FindGTK2.cmake index 4634876..f91d2fd 100644 --- a/Modules/FindGTK2.cmake +++ b/Modules/FindGTK2.cmake
@@ -175,7 +175,10 @@ include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) function(_GTK2_GET_VERSION _OUT_major _OUT_minor _OUT_micro _gtkversion_hdr) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS ${_gtkversion_hdr} _contents REGEX "#define GTK_M[A-Z]+_VERSION[ \t]+") + cmake_policy(POP) if(_contents) string(REGEX REPLACE ".*#define GTK_MAJOR_VERSION[ \t]+\\(([0-9]+)\\).*" "\\1" ${_OUT_major} "${_contents}") string(REGEX REPLACE ".*#define GTK_MINOR_VERSION[ \t]+\\(([0-9]+)\\).*" "\\1" ${_OUT_minor} "${_contents}") @@ -211,7 +214,10 @@ #============================================================= function(_GTK2_SIGCXX_GET_VERSION _OUT_major _OUT_minor _OUT_micro _sigcxxversion_hdr) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS ${_sigcxxversion_hdr} _contents REGEX "#define SIGCXX_M[A-Z]+_VERSION[ \t]+") + cmake_policy(POP) if(_contents) string(REGEX REPLACE ".*#define SIGCXX_MAJOR_VERSION[ \t]+([0-9]+).*" "\\1" ${_OUT_major} "${_contents}") string(REGEX REPLACE ".*#define SIGCXX_MINOR_VERSION[ \t]+([0-9]+).*" "\\1" ${_OUT_minor} "${_contents}") @@ -300,6 +306,10 @@ endif() find_path(GTK2_${_var}_INCLUDE_DIR ${_hdr} PATHS + ${PC_GLIB2_INCLUDEDIR} + ${PC_GLIB2_LIBDIR} + ${PC_GTK2_INCLUDEDIR} + ${PC_GTK2_LIBDIR} ${_gtk2_arch_dir} /usr/local/libx32 /usr/local/lib64 @@ -610,6 +620,23 @@ set(GTK2_FIND_COMPONENTS gtk) endif() +# Retrieve LIBDIR from the GTK2 and GLIB2 pkg-config files, which are +# used to compute the arch-specific include prefixes. While at it, +# also retrieve their INCLUDEDIR, to accommodate non-standard layouts. +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_GTK2 QUIET gtk+-2.0) + if(PC_GTK2_FOUND) + pkg_get_variable(PC_GTK2_INCLUDEDIR gtk+-2.0 includedir) + pkg_get_variable(PC_GTK2_LIBDIR gtk+-2.0 libdir) + endif() + pkg_check_modules(PC_GLIB2 QUIET glib-2.0) + if(PC_GLIB2_FOUND) + pkg_get_variable(PC_GLIB2_INCLUDEDIR glib-2.0 includedir) + pkg_get_variable(PC_GLIB2_LIBDIR glib-2.0 libdir) + endif() +endif() + # # If specified, enforce version number #
diff --git a/Modules/FindGTest.cmake b/Modules/FindGTest.cmake index 92334e4..c2128a9 100644 --- a/Modules/FindGTest.cmake +++ b/Modules/FindGTest.cmake
@@ -125,7 +125,10 @@ if(EXISTS "${${_var}}") file(TO_NATIVE_PATH "${${_var}}" _lib_path) get_filename_component(_name "${${_var}}" NAME_WE) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${${_var}}" _match REGEX "${_name}\\.dll" LIMIT_COUNT 1) + cmake_policy(POP) if(NOT _match STREQUAL "") set(${_var}_TYPE SHARED PARENT_SCOPE) else()
diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake index cc6186f..bd21e3d 100644 --- a/Modules/FindHDF5.cmake +++ b/Modules/FindHDF5.cmake
@@ -157,6 +157,9 @@ include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # We haven't found HDF5 yet. Clear its state in case it is set in the parent # scope somewhere else. We can't rely on it because different components may # have been requested for this call. @@ -1243,3 +1246,5 @@ endif() unset(_lang) unset(_HDF5_NEED_TO_SEARCH) + +cmake_policy(POP)
diff --git a/Modules/FindHSPELL.cmake b/Modules/FindHSPELL.cmake index 9724d2c..ca09444 100644 --- a/Modules/FindHSPELL.cmake +++ b/Modules/FindHSPELL.cmake
@@ -25,6 +25,9 @@ HSPELL_MINOR_VERSION - The minor version of Hspell #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(HSPELL_INCLUDE_DIR hspell.h) find_library(HSPELL_LIBRARIES NAMES hspell) @@ -43,3 +46,5 @@ VERSION_VAR HSPELL_VERSION_STRING) mark_as_advanced(HSPELL_INCLUDE_DIR HSPELL_LIBRARIES) + +cmake_policy(POP)
diff --git a/Modules/FindICU.cmake b/Modules/FindICU.cmake index 9514d2d..63c2576 100644 --- a/Modules/FindICU.cmake +++ b/Modules/FindICU.cmake
@@ -24,10 +24,10 @@ This module reports information about the ICU installation in several variables. General variables:: - ICU_VERSION - ICU release version ICU_FOUND - true if the main programs and libraries were found - ICU_LIBRARIES - component libraries to be linked ICU_INCLUDE_DIRS - the directories containing the ICU headers + ICU_LIBRARIES - component libraries to be linked + ICU_VERSION - ICU release version Imported targets:: @@ -83,12 +83,10 @@ In most cases none of the above variables will require setting, unless multiple ICU versions are available and a specific version is required. - -Other variables one may set to control this module are:: - - ICU_DEBUG - Set to ON to enable debug output from FindICU. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> # Written by Roger Leigh <rleigh@codelibre.net> set(icu_programs @@ -179,7 +177,6 @@ # Find all ICU libraries list(APPEND icu_library_suffixes "${_lib64}" "lib") - set(ICU_REQUIRED_LIBS_FOUND ON) set(static_prefix ) # static icu libraries compiled with MSVC have the prefix 's' if(MSVC) @@ -190,8 +187,9 @@ set(component_cache "ICU_${component_upcase}_LIBRARY") set(component_cache_release "${component_cache}_RELEASE") set(component_cache_debug "${component_cache}_DEBUG") - set(component_found "ICU_${component_upcase}_FOUND") + set(component_found "ICU_${component}_FOUND") set(component_found_compat "${component_upcase}_FOUND") + set(component_found_compat2 "ICU_${component_upcase}_FOUND") set(component_libnames "icu${component}") set(component_debug_libnames "icu${component}d") @@ -254,27 +252,17 @@ if(${component_cache}) set("${component_found}" ON) set("${component_found_compat}" ON) + set("${component_found_compat2}" ON) list(APPEND ICU_LIBRARY "${${component_cache}}") - if (ICU_FIND_REQUIRED_${component}) - list(APPEND ICU_LIBS_FOUND "${component} (required): ${${component_cache}}") - else() - list(APPEND ICU_LIBS_FOUND "${component} (optional): ${${component_cache}}") - endif() - else() - if (ICU_FIND_REQUIRED_${component}) - set(ICU_REQUIRED_LIBS_FOUND OFF) - list(APPEND ICU_LIBS_NOTFOUND "${component} (required)") - else() - list(APPEND ICU_LIBS_NOTFOUND "${component} (optional)") - endif() endif() mark_as_advanced("${component_found}") mark_as_advanced("${component_found_compat}") + mark_as_advanced("${component_found_compat2}") set("${component_cache}" "${${component_cache}}" PARENT_SCOPE) set("${component_found}" "${${component_found}}" PARENT_SCOPE) set("${component_found_compat}" "${${component_found_compat}}" PARENT_SCOPE) + set("${component_found_compat2}" "${${component_found_compat2}}" PARENT_SCOPE) endforeach() - set(_ICU_REQUIRED_LIBS_FOUND "${ICU_REQUIRED_LIBS_FOUND}" PARENT_SCOPE) set(ICU_LIBRARY "${ICU_LIBRARY}" PARENT_SCOPE) # Find all ICU data files @@ -303,43 +291,21 @@ mark_as_advanced("${cache_var}") set("${data_var}" "${${cache_var}}" PARENT_SCOPE) endforeach() - - if(NOT ICU_FIND_QUIETLY) - if(ICU_LIBS_FOUND) - message(STATUS "Found the following ICU libraries:") - foreach(found ${ICU_LIBS_FOUND}) - message(STATUS " ${found}") - endforeach() - endif() - if(ICU_LIBS_NOTFOUND) - message(STATUS "The following ICU libraries were not found:") - foreach(notfound ${ICU_LIBS_NOTFOUND}) - message(STATUS " ${notfound}") - endforeach() - endif() - endif() - - if(ICU_DEBUG) - message(STATUS "--------FindICU.cmake search debug--------") - message(STATUS "ICU binary path search order: ${icu_roots}") - message(STATUS "ICU include path search order: ${icu_roots}") - message(STATUS "ICU library path search order: ${icu_roots}") - message(STATUS "----------------") - endif() endfunction() _ICU_FIND() include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(ICU - FOUND_VAR ICU_FOUND - REQUIRED_VARS ICU_INCLUDE_DIR - ICU_LIBRARY - _ICU_REQUIRED_LIBS_FOUND - VERSION_VAR ICU_VERSION - FAIL_MESSAGE "Failed to find all ICU components") - -unset(_ICU_REQUIRED_LIBS_FOUND) +find_package_handle_standard_args(ICU + REQUIRED_VARS + ICU_INCLUDE_DIR + ICU_LIBRARY + VERSION_VAR + ICU_VERSION + HANDLE_COMPONENTS + FAIL_MESSAGE + "Failed to find all ICU components" +) if(ICU_FOUND) set(ICU_INCLUDE_DIRS "${ICU_INCLUDE_DIR}") @@ -393,45 +359,5 @@ endforeach() endif() -if(ICU_DEBUG) - message(STATUS "--------FindICU.cmake results debug--------") - message(STATUS "ICU found: ${ICU_FOUND}") - message(STATUS "ICU_VERSION number: ${ICU_VERSION}") - message(STATUS "ICU_ROOT directory: ${ICU_ROOT}") - message(STATUS "ICU_INCLUDE_DIR directory: ${ICU_INCLUDE_DIR}") - message(STATUS "ICU_LIBRARIES: ${ICU_LIBRARIES}") - - foreach(program IN LISTS icu_programs) - string(TOUPPER "${program}" program_upcase) - set(program_lib "ICU_${program_upcase}_EXECUTABLE") - message(STATUS "${program} program: ${program_lib}=${${program_lib}}") - unset(program_upcase) - unset(program_lib) - endforeach() - - foreach(data IN LISTS icu_data) - string(TOUPPER "${data}" data_upcase) - string(REPLACE "." "_" data_upcase "${data_upcase}") - set(data_lib "ICU_${data_upcase}") - message(STATUS "${data} data: ${data_lib}=${${data_lib}}") - unset(data_upcase) - unset(data_lib) - endforeach() - - foreach(component IN LISTS ICU_FIND_COMPONENTS) - string(TOUPPER "${component}" component_upcase) - set(component_lib "ICU_${component_upcase}_LIBRARIES") - set(component_found "ICU_${component_upcase}_FOUND") - set(component_found_compat "${component_upcase}_FOUND") - message(STATUS "${component} library found: ${component_found}=${${component_found}}") - message(STATUS "${component} library found (compat name): ${component_found_compat}=${${component_found_compat}}") - message(STATUS "${component} library: ${component_lib}=${${component_lib}}") - unset(component_upcase) - unset(component_lib) - unset(component_found) - unset(component_found_compat) - endforeach() - message(STATUS "----------------") -endif() - unset(icu_programs) +cmake_policy(POP)
diff --git a/Modules/FindIconv.cmake b/Modules/FindIconv.cmake index 566330f..879ff16f 100644 --- a/Modules/FindIconv.cmake +++ b/Modules/FindIconv.cmake
@@ -75,6 +75,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + include(${CMAKE_CURRENT_LIST_DIR}/CMakePushCheckState.cmake) if(CMAKE_C_COMPILER_LOADED) include(${CMAKE_CURRENT_LIST_DIR}/CheckCSourceCompiles.cmake) @@ -101,7 +104,7 @@ " #include <stddef.h> #include <iconv.h> - int main() { + int main(void) { char *a, *b; size_t i, j; iconv_t ic; @@ -180,3 +183,5 @@ set_property(TARGET Iconv::Iconv PROPERTY INTERFACE_LINK_LIBRARIES "${Iconv_LIBRARIES}") endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindIntl.cmake b/Modules/FindIntl.cmake index 95eeabd..04f5a23 100644 --- a/Modules/FindIntl.cmake +++ b/Modules/FindIntl.cmake
@@ -82,6 +82,9 @@ ``msgfmt``, etc.), use :module:`FindGettext`. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + include(${CMAKE_CURRENT_LIST_DIR}/CMakePushCheckState.cmake) if(CMAKE_C_COMPILER_LOADED) include(${CMAKE_CURRENT_LIST_DIR}/CheckCSourceCompiles.cmake) @@ -181,3 +184,5 @@ INTERFACE_LINK_LIBRARIES "${Intl_LIBRARIES}") endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindJNI.cmake b/Modules/FindJNI.cmake index abc76cf..82e3d88 100644 --- a/Modules/FindJNI.cmake +++ b/Modules/FindJNI.cmake
@@ -343,7 +343,9 @@ list(APPEND JAVA_JVM_LIBRARY_DIRECTORIES "${dir}" "${dir}/client" + "${dir}/minimal" "${dir}/server" + "${dir}/zero" # IBM SDK, Java Technology Edition, specific paths "${dir}/j9vm" "${dir}/default" @@ -389,8 +391,10 @@ execute_process( COMMAND xcodebuild -version OUTPUT_VARIABLE _FindJNI_XCODEBUILD_VERSION + ERROR_VARIABLE _FindJNI_XCODEBUILD_VERSION + RESULT_VARIABLE _FindJNI_XCODEBUILD_RESULT ) - if(_FindJNI_XCODEBUILD_VERSION MATCHES "Xcode ([0-9]+(\\.[0-9]+)*)") + if(_FindJNI_XCODEBUILD_RESULT EQUAL 0 AND _FindJNI_XCODEBUILD_VERSION MATCHES "Xcode ([0-9]+(\\.[0-9]+)*)") set(_FindJNI_XCODE_VERSION "${CMAKE_MATCH_1}") else() set(_FindJNI_XCODE_VERSION "")
diff --git a/Modules/FindJPEG.cmake b/Modules/FindJPEG.cmake index 3f243de..08a0bd3 100644 --- a/Modules/FindJPEG.cmake +++ b/Modules/FindJPEG.cmake
@@ -56,6 +56,9 @@ where to find the JPEG library. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(JPEG_INCLUDE_DIR jpeglib.h) set(jpeg_names ${JPEG_NAMES} jpeg jpeg-static libjpeg libjpeg-static) @@ -140,3 +143,5 @@ endif() mark_as_advanced(JPEG_LIBRARY JPEG_INCLUDE_DIR) + +cmake_policy(POP)
diff --git a/Modules/FindJasper.cmake b/Modules/FindJasper.cmake index ec2907d..79afcd5 100644 --- a/Modules/FindJasper.cmake +++ b/Modules/FindJasper.cmake
@@ -42,6 +42,9 @@ where to find the Jasper library (debug). #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(JASPER_INCLUDE_DIR jasper/jasper.h) mark_as_advanced(JASPER_INCLUDE_DIR) @@ -88,3 +91,5 @@ endif() endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index e142516..bbf25b6 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake
@@ -742,6 +742,14 @@ unset(_lapack_nvhpc_flags) endif() + # libblastrampoline? (https://github.com/JuliaLinearAlgebra/libblastrampoline/tree/main) + if(NOT LAPACK_LIBRARIES + AND (BLA_VENDOR STREQUAL "libblastrampoline" OR BLA_VENDOR STREQUAL "All")) + if(BLAS_LIBRARIES MATCHES "blastrampoline.+") + set(LAPACK_LIBRARIES ${BLAS_LIBRARIES}) + endif() + endif() + # Generic LAPACK library? if(NOT LAPACK_LIBRARIES AND (BLA_VENDOR STREQUAL "Generic"
diff --git a/Modules/FindLTTngUST.cmake b/Modules/FindLTTngUST.cmake index eaace4f..34b2e82 100644 --- a/Modules/FindLTTngUST.cmake +++ b/Modules/FindLTTngUST.cmake
@@ -37,6 +37,9 @@ ``TRUE`` if the ``tracelog()`` API is available in the system's LTTng-UST #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(LTTNGUST_INCLUDE_DIRS NAMES lttng/tracepoint.h) find_library(LTTNGUST_LIBRARIES NAMES lttng-ust) @@ -100,3 +103,5 @@ LTTNGUST_INCLUDE_DIRS VERSION_VAR LTTNGUST_VERSION_STRING) mark_as_advanced(LTTNGUST_LIBRARIES LTTNGUST_INCLUDE_DIRS) + +cmake_policy(POP)
diff --git a/Modules/FindLibArchive.cmake b/Modules/FindLibArchive.cmake index 9d3ac13..38a43db 100644 --- a/Modules/FindLibArchive.cmake +++ b/Modules/FindLibArchive.cmake
@@ -31,6 +31,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(LibArchive_INCLUDE_DIR NAMES archive.h PATHS @@ -81,3 +84,5 @@ INTERFACE_INCLUDE_DIRECTORIES "${LibArchive_INCLUDE_DIR}") endif () endif() + +cmake_policy(POP)
diff --git a/Modules/FindLibLZMA.cmake b/Modules/FindLibLZMA.cmake index 1b3929b..7b0c530 100644 --- a/Modules/FindLibLZMA.cmake +++ b/Modules/FindLibLZMA.cmake
@@ -58,6 +58,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(LIBLZMA_INCLUDE_DIR lzma.h ) if(NOT LIBLZMA_LIBRARY) find_library(LIBLZMA_LIBRARY_RELEASE NAMES lzma liblzma NAMES_PER_DIR PATH_SUFFIXES lib) @@ -140,3 +143,5 @@ endif() endif() endif () + +cmake_policy(POP)
diff --git a/Modules/FindLibXml2.cmake b/Modules/FindLibXml2.cmake index ce28d03..1c5f9be 100644 --- a/Modules/FindLibXml2.cmake +++ b/Modules/FindLibXml2.cmake
@@ -52,6 +52,9 @@ path to the LibXml2 library #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # use pkg-config to get the directories and then use these values # in the find_path() and find_library() calls find_package(PkgConfig QUIET) @@ -122,3 +125,5 @@ add_executable(LibXml2::xmllint IMPORTED) set_target_properties(LibXml2::xmllint PROPERTIES IMPORTED_LOCATION "${LIBXML2_XMLLINT_EXECUTABLE}") endif() + +cmake_policy(POP)
diff --git a/Modules/FindLibXslt.cmake b/Modules/FindLibXslt.cmake index a9920ee..1154ae1 100644 --- a/Modules/FindLibXslt.cmake +++ b/Modules/FindLibXslt.cmake
@@ -45,6 +45,9 @@ Contains the full path to the xsltproc executable if found. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # use pkg-config to get the directories and then use these values # in the find_path() and find_library() calls find_package(PkgConfig QUIET) @@ -133,3 +136,5 @@ add_executable(LibXslt::xsltproc IMPORTED) set_target_properties(LibXslt::xsltproc PROPERTIES IMPORTED_LOCATION "${LIBXSLT_XSLTPROC_EXECUTABLE}") endif() + +cmake_policy(POP)
diff --git a/Modules/FindLua.cmake b/Modules/FindLua.cmake index b56a7b1..b4106c4 100644 --- a/Modules/FindLua.cmake +++ b/Modules/FindLua.cmake
@@ -45,6 +45,7 @@ cmake_policy(PUSH) # Policies apply to functions at definition-time cmake_policy(SET CMP0012 NEW) # For while(TRUE) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> unset(_lua_include_subdirs) unset(_lua_library_names)
diff --git a/Modules/FindLua51.cmake b/Modules/FindLua51.cmake index 405a7a7..2249ab1 100644 --- a/Modules/FindLua51.cmake +++ b/Modules/FindLua51.cmake
@@ -26,6 +26,9 @@ locations other than lua/ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(LUA_INCLUDE_DIR lua.h HINTS ENV LUA_DIR @@ -73,3 +76,5 @@ VERSION_VAR LUA_VERSION_STRING) mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) + +cmake_policy(POP)
diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake index 7aa371a..aa322ac 100644 --- a/Modules/FindMPI.cmake +++ b/Modules/FindMPI.cmake
@@ -265,6 +265,7 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) find_package(PkgConfig QUIET) @@ -282,6 +283,9 @@ set(_MPI_Fujitsu_C_COMPILER_NAMES mpifccpx mpifcc) set(_MPI_Fujitsu_CXX_COMPILER_NAMES mpiFCCpx mpiFCC) set(_MPI_Fujitsu_Fortran_COMPILER_NAMES mpifrtpx mpifrt) +set(_MPI_FujitsuClang_C_COMPILER_NAMES mpifccpx mpifcc) +set(_MPI_FujitsuClang_CXX_COMPILER_NAMES mpiFCCpx mpiFCC) +set(_MPI_FujitsuClang_Fortran_COMPILER_NAMES mpifrtpx mpifrt) # GNU compiler names set(_MPI_GNU_C_COMPILER_NAMES mpigcc mpgcc mpigcc_r mpgcc_r)
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 2488bd9..6cdf1a4 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake
@@ -42,6 +42,13 @@ Removed the ``MX_LIBRARY``, ``ENGINE_LIBRARY`` and ``DATAARRAY_LIBRARY`` components. These libraries are found unconditionally. +.. versionadded:: 3.30 + Added support for specifying a version range to :command:`find_package` and + added support for specifying ``REGISTRY_VIEW`` to :command:`find_package`, + :command:`matlab_extract_all_installed_versions_from_registry` and + :command:`matlab_get_all_valid_matlab_roots_from_registry`. The default + behavior remained unchanged, by using the registry view ``TARGET``. + .. note:: The version given to the :command:`find_package` directive is the Matlab @@ -56,7 +63,8 @@ specific: * Windows: The installed versions of Matlab/MCR are retrieved from the - Windows registry + Windows registry. The ``REGISTRY_VIEW`` argument may optionally be specified + to manually control whether 32bit or 64bit versions shall be searched for. * macOS: The installed versions of Matlab/MCR are given by the MATLAB default installation paths in ``/Application``. If no such application is found, it falls back to the one that might be accessible from the ``PATH``. @@ -109,10 +117,12 @@ This module defines the following :prop_tgt:`IMPORTED` targets: ``Matlab::mex`` - The ``mex`` library, always available. + The ``mex`` library, always available for MATLAB installations. Available for + MCR installations if provided by MCR. ``Matlab::mx`` - The mx library of Matlab (arrays), always available. + The mx library of Matlab (arrays), always available for MATLAB installations. + Available for MCR installations if provided by MCR. ``Matlab::eng`` Matlab engine library. Available only if the ``ENG_LIBRARY`` component @@ -123,10 +133,12 @@ is requested. ``Matlab::MatlabEngine`` - Matlab C++ engine library, always available for R2018a and newer. + Matlab C++ engine library, always available for MATLAB R2018a and newer. + Available for MCR installations if provided by MCR. ``Matlab::MatlabDataArray`` - Matlab C++ data array library, always available for R2018a and newer. + Matlab C++ data array library, always available for MATLAB R2018a and newer. + Available for MCR installations if provided by MCR. Variables defined by the module ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,9 +164,11 @@ ``Matlab_INCLUDE_DIRS`` the path of the Matlab libraries headers ``Matlab_MEX_LIBRARY`` - library for mex, always available. + library for mex, always available for MATLAB installations. Available for MCR + installations if provided by MCR. ``Matlab_MX_LIBRARY`` - mx library of Matlab (arrays), always available. + mx library of Matlab (arrays), always available for MATLAB installations. + Available for MCR installations if provided by MCR. ``Matlab_ENG_LIBRARY`` Matlab engine library. Available only if the component ``ENG_LIBRARY`` is requested. @@ -164,11 +178,13 @@ ``Matlab_ENGINE_LIBRARY`` .. versionadded:: 3.13 - Matlab C++ engine library, always available for R2018a and newer. + Matlab C++ engine library, always available for MATLAB R2018a and newer. + Available for MCR installations if provided by MCR. ``Matlab_DATAARRAY_LIBRARY`` .. versionadded:: 3.13 - Matlab C++ data array library, always available for R2018a and newer. + Matlab C++ data array library, always available for MATLAB R2018a and newer. + Available for MCR installations if provided by MCR. ``Matlab_LIBRARIES`` the whole set of libraries of Matlab ``Matlab_MEX_COMPILER`` @@ -283,13 +299,18 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}") include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -include(CheckCXXCompilerFlag) -include(CheckCCompilerFlag) +if(NOT WIN32 AND NOT APPLE AND NOT Threads_FOUND + AND (CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED)) + # MEX files use pthread if available + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) +endif() # The currently supported versions. Other version can be added by the user by # providing MATLAB_ADDITIONAL_VERSIONS @@ -298,6 +319,7 @@ endif() set(MATLAB_VERSIONS_MAPPING + "R2024b=24.2" "R2024a=24.1" "R2023b=23.2" "R2023a=9.14" @@ -438,20 +460,33 @@ #[=======================================================================[.rst: .. command:: matlab_extract_all_installed_versions_from_registry - .. code-block:: cmake + This function parses the Windows registry and finds the Matlab versions that + are installed. The found versions are stored in ``matlab_versions``. + .. signature:: + matlab_extract_all_installed_versions_from_registry(matlab_versions + [REGISTRY_VIEW view]) + + .. versionadded:: 3.30 + + * Output: ``matlab_versions`` is a list of all the versions of Matlab found + * Input: ``REGISTRY_VIEW`` Optional registry view to use for registry + interaction. The argument is passed (or omitted) to + :command:`cmake_host_system_information` without further checks or + modification. + + .. signature:: matlab_extract_all_installed_versions_from_registry(win64 matlab_versions) - * Input: ``win64`` is a boolean to search for the 64 bit version of Matlab - * Output: ``matlab_versions`` is a list of all the versions of Matlab found + * Input: ``win64`` is a boolean to search for the 64 bit version of + Matlab. Set to ``ON`` to use the 64bit registry view or ``OFF`` to use the + 32bit registry view. If finer control is needed, see signature above. + * Output: ``matlab_versions`` is a list of all the versions of Matlab found - This function parses the Windows registry and founds the Matlab versions that - are installed. The found versions are returned in `matlab_versions`. - Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for The returned list contains all versions under - ``HKLM\\SOFTWARE\\Mathworks\\MATLAB``, - ``HKLM\\SOFTWARE\\Mathworks\\MATLAB Runtime`` and - ``HKLM\\SOFTWARE\\Mathworks\\MATLAB Compiler Runtime`` or an empty list in + ``HKLM\SOFTWARE\Mathworks\MATLAB``, + ``HKLM\SOFTWARE\Mathworks\MATLAB Runtime`` and + ``HKLM\SOFTWARE\Mathworks\MATLAB Compiler Runtime`` or an empty list in case an error occurred (or nothing found). .. note:: @@ -460,16 +495,32 @@ installation referenced in the registry, #]=======================================================================] -function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions) +function(matlab_extract_all_installed_versions_from_registry win64_or_matlab_versions) if(NOT CMAKE_HOST_WIN32) message(FATAL_ERROR "[MATLAB] This function can only be called by a Windows host") endif() - if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64") - set(_view "64") + set(_registry_view_args) + if("${ARGC}" EQUAL "2") + # Old API: <win64> <matlab_versions> + if(${win64_or_matlab_versions}) + set(_registry_view_args VIEW 64) + else() + set(_registry_view_args VIEW 32) + endif() + set(matlab_versions ${ARGV1}) else() - set(_view "32") + # New API: <matlab_versions> [REGISTRY_VIEW <view>] + set(matlab_versions ${win64_or_matlab_versions}) + cmake_parse_arguments(_Matlab "" "REGISTRY_VIEW" "" ${ARGN}) + if(_Matlab_REGISTRY_VIEW) + set(_registry_view_args VIEW "${_Matlab_REGISTRY_VIEW}") + endif() + endif() + + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Extracting MATLAB versions with registry view args '${_registry_view_args}'") endif() set(matlabs_from_registry) @@ -477,20 +528,15 @@ foreach(_installation_type IN ITEMS "MATLAB" "MATLAB Runtime" "MATLAB Compiler Runtime") cmake_host_system_information(RESULT _reg - QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}" - SUBKEYS VIEW ${_view} + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}" + SUBKEYS + ${_registry_view_args} ) - if(_reg) - string(REGEX MATCHALL "([0-9]+(\\.[0-9]+)+)" _versions_regex "${_reg}") + string(REGEX MATCHALL "([0-9]+(\\.[0-9]+)+)" _versions_regex "${_reg}") - foreach(_match IN LISTS _versions_regex) - if(_match MATCHES "([0-9]+(\\.[0-9]+)+)") - list(APPEND matlabs_from_registry ${_match}) - endif() - endforeach() + list(APPEND matlabs_from_registry ${_versions_regex}) - endif() endforeach() if(matlabs_from_registry) @@ -531,10 +577,19 @@ .. code-block:: cmake - matlab_get_all_valid_matlab_roots_from_registry(matlab_versions matlab_roots) + matlab_get_all_valid_matlab_roots_from_registry(matlab_versions matlab_roots [REGISTRY_VIEW view]) * Input: ``matlab_versions`` of each of the Matlab or MCR installations * Output: ``matlab_roots`` location of each of the Matlab or MCR installations + * Input: ``REGISTRY_VIEW`` Optional registry view to use for registry + interaction. The argument is passed (or omitted) to + :command:`cmake_host_system_information` without further checks or + modification. + + .. versionadded:: 3.30 + The optional ``REGISTRY_VIEW`` argument was added to provide a more precise + interface on how to interact with the Windows Registry. + #]=======================================================================] function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_roots) @@ -542,6 +597,15 @@ # extract_matlab_versions_from_registry_brute_force or # matlab_extract_all_installed_versions_from_registry. + cmake_parse_arguments(_Matlab "" "REGISTRY_VIEW" "" ${ARGN}) + set(_registry_view_args) + if(_Matlab_REGISTRY_VIEW) + set(_registry_view_args VIEW "${_Matlab_REGISTRY_VIEW}") + endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Getting MATLAB roots with registry view args '${_registry_view_args}'") + endif() + # Mostly the major.minor version is used in Mathworks Windows Registry keys. # If the patch is not zero, major.minor.patch is used. list(TRANSFORM matlab_versions REPLACE "^([0-9]+\\.[0-9]+(\\.[1-9][0-9]*)?).*" "\\1") @@ -552,57 +616,44 @@ cmake_host_system_information(RESULT current_MATLAB_ROOT QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB/${_matlab_current_version}" VALUE "MATLABROOT" + ${_registry_view_args} ) cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT) if(IS_DIRECTORY "${current_MATLAB_ROOT}") _Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp) if("${_matlab_version_tmp}" STREQUAL "unknown") - list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT}) - else() - list(APPEND _matlab_roots_list "MATLAB" ${_matlab_version_tmp} ${current_MATLAB_ROOT}) + set(_matlab_version_tmp ${_matlab_current_version}) endif() + list(APPEND _matlab_roots_list "MATLAB" ${_matlab_version_tmp} ${current_MATLAB_ROOT}) endif() endforeach() # Check for MCR installations - foreach(_matlab_current_version IN LISTS matlab_versions) - cmake_host_system_information(RESULT current_MATLAB_ROOT - QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB Runtime/${_matlab_current_version}" - VALUE "MATLABROOT" - ) - cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT) + foreach(_installation_type IN ITEMS "MATLAB Runtime" "MATLAB Compiler Runtime") + foreach(_matlab_current_version IN LISTS matlab_versions) + cmake_host_system_information(RESULT current_MATLAB_ROOT + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}/${_matlab_current_version}" + VALUE "MATLABROOT" + ${_registry_view_args} + ) + cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT) - # remove the dot - string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") + # remove the dot + string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - if(IS_DIRECTORY "${current_MATLAB_ROOT}") - _Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp) - if("${_matlab_version_tmp}" STREQUAL "unknown") - list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") - else() - list(APPEND _matlab_roots_list "MCR" ${_matlab_version_tmp} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") + cmake_path(APPEND current_MATLAB_ROOT "v${_matlab_current_version_without_dot}") + endif() + _Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp) + if("${_matlab_version_tmp}" STREQUAL "unknown") + set(_matlab_version_tmp ${_matlab_current_version}) + endif() + list(APPEND _matlab_roots_list "MCR" ${_matlab_version_tmp} "${current_MATLAB_ROOT}") endif() - endif() - - endforeach() - - # Check for old MCR installations - foreach(_matlab_current_version IN LISTS matlab_versions) - cmake_host_system_information(RESULT current_MATLAB_ROOT - QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB Compiler Runtime/${_matlab_current_version}" - VALUE "MATLABROOT" - ) - cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT) - - # remove the dot - string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - - if(IS_DIRECTORY "${current_MATLAB_ROOT}") - list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") - endif() - + endforeach() endforeach() set(${matlab_roots} ${_matlab_roots_list} PARENT_SCOPE) endfunction() @@ -1069,20 +1120,6 @@ #]=======================================================================] function(matlab_add_mex) - if(NOT WIN32) - # we do not need all this on Windows - # pthread options - if(CMAKE_CXX_COMPILER_LOADED) - check_cxx_compiler_flag(-pthread HAS_MINUS_PTHREAD) - elseif(CMAKE_C_COMPILER_LOADED) - check_c_compiler_flag(-pthread HAS_MINUS_PTHREAD) - endif() - # we should use try_compile instead, the link flags are discarded from - # this compiler_flag function. - #check_cxx_compiler_flag(-Wl,--exclude-libs,ALL HAS_SYMBOL_HIDING_CAPABILITY) - - endif() - set(options EXECUTABLE MODULE SHARED R2017b R2018a EXCLUDE_FROM_ALL NO_IMPLICIT_LINK_TO_MATLAB_LIBRARIES) set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME) set(multiValueArgs LINK_TO SRC) @@ -1099,14 +1136,27 @@ 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 + # These compilers definitely support the -w flag to suppress warnings. + # 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++ 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 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() else() # If neither C or CXX is enabled, warn because we cannot add the source. # TODO: add support for fortran mex files @@ -1237,10 +1287,8 @@ else() # Linux - if(HAS_MINUS_PTHREAD) - # Apparently, compiling with -pthread generated the proper link flags - # and some defines at compilation - target_compile_options(${${prefix}_NAME} PRIVATE "-pthread") + if(Threads_FOUND) + target_link_libraries(${${prefix}_NAME} Threads::Threads) endif() string(APPEND _link_flags " -Wl,--as-needed") @@ -1278,6 +1326,10 @@ # set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version") #endif() + if(NOT matlab_or_mcr STREQUAL "UNKNOWN") + set(Matlab_OR_MCR_INTERNAL ${matlab_or_mcr} CACHE INTERNAL "Whether Matlab root contains MATLAB or MCR") + endif() + if(NOT matlab_known_version STREQUAL "NOTFOUND") # the version is known, we just return it set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE) @@ -1344,6 +1396,7 @@ endif() set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version") set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version") + 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() @@ -1383,6 +1436,7 @@ # set the version into the cache set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)") + set(Matlab_OR_MCR_INTERNAL ${matlab_or_mcr} CACHE INTERNAL "Whether Matlab root contains MATLAB or MCR") # warning, just in case several versions found (should not happen) if((list_of_all_versions_length GREATER 1) AND MATLAB_FIND_DEBUG) @@ -1396,6 +1450,7 @@ _Matlab_VersionInfoXML("${matlab_root}" _matlab_version_tmp) if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)") + set(Matlab_OR_MCR_INTERNAL ${matlab_or_mcr} CACHE INTERNAL "Whether Matlab root contains MATLAB or MCR") endif() endif() # Matlab or MCR @@ -1433,13 +1488,7 @@ # testing if we are able to extract the needed information from the registry set(_matlab_versions_from_registry) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_matlab_win64 ON) - else() - set(_matlab_win64 OFF) - endif() - - matlab_extract_all_installed_versions_from_registry(_matlab_win64 _matlab_versions_from_registry) + matlab_extract_all_installed_versions_from_registry(_matlab_versions_from_registry ${ARGN}) # the returned list is empty, doing the search on all known versions if(NOT _matlab_versions_from_registry) @@ -1450,7 +1499,7 @@ endif() # filtering the results with the registry keys - matlab_get_all_valid_matlab_roots_from_registry("${_matlab_versions_from_registry}" _matlab_possible_roots) + matlab_get_all_valid_matlab_roots_from_registry("${_matlab_versions_from_registry}" _matlab_possible_roots ${ARGN}) set(${matlab_roots} ${_matlab_possible_roots} PARENT_SCOPE) endfunction() @@ -1587,12 +1636,18 @@ message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})") endif() else() + if("${Matlab_OR_MCR_INTERNAL}" STREQUAL "") + set(_matlab_cached_matlab_or_mcr "UNKNOWN") + else() + set(_matlab_cached_matlab_or_mcr "${Matlab_OR_MCR_INTERNAL}") + endif() # NOTFOUND indicates the code below to search for the version automatically if("${Matlab_VERSION_STRING_INTERNAL}" STREQUAL "") - list(APPEND _matlab_possible_roots "UNKNOWN" "NOTFOUND" ${Matlab_ROOT_DIR}) # empty version, empty MCR/Matlab indication + set(_matlab_cached_version "NOTFOUND") # empty version, empty MCR/Matlab indication else() - list(APPEND _matlab_possible_roots "UNKNOWN" ${Matlab_VERSION_STRING_INTERNAL} ${Matlab_ROOT_DIR}) # cached version + set(_matlab_cached_version "${Matlab_VERSION_STRING_INTERNAL}") # cached version endif() + list(APPEND _matlab_possible_roots "${_matlab_cached_matlab_or_mcr}" "${_matlab_cached_version}" "${Matlab_ROOT_DIR}") endif() else() @@ -1600,7 +1655,10 @@ # one installation using the appropriate heuristics. # There is apparently no standard way on Linux. if(CMAKE_HOST_WIN32) - _Matlab_find_instances_win32(_matlab_possible_roots_win32) + if(NOT DEFINED Matlab_FIND_REGISTRY_VIEW) + set(Matlab_FIND_REGISTRY_VIEW TARGET) + endif() + _Matlab_find_instances_win32(_matlab_possible_roots_win32 REGISTRY_VIEW ${Matlab_FIND_REGISTRY_VIEW}) list(APPEND _matlab_possible_roots ${_matlab_possible_roots_win32}) elseif(APPLE) _Matlab_find_instances_macos(_matlab_possible_roots_macos) @@ -1625,62 +1683,29 @@ set(Matlab_VERSION_STRING "NOTFOUND") set(Matlab_Or_MCR "UNKNOWN") if(_numbers_of_matlab_roots GREATER 0) - if(Matlab_FIND_VERSION_EXACT) - set(_list_index -1) - foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3) - list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version) - # only the major.minor version is used - string(REGEX REPLACE "^([0-9]+\\.[0-9]+).*" "\\1" _matlab_root_version "${_matlab_root_version}") - if(_matlab_root_version VERSION_EQUAL Matlab_FIND_VERSION) - set(_list_index ${_matlab_root_index}) - break() - endif() - endforeach() - - if(_list_index LESS 0) - set(_list_index 1) + set(_list_index -1) + foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3) + list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version) + find_package_check_version(${_matlab_root_version} _matlab_version_ok HANDLE_VERSION_RANGE) + if(_matlab_version_ok) + set(_list_index ${_matlab_root_index}) + break() endif() + endforeach() - math(EXPR _matlab_or_mcr_index "${_list_index} - 1") - math(EXPR _matlab_root_dir_index "${_list_index} + 1") + if(_list_index LESS 0) + set(_list_index 1) + endif() - list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR) - list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING) - list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR) - elseif(DEFINED Matlab_FIND_VERSION) - set(_list_index -1) - foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3) - list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version) - if(_matlab_root_version VERSION_GREATER_EQUAL Matlab_FIND_VERSION) - set(_list_index ${_matlab_root_index}) - break() - endif() - endforeach() - - if(_list_index LESS 0) - set(_list_index 1) - endif() - - math(EXPR _matlab_or_mcr_index "${_list_index} - 1") - math(EXPR _matlab_root_dir_index "${_list_index} + 1") - list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR) - list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING) - list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR) - # adding a warning in case of ambiguity - if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG) - message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})." - " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line") - endif() - else() - list(GET _matlab_possible_roots 0 Matlab_Or_MCR) - list(GET _matlab_possible_roots 1 Matlab_VERSION_STRING) - list(GET _matlab_possible_roots 2 Matlab_ROOT_DIR) - - # adding a warning in case of ambiguity - if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG) - message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})." - " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line") - endif() + math(EXPR _matlab_or_mcr_index "${_list_index} - 1") + math(EXPR _matlab_root_dir_index "${_list_index} + 1") + list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR) + list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING) + list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR) + # adding a warning in case of ambiguity + if(_numbers_of_matlab_roots GREATER 3 AND NOT Matlab_FIND_VERSION_EXACT AND MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})." + " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line") endif() endif() @@ -1744,12 +1769,6 @@ file(TO_CMAKE_PATH ${Matlab_ROOT_DIR} Matlab_ROOT_DIR) endif() -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(_matlab_64Build FALSE) -else() - set(_matlab_64Build TRUE) -endif() - if(NOT DEFINED Matlab_MEX_EXTENSION) set(_matlab_mex_extension "") @@ -1783,10 +1802,10 @@ set(MATLAB_INCLUDE_DIR_TO_LOOK ${Matlab_ROOT_DIR}/extern/include) -if(_matlab_64Build) - set(_matlab_current_suffix ${_matlab_bin_suffix_64bits}) -else() +if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(_matlab_current_suffix ${_matlab_bin_suffix_32bits}) +else() + set(_matlab_current_suffix ${_matlab_bin_suffix_64bits}) endif() set(Matlab_BINARIES_DIR @@ -1808,11 +1827,9 @@ set(_matlab_lib_prefix_for_search "lib") endif() -unset(_matlab_64Build) - if(MATLAB_FIND_DEBUG) - message(STATUS "[MATLAB] [DEBUG]_matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}") + message(STATUS "[MATLAB] _matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}") endif() @@ -1826,7 +1843,9 @@ endfunction() -set(_matlab_required_variables) +# the matlab root is required +set(_matlab_required_variables Matlab_ROOT_DIR) +set(Matlab_LIBRARIES) # Order is as follow: # - unconditionally required libraries/headers first @@ -1836,40 +1855,50 @@ # the MEX library/header are required find_path( Matlab_INCLUDE_DIRS - NAMES mex.h + NAMES mex.h matrix.h PATHS ${MATLAB_INCLUDE_DIR_TO_LOOK} NO_DEFAULT_PATH ) list(APPEND _matlab_required_variables Matlab_INCLUDE_DIRS) +_Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MEX_LIBRARY + NAMES mex + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH +) +if(Matlab_MEX_LIBRARY) + set(Matlab_MEX_LIBRARY_FOUND TRUE) + list(APPEND Matlab_LIBRARIES ${Matlab_MEX_LIBRARY}) +endif() +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] mex C library: ${Matlab_MEX_LIBRARY}") +endif() + +# The MX library is required +_Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MX_LIBRARY + NAMES mx + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH +) +if(Matlab_MX_LIBRARY) + set(Matlab_MX_LIBRARY_FOUND TRUE) + list(APPEND Matlab_LIBRARIES ${Matlab_MX_LIBRARY}) +endif() +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] mx C library: ${Matlab_MX_LIBRARY}") +endif() + if(Matlab_Or_MCR STREQUAL "MATLAB" OR Matlab_Or_MCR STREQUAL "UNKNOWN") - _Matlab_find_library( - ${_matlab_lib_prefix_for_search} - Matlab_MEX_LIBRARY - NAMES mex - PATHS ${_matlab_lib_dir_for_search} - NO_DEFAULT_PATH - ) list(APPEND _matlab_required_variables Matlab_MEX_LIBRARY) # the MEX extension is required list(APPEND _matlab_required_variables Matlab_MEX_EXTENSION) - # the matlab root is required - list(APPEND _matlab_required_variables Matlab_ROOT_DIR) - - # The MX library is required - _Matlab_find_library( - ${_matlab_lib_prefix_for_search} - Matlab_MX_LIBRARY - NAMES mx - PATHS ${_matlab_lib_dir_for_search} - NO_DEFAULT_PATH - ) list(APPEND _matlab_required_variables Matlab_MX_LIBRARY) - if(Matlab_MX_LIBRARY) - set(Matlab_MX_LIBRARY_FOUND TRUE) - endif() endif() if(Matlab_HAS_CPP_API) @@ -1885,6 +1914,10 @@ ) if(Matlab_ENGINE_LIBRARY) set(Matlab_ENGINE_LIBRARY_FOUND TRUE) + list(APPEND Matlab_LIBRARIES ${Matlab_ENGINE_LIBRARY}) + endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Engine C++ library: ${Matlab_ENGINE_LIBRARY}") endif() # The MatlabDataArray library is required for R2018a+ @@ -1898,6 +1931,10 @@ ) if(Matlab_DATAARRAY_LIBRARY) set(Matlab_DATAARRAY_LIBRARY_FOUND TRUE) + list(APPEND Matlab_LIBRARIES ${Matlab_DATAARRAY_LIBRARY}) + endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Data array C++ library: ${Matlab_DATAARRAY_LIBRARY}") endif() endif() @@ -1913,6 +1950,10 @@ ) if(Matlab_ENG_LIBRARY) set(Matlab_ENG_LIBRARY_FOUND TRUE) + list(APPEND Matlab_LIBRARIES ${Matlab_ENG_LIBRARY}) + endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] eng C library: ${Matlab_ENG_LIBRARY}") endif() endif() @@ -1927,6 +1968,10 @@ ) if(Matlab_MAT_LIBRARY) set(Matlab_MAT_LIBRARY_FOUND TRUE) + list(APPEND Matlab_LIBRARIES ${Matlab_MAT_LIBRARY}) + endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] mat C library: ${Matlab_MAT_LIBRARY}") endif() endif() @@ -1942,6 +1987,9 @@ set(Matlab_SIMULINK_FOUND TRUE) list(APPEND Matlab_INCLUDE_DIRS "${Matlab_SIMULINK_INCLUDE_DIR}") endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Simulink include dir: ${Matlab_SIMULINK_INCLUDE_DIR}") + endif() endif() # component Matlab program @@ -1956,6 +2004,9 @@ if(Matlab_MAIN_PROGRAM) set(Matlab_MAIN_PROGRAM_FOUND TRUE) endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Main program: ${Matlab_MAIN_PROGRAM}") + endif() endif() # component Mex Compiler @@ -1970,6 +2021,9 @@ if(Matlab_MEX_COMPILER) set(Matlab_MEX_COMPILER_FOUND TRUE) endif() + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] MEX compiler: ${Matlab_MEX_COMPILER}") + endif() endif() # component MCC Compiler @@ -1984,18 +2038,9 @@ if(Matlab_MCC_COMPILER) set(Matlab_MCC_COMPILER_FOUND TRUE) endif() -endif() - -set(Matlab_LIBRARIES - ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY} - ${Matlab_ENG_LIBRARY} ${Matlab_MAT_LIBRARY}) - -if(Matlab_ENGINE_LIBRARY) - list(APPEND Matlab_LIBRARIES ${Matlab_ENGINE_LIBRARY}) -endif() - -if(Matlab_DATAARRAY_LIBRARY) - list(APPEND Matlab_LIBRARIES ${Matlab_DATAARRAY_LIBRARY}) + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] MCC compiler: ${Matlab_MCC_COMPILER}") + endif() endif() # internal @@ -2031,6 +2076,7 @@ FOUND_VAR Matlab_FOUND REQUIRED_VARS ${_matlab_required_variables} VERSION_VAR Matlab_VERSION + HANDLE_VERSION_RANGE HANDLE_COMPONENTS) unset(_matlab_required_variables)
diff --git a/Modules/FindOpenACC.cmake b/Modules/FindOpenACC.cmake index e9ca683..d5d26a1 100644 --- a/Modules/FindOpenACC.cmake +++ b/Modules/FindOpenACC.cmake
@@ -70,9 +70,12 @@ be returned with OpenACC_<lang>_FLAGS. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + set(OpenACC_C_CXX_TEST_SOURCE " -int main(){ +int main(void){ #ifdef _OPENACC return 0; #else @@ -102,7 +105,7 @@ ('0' + ((_OPENACC/10)%10)), ('0' + ((_OPENACC/1)%10)), ']', '\\0' }; -int main() +int main(void) { puts(accver_str); return 0; @@ -306,3 +309,5 @@ unset(OpenACC_Fortran_TEST_SOURCE) unset(OpenACC_C_CXX_CHECK_VERSION_SOURCE) unset(OpenACC_Fortran_CHECK_VERSION_SOURCE) + +cmake_policy(POP)
diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index 1d36b9b..f88e43c 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake
@@ -16,8 +16,22 @@ .. versionadded:: 3.5 Clang support. -Variables -^^^^^^^^^ +Input Variables +^^^^^^^^^^^^^^^ + +The following variables may be set to influence this module's behavior: + +``OpenMP_RUNTIME_MSVC`` + .. versionadded:: 3.30 + + Specify the `OpenMP Runtime <msvc-openmp_>`_ when compiling with MSVC. + If set to a non-empty value, such as ``experimental`` or ``llvm``, it + will be passed as the value of the ``-openmp:`` flag. + +.. _`msvc-openmp`: https://learn.microsoft.com/en-us/cpp/build/reference/openmp-enable-openmp-2-0-support + +Result Variables +^^^^^^^^^^^^^^^^ .. versionadded:: 3.10 The module exposes the components ``C``, ``CXX``, and ``Fortran``. @@ -96,6 +110,7 @@ cmake_policy(SET CMP0012 NEW) # if() recognizes numbers and booleans cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced cmake_policy(SET CMP0057 NEW) # if IN_LIST +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> function(_OPENMP_FLAG_CANDIDATES LANG) if(NOT OpenMP_${LANG}_FLAG) @@ -120,7 +135,11 @@ else() set(OMP_FLAG_IntelLLVM "-fiopenmp") endif() - set(OMP_FLAG_MSVC "-openmp") + if(OpenMP_RUNTIME_MSVC) + set(OMP_FLAG_MSVC "-openmp:${OpenMP_RUNTIME_MSVC}") + else() + set(OMP_FLAG_MSVC "-openmp") + endif() set(OMP_FLAG_PathScale "-openmp") set(OMP_FLAG_NAG "-openmp") set(OMP_FLAG_Absoft "-openmp")
diff --git a/Modules/FindOpenSP.cmake b/Modules/FindOpenSP.cmake index 25d0e6f..4255aef 100644 --- a/Modules/FindOpenSP.cmake +++ b/Modules/FindOpenSP.cmake
@@ -63,6 +63,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_package(PkgConfig QUIET) if (PkgConfig_FOUND) pkg_check_modules(PC_OpenSP QUIET opensp) @@ -158,3 +161,5 @@ URL "http://openjade.sourceforge.net/doc/index.htm" DESCRIPTION "An SGML System Conforming to International Standard ISO 8879" ) + +cmake_policy(POP)
diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index 057a8d1..01b18f3 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake
@@ -111,6 +111,9 @@ locations. Useful on multi-lib systems. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library) unset(_OpenSSL_extra_static_deps) if(UNIX AND @@ -796,3 +799,5 @@ unset(_OpenSSL_has_dependency_dl) unset(_OpenSSL_has_dependency_threads) unset(_OpenSSL_has_dependency_zlib) + +cmake_policy(POP)
diff --git a/Modules/FindOpenSceneGraph.cmake b/Modules/FindOpenSceneGraph.cmake index 27909bc..9df2a62 100644 --- a/Modules/FindOpenSceneGraph.cmake +++ b/Modules/FindOpenSceneGraph.cmake
@@ -101,6 +101,9 @@ target_link_libraries(foo ${OPENSCENEGRAPH_LIBRARIES}) #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # # Naming convention: # Local variables of the form _osg_foo @@ -230,3 +233,5 @@ unset(_osg_component_founds) set(OPENSCENEGRAPH_INCLUDE_DIRS ${OPENSCENEGRAPH_INCLUDE_DIR}) + +cmake_policy(POP)
diff --git a/Modules/FindPNG.cmake b/Modules/FindPNG.cmake index 043b69c..4294326 100644 --- a/Modules/FindPNG.cmake +++ b/Modules/FindPNG.cmake
@@ -48,6 +48,9 @@ will be defined unless ZLib can be found. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # Default install location on windows when installing from included cmake build # From FindZLIB.cmake set(_PNG_x86 "(x86)") @@ -177,3 +180,5 @@ find_package_handle_standard_args(PNG REQUIRED_VARS PNG_LIBRARY PNG_PNG_INCLUDE_DIR VERSION_VAR PNG_VERSION_STRING) + +cmake_policy(POP)
diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake index 63b2bf2..c6db433 100644 --- a/Modules/FindPackageHandleStandardArgs.cmake +++ b/Modules/FindPackageHandleStandardArgs.cmake
@@ -225,6 +225,9 @@ set (__msg "${_msg}") if (FPHSA_REASON_FAILURE_MESSAGE) string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") + elseif(NOT DEFINED PROJECT_NAME) + string(APPEND __msg "\n" + "Hint: The project() command has not yet been called. It sets up system-specific search paths.") endif() if (${_NAME}_FIND_REQUIRED) message(FATAL_ERROR "${__msg}")
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake index f3bacc3..450acf4 100644 --- a/Modules/FindPkgConfig.cmake +++ b/Modules/FindPkgConfig.cmake
@@ -158,6 +158,17 @@ string(REGEX REPLACE "${_regexp}" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}") endif() + # pkg-config <0.29.1 and pkgconf <1.5.1 prints quoted variables without unquoting + # unquote only if quotes are first and last characters + if((PKG_CONFIG_VERSION_STRING VERSION_LESS 0.29.1) OR + (PKG_CONFIG_VERSION_STRING VERSION_GREATER_EQUAL 1.0 AND PKG_CONFIG_VERSION_STRING VERSION_LESS 1.5.1)) + if (_pkgconfig_invoke_result MATCHES "^\"(.*)\"$") + set(_pkgconfig_invoke_result "${CMAKE_MATCH_1}") + elseif(_pkgconfig_invoke_result MATCHES "^'(.*)'$") + set(_pkgconfig_invoke_result "${CMAKE_MATCH_1}") + endif() + endif() + # pkg-config can represent "spaces within an argument" by backslash-escaping the space. # UNIX_COMMAND mode treats backslash-escaped spaces as "not a space that delimits arguments". separate_arguments(_pkgconfig_invoke_result UNIX_COMMAND "${_pkgconfig_invoke_result}")
diff --git a/Modules/FindPostgreSQL.cmake b/Modules/FindPostgreSQL.cmake index 84bc1ed..1984e18 100644 --- a/Modules/FindPostgreSQL.cmake +++ b/Modules/FindPostgreSQL.cmake
@@ -93,6 +93,7 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> set(PostgreSQL_INCLUDE_PATH_DESCRIPTION "top-level directory containing the PostgreSQL include directories. E.g /usr/local/include/PostgreSQL/8.4 or C:/Program Files/PostgreSQL/8.4/include") set(PostgreSQL_INCLUDE_DIR_MESSAGE "Set the PostgreSQL_INCLUDE_DIR cmake cache entry to the ${PostgreSQL_INCLUDE_PATH_DESCRIPTION}")
diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake index a7ee0c4..8ad3270 100644 --- a/Modules/FindProtobuf.cmake +++ b/Modules/FindProtobuf.cmake
@@ -218,6 +218,9 @@ GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc) #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + function(protobuf_generate) set(_options APPEND_PATH DESCRIPTORS) set(_singleargs LANGUAGE OUT_VAR EXPORT_MACRO PROTOC_OUT_DIR PLUGIN PLUGIN_OPTIONS DEPENDENCIES) @@ -782,3 +785,5 @@ string(TOUPPER ${Camel} UPPER) set(${UPPER} ${${Camel}}) endforeach() + +cmake_policy(POP)
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake index 19b6c2a..797891b 100644 --- a/Modules/FindPython.cmake +++ b/Modules/FindPython.cmake
@@ -77,9 +77,25 @@ :prop_gbl:`CMAKE_ROLE` is ``PROJECT``. ``Python::Interpreter`` - Python interpreter. Target defined if component ``Interpreter`` is found. + Python interpreter. This target is defined only if the ``Interpreter`` + component is found. +``Python::InterpreterDebug`` + .. versionadded:: 3.30 + + Python debug interpreter. This target is defined only if the ``Interpreter`` + component is found and the ``Python_EXECUTABLE_DEBUG`` variable is defined. + The target is only defined on the ``Windows`` platform. + +``Python::InterpreterMultiConfig`` + .. versionadded:: 3.30 + + Python interpreter. The release or debug version of the interpreter will be + used, based on the context (platform, configuration). + This target is defined only if the ``Interpreter`` component is found + ``Python::Compiler`` - Python compiler. Target defined if component ``Compiler`` is found. + Python compiler. This target is defined only if the ``Compiler`` component is + found. ``Python::Module`` .. versionadded:: 3.15 @@ -114,6 +130,20 @@ System has the Python interpreter. ``Python_EXECUTABLE`` Path to the Python interpreter. +``Python_EXECUTABLE_DEBUG`` + .. versionadded:: 3.30 + + Path to the debug Python interpreter. It is only defined on the ``Windows`` + platform. + +``Python_INTERPRETER`` + .. versionadded:: 3.30 + + Path to the Python interpreter, defined as a + :manual:`generator expression <cmake-generator-expressions(7)>` selecting + the ``Python_EXECUTABLE`` or ``Python_EXECUTABLE_DEBUG`` variable based on + the context (platform, configuration). + ``Python_INTERPRETER_ID`` A short string unique to the interpreter. Possible values include: * Python @@ -194,6 +224,17 @@ The Python include directories. +``Python_DEFINITIONS`` + .. versionadded:: 3.30.3 + + The Python preprocessor definitions. + +``Python_DEBUG_POSTFIX`` + .. versionadded.. 3.30 + + Postfix of debug python module. This variable can be used to define the + :prop_tgt:`DEBUG_POSTFIX` target property. + ``Python_LINK_OPTIONS`` .. versionadded:: 3.19 @@ -274,42 +315,54 @@ This hint will be honored only when searched for ``Python`` version 3. - .. note:: + The ``Python_FIND_ABI`` variable is a 4-tuple specifying, in that order, + ``pydebug`` (``d``), ``pymalloc`` (``m``), ``unicode`` (``u``) and + ``gil_disabled`` (``t``) flags. - If ``Python_FIND_ABI`` is not defined, any ABI will be searched. + .. versionadded:: 3.30 + A fourth element, specifying the ``gil_disabled`` flag (i.e. free + threaded python), is added and is optional. If not specified, the value is + ``OFF``. - The ``Python_FIND_ABI`` variable is a 3-tuple specifying, in that order, - ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags. Each element can be set to one of the following: * ``ON``: Corresponding flag is selected. * ``OFF``: Corresponding flag is not selected. * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched. - From this 3-tuple, various ABIs will be searched starting from the most - specialized to the most general. Moreover, ``debug`` versions will be - searched **after** ``non-debug`` ones. + .. note:: + + If ``Python3_FIND_ABI`` is not defined, any ABI, excluding the + ``gil_disabled`` flag, will be searched. + + From this 4-tuple, various ABIs will be searched starting from the most + specialized to the most general. Moreover, when ``ANY`` is specified for + ``pydebug`` and ``gil_disabled``, ``debug`` and ``free threaded`` versions + will be searched **after** ``non-debug`` and ``non-gil-disabled`` ones. For example, if we have:: - set (Python_FIND_ABI "ON" "ANY" "ANY") + set (Python_FIND_ABI "ON" "ANY" "ANY" "ON") The following flags combinations will be appended, in that order, to the - artifact names: ``dmu``, ``dm``, ``du``, and ``d``. + artifact names: ``tdmu``, ``tdm``, ``tdu``, and ``td``. And to search any possible ABIs:: - set (Python_FIND_ABI "ANY" "ANY" "ANY") + set (Python_FIND_ABI "ANY" "ANY" "ANY" "ANY") The following combinations, in that order, will be used: ``mu``, ``m``, - ``u``, ``<empty>``, ``dmu``, ``dm``, ``du`` and ``d``. + ``u``, ``<empty>``, ``dmu``, ``dm``, ``du``, ``d``, ``tmu``, ``tm``, ``tu``, + ``t``, ``tdmu``, ``tdm``, ``tdu``, and ``td``. .. note:: - This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems, + 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 value for - each flag is ``OFF`` or ``ANY``. + `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``). ``Python_FIND_STRATEGY`` .. versionadded:: 3.15 @@ -537,6 +590,10 @@ When option ``WITH_SOABI`` is also specified, the module suffix will include the ``Python_SOSABI`` value, if any. + +.. versionadded:: 3.30 + For ``MODULE`` type, the :prop_tgt:`DEBUG_POSTFIX` target property is + initialized with the value of ``Python_DEBUG_POSTFIX`` variable if defined. #]=======================================================================]
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index 08ab9c0..e0bdbc5 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake
@@ -20,6 +20,8 @@ cmake_policy (SET CMP0124 NEW) # registry view behavior cmake_policy (SET CMP0134 NEW) +# file(STRINGS) with REGEX updates CMAKE_MATCH_<n> +cmake_policy(SET CMP0159 NEW) if (NOT DEFINED _PYTHON_PREFIX) message (FATAL_ERROR "FindPython: INTERNAL ERROR") @@ -28,7 +30,7 @@ message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") - set(_${_PYTHON_PREFIX}_VERSIONS 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + set(_${_PYTHON_PREFIX}_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "2") set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) else() @@ -223,18 +225,24 @@ endfunction() -function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS) - set (abiflags) - list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug) - list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc) - list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode) +function (_PYTHON_GET_ABIFLAGS _PGA_FIND_ABI _PGABIFLAGS) + set (abiflags "<none>") + list (GET _PGA_FIND_ABI 0 pydebug) + list (GET _PGA_FIND_ABI 1 pymalloc) + list (GET _PGA_FIND_ABI 2 unicode) + list (LENGTH _PGA_FIND_ABI find_abi_length) + if (find_abi_length GREATER 3) + list (GET _PGA_FIND_ABI 3 gil) + else() + set (gil "OFF") + endif() if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY") - set (abiflags "mu" "m" "u" "") + set (abiflags "mu" "m" "u" "<none>") elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON") set (abiflags "mu" "u") elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF") - set (abiflags "m" "") + set (abiflags "m" "<none>") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") set (abiflags "mu" "m") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON") @@ -242,7 +250,7 @@ elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF") set (abiflags "m") elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") - set (abiflags "u" "") + set (abiflags "u" "<none>") elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON") set (abiflags "u") endif() @@ -259,10 +267,26 @@ list (TRANSFORM flags PREPEND "d") list (APPEND abiflags "${flags}") else() - set (abiflags "" "d") + set (abiflags "<none>" "d") endif() endif() + if (gil STREQUAL "ON") + if (abiflags) + list (TRANSFORM abiflags PREPEND "t") + else() + set (abiflags "t") + endif() + elseif (gil STREQUAL "ANY") + if (abiflags) + set (flags "${abiflags}") + list (TRANSFORM flags PREPEND "t") + list (APPEND abiflags "${flags}") + else() + set (abiflags "<none>" "t") + endif() + endif() + list (TRANSFORM abiflags REPLACE "^(.+)<none>$" "\\1") set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE) endfunction() @@ -273,11 +297,8 @@ set (_PGPS_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) endif() - if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) - set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") - else() - set (abi "mu" "m" "u" "") - endif() + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + list (TRANSFORM abi REPLACE "^<none>$" "") set (path_suffixes) @@ -358,30 +379,36 @@ foreach (implementation IN LISTS _PGN_IMPLEMENTATIONS) if (implementation STREQUAL "CPython") if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "FIRST") - list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + if (_PGN_DEBUG) + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}_d python_d) + else() + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + endif() endif() foreach (version IN LISTS _PGN_VERSION) if (_PGN_WIN32) - string (REPLACE "." "" version_no_dots ${version}) - - set (name python${version_no_dots}) - if (_PGN_DEBUG) - string (APPEND name "_d") + if (_PGN_INTERPRETER) + set (name_version ${version}) + else() + string (REPLACE "." "" name_version ${version}) endif() - - list (APPEND names "${name}") + 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) + list (TRANSFORM abinames APPEND "_d") + endif() + list (APPEND names ${abinames}) endif() if (_PGN_POSIX) - if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) - set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") - else() - if (_PGN_INTERPRETER OR _PGN_CONFIG) - set (abi "") - else() - set (abi "mu" "m" "u" "") - endif() - endif() + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + list (TRANSFORM abi REPLACE "^<none>$" "") if (abi) if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) @@ -410,7 +437,11 @@ endif() endforeach() if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "LAST") - list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + if (_PGN_DEBUG) + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}_d python_d) + else() + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + endif() endif() elseif (implementation STREQUAL "IronPython") if (_PGN_INTERPRETER) @@ -456,7 +487,7 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME) unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE) - if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI)$") + if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI|POSTFIX)$") return() endif() @@ -492,10 +523,13 @@ if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") set(_values "") else() - string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + string (REGEX REPLACE "^([.-]|_d\\.)(.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\2" _values "${_values}") endif() endif() endif() + if (NAME STREQUAL "ABIFLAGS" AND NOT _values) + set (_values "<none>") + endif() endif() if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING) @@ -543,7 +577,7 @@ if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") set(_values "") else() - string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + string (REGEX REPLACE "^([.-]|_d\\.)(.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\2" _values "${_values}") endif() endif() endif() @@ -570,7 +604,7 @@ if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") set(_values "") else() - string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + string (REGEX REPLACE "^([.-]|_d\\.)(.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") endif() endif() endif() @@ -586,6 +620,24 @@ else() string (REGEX REPLACE "^\\.(.+)\\.[^.]+$" "\\1" _values "${_values}") endif() + elseif (NAME STREQUAL "POSTFIX") + if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "_d${CMAKE_IMPORT_LIBRARY_SUFFIX}$") + set (_values "_d") + endif() + elseif (NAME STREQUAL "ABIFLAGS" AND WIN32) + # config var ABIFLAGS does not exist, check GIL specific variable + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; import sysconfig; sys.stdout.write(str(sysconfig.get_config_var('Py_GIL_DISABLED')))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (result OR NOT _values EQUAL "1") + # assume ABI is not supported or GIL is set + set (_values "<none>") + else() + set (_values "t") + endif() else() set (config_flag "${NAME}") if (NAME STREQUAL "CONFIGDIR") @@ -600,10 +652,13 @@ if (_result) unset (_values) endif() + if (NAME STREQUAL "ABIFLAGS" AND NOT _values) + set (_values "<none>") + endif() endif() endif() - if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI") + if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI" OR NAME STREQUAL "POSTFIX") set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) return() endif() @@ -636,26 +691,27 @@ unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE) unset (${_PGV_PREFIX}ABI PARENT_SCOPE) + unset (abi) + if (_PGV_LIBRARY) # retrieve version and abi from library name 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]+)") + 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) - set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) - elseif (library_name MATCHES "python([23])\\.([0-9]+)([dmu]*)") + set (abi "${CMAKE_MATCH_3}") + elseif (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) - set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_3}" PARENT_SCOPE) + set (abi "${CMAKE_MATCH_3}") elseif (library_name MATCHES "pypy([23])\\.([0-9]+)-c") 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) - set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) elseif (library_name MATCHES "pypy(3)?-c") set (version "${CMAKE_MATCH_1}") # try to pick-up a more precise version from the path @@ -671,7 +727,6 @@ set (${_PGV_PREFIX}VERSION_MAJOR "2" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "2" PARENT_SCOPE) endif() - set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) endif() endif() elseif (_PGV_SABI_LIBRARY) @@ -679,14 +734,13 @@ 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])([dmu]*)") + 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 (${_PGV_PREFIX}ABI "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (abi "${CMAKE_MATCH_2}") elseif (library_name MATCHES "pypy([23])-c") set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) - set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) elseif (library_name MATCHES "pypy-c") # try to pick-up a more precise version from the path get_filename_component (library_dir "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) @@ -694,7 +748,6 @@ set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) endif() - set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) endif() endif() else() @@ -716,39 +769,44 @@ # compute ABI flags if (version_major VERSION_GREATER "2") - file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(Py_DEBUG|WITH_PYMALLOC|Py_UNICODE_SIZE|MS_WIN32)") - set (abi) - if (config MATCHES "#[ ]*define[ ]+MS_WIN32") - # ABI not used on Windows - set (abi "") + set (config_flags "Py_GIL_DISABLED|Py_DEBUG|Py_UNICODE_SIZE|MS_WIN32") + if ("${version_major}.${version_minor}" VERSION_LESS "3.8") + # pymalloc is not the default. Must be part of the name signature + string (APPEND config_flags "|WITH_PYMALLOC") + endif() + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(${config_flags})") + if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+MS_WIN32") + # same header is used regardless abi, so set is as <none> + set (abi "<none>") else() if (NOT config) # pyconfig.h can be a wrapper to a platform specific pyconfig.h # In this case, try to identify ABI from include directory - if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([dmu]*)") + if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([tdmu]*)") set (abi "${CMAKE_MATCH_1}") - else() - set (abi "") endif() else() - if (config MATCHES "#[ ]*define[ ]+Py_DEBUG[ ]+1") + if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+Py_GIL_DISABLED[ ]+1") + string (APPEND abi "t") + endif() + if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+Py_DEBUG[ ]+1") string (APPEND abi "d") endif() - if (config MATCHES "#[ ]*define[ ]+WITH_PYMALLOC[ ]+1") + if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+WITH_PYMALLOC[ ]+1") string (APPEND abi "m") endif() - if (config MATCHES "#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4") + if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4") string (APPEND abi "u") endif() endif() - set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE) endif() - else() - # ABI not supported - set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) endif() endif() endif() + if (NOT abi) + set (abi "<none>") + endif() + set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE) endfunction() function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME) @@ -815,6 +873,7 @@ function (_PYTHON_VALIDATE_INTERPRETER) if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + unset (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG CACHE) return() endif() @@ -824,23 +883,17 @@ # interpreter does not exist anymore set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() - _python_get_launcher (launcher INTERPRETER) + _python_get_launcher (_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER INTERPRETER) # validate ABI compatibility if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI) - execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c - "import sys; sys.stdout.write(sys.abiflags)" - RESULT_VARIABLE result - OUTPUT_VARIABLE abi - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (result) - # assume ABI is not supported - set (abi "") - endif() + _python_get_config_var (abi ABIFLAGS) if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") @@ -851,7 +904,7 @@ if (_PVI_IN_RANGE OR _PVI_VERSION) # retrieve full version - execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" RESULT_VARIABLE result OUTPUT_VARIABLE version @@ -861,6 +914,9 @@ # interpreter is not usable set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() @@ -886,6 +942,9 @@ # interpreter has wrong version set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() else() # check that version is OK @@ -895,6 +954,9 @@ OR NOT version VERSION_GREATER_EQUAL _PVI_VERSION) set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() endif() @@ -907,6 +969,9 @@ # interpreter has invalid version set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() endif() @@ -915,7 +980,7 @@ if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}") # executable found do not have version in name # ensure major version is OK - execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(str(sys.version_info[0]))" RESULT_VARIABLE result OUTPUT_VARIABLE version @@ -929,6 +994,9 @@ set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() endif() @@ -939,7 +1007,7 @@ OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) AND NOT CMAKE_CROSSCOMPILING) # In this case, interpreter must have same architecture as environment - execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))" RESULT_VARIABLE result OUTPUT_VARIABLE size @@ -953,6 +1021,9 @@ set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() @@ -963,7 +1034,7 @@ else() set(target_arm FALSE) endif() - execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys, sysconfig; sys.stdout.write(sysconfig.get_platform())" RESULT_VARIABLE result OUTPUT_VARIABLE platform @@ -979,6 +1050,9 @@ set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") endif() set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + if (WIN32 AND DEFINED CACHE{_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}) + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND") + endif() return() endif() endif() @@ -1104,7 +1178,7 @@ function (_PYTHON_VALIDATE_LIBRARY) if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) - unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) return() endif() @@ -1171,7 +1245,7 @@ function (_PYTHON_VALIDATE_SABI_LIBRARY) if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) - unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) + unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE) return() endif() @@ -1231,7 +1305,7 @@ # retrieve version from header file _python_get_version (INCLUDE PREFIX inc_) - if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + if (NOT WIN32 AND DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # incompatible ABI set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") @@ -1431,25 +1505,33 @@ if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS "3") # ABI not supported unset (_${_PYTHON_PREFIX}_FIND_ABI) - set (_${_PYTHON_PREFIX}_ABIFLAGS "") + set (_${_PYTHON_PREFIX}_ABIFLAGS "<none>") else() unset (_${_PYTHON_PREFIX}_FIND_ABI) - unset (_${_PYTHON_PREFIX}_ABIFLAGS) if (DEFINED ${_PYTHON_PREFIX}_FIND_ABI) # normalization string (TOUPPER "${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_FIND_ABI) list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(TRUE|Y(ES)?|1)$" "ON") list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(FALSE|N(O)?|0)$" "OFF") - if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(ON|OFF|ANY);(ON|OFF|ANY)$") + if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(ON|OFF|ANY);(ON|OFF|ANY)(;(ON|OFF|ANY))?$") 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}_ABIFLAGS) + _python_get_abiflags ("${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_ABIFLAGS) + else() + if (WIN32) + _python_get_abiflags ("OFF;OFF;OFF;OFF" _${_PYTHON_PREFIX}_ABIFLAGS) + else() + _python_get_abiflags ("ANY;ANY;ANY;OFF" _${_PYTHON_PREFIX}_ABIFLAGS) + endif() endif() endif() unset (${_PYTHON_PREFIX}_SOABI) unset (${_PYTHON_PREFIX}_SOSABI) +# Windows CPython implementation may be requiring a postfix in debug mode +unset (${_PYTHON_PREFIX}_DEBUG_POSTFIX) + # Define lookup strategy cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY) if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW") @@ -1786,6 +1868,7 @@ # first step, search for the interpreter if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_EXECUTABLE + _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) @@ -1798,6 +1881,7 @@ unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) endif() set (_${_PYTHON_PREFIX}_EXECUTABLE "${${_PYTHON_PREFIX}_EXECUTABLE}" CACHE INTERNAL "") + unset (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG CACHE) elseif (DEFINED _${_PYTHON_PREFIX}_EXECUTABLE) # compute interpreter signature and check validity of definition string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") @@ -1814,6 +1898,7 @@ endif() else() unset (_${_PYTHON_PREFIX}_EXECUTABLE CACHE) + unset (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG CACHE) endif() if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) @@ -2141,16 +2226,7 @@ # Use interpreter version and ABI for future searches to ensure consistency set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) - execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c - "import sys; sys.stdout.write(sys.abiflags)" - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_ABIFLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (_${_PYTHON_PREFIX}_RESULT) - # assunme ABI is not supported - set (_${_PYTHON_PREFIX}_ABIFLAGS "") - endif() + _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS) endif() if (${_PYTHON_PREFIX}_Interpreter_FOUND) @@ -2261,7 +2337,30 @@ set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}" CACHE FILEPATH "${_PYTHON_PREFIX} Interpreter") endif() + if (WIN32 AND _${_PYTHON_PREFIX}_EXECUTABLE AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + # search for debug interpreter + # use release interpreter location as a hint + _python_get_names (_${_PYTHON_PREFIX}_INTERPRETER_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} IMPLEMENTATIONS CPython INTERPRETER WIN32 DEBUG) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + find_program (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG + NAMES ${_${_PYTHON_PREFIX}_INTERPRETER_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" ${${_PYTHON_PREFIX}_HINTS} + NO_DEFAULT_PATH) + # second try including CMAKE variables to catch-up non conventional layouts + find_program (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG + NAMES ${_${_PYTHON_PREFIX}_INTERPRETER_NAMES_DEBUG} + NAMES_PER_DIR + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + set (${_PYTHON_PREFIX}_EXECUTABLE_DEBUG "${_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}") + set (${_PYTHON_PREFIX}_INTERPRETER "$<IF:$<AND:$<CONFIG:Debug>,$<BOOL:${WIN32}>,$<BOOL:${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}>>,${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG},${${_PYTHON_PREFIX}_EXECUTABLE}>") + _python_mark_as_internal (_${_PYTHON_PREFIX}_EXECUTABLE + _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) endif() @@ -2785,7 +2884,7 @@ OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assume ABI is not supported - set (__${_PYTHON_PREFIX}_ABIFLAGS "") + set (__${_PYTHON_PREFIX}_ABIFLAGS "<none>") endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # Wrong ABI @@ -2880,7 +2979,7 @@ OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) # assume ABI is not supported - set (__${_PYTHON_PREFIX}_ABIFLAGS "") + set (__${_PYTHON_PREFIX}_ABIFLAGS "<none>") endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) # Wrong ABI @@ -3625,6 +3724,14 @@ _${_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) @@ -3654,6 +3761,14 @@ _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) 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() endif() if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR) @@ -3673,7 +3788,7 @@ endif() if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS - OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)) + OR (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) set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE) @@ -3728,6 +3843,10 @@ _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI) endif() + if (WIN32 AND NOT DEFINED ${_PYTHON_PREFIX}_DEBUG_POSTFIX) + _python_get_config_var (${_PYTHON_PREFIX}_DEBUG_POSTFIX POSTFIX) + endif() + _python_compute_development_signature (Module) _python_compute_development_signature (SABIModule) _python_compute_development_signature (Embed) @@ -3782,6 +3901,15 @@ endif() endif() + # Workaround Intel MKL library outputting a message in stdout, which cause + # incorrect detection of numpy.get_include() and numpy.__version__ + # See https://github.com/numpy/numpy/issues/23775 and + # https://gitlab.kitware.com/cmake/cmake/-/issues/26240 + if(NOT DEFINED ENV{MKL_ENABLE_INSTRUCTIONS}) + set(_${_PYTHON_PREFIX}_UNSET_ENV_VAR_MKL_ENABLE_INSTRUCTIONS YES) + set(ENV{MKL_ENABLE_INSTRUCTIONS} "SSE4_2") + endif() + if (NOT _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) execute_process(COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry: import numpy; sys.stdout.write(numpy.get_include())\nexcept:pass\n" @@ -3822,6 +3950,12 @@ set (${_PYTHON_PREFIX}_NumPy_FOUND FALSE) endif() + # Unset MKL_ENABLE_INSTRUCTIONS if set by us + if(DEFINED _${_PYTHON_PREFIX}_UNSET_ENV_VAR_MKL_ENABLE_INSTRUCTIONS) + unset(_${_PYTHON_PREFIX}_UNSET_ENV_VAR_MKL_ENABLE_INSTRUCTIONS) + unset(ENV{MKL_ENABLE_INSTRUCTIONS}) + endif() + if (${_PYTHON_PREFIX}_NumPy_FOUND) unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE) @@ -3874,11 +4008,27 @@ # Create imported targets and helper functions if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS - AND ${_PYTHON_PREFIX}_Interpreter_FOUND - AND NOT TARGET ${_PYTHON_PREFIX}::Interpreter) - add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED) - set_property (TARGET ${_PYTHON_PREFIX}::Interpreter - PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}") + AND ${_PYTHON_PREFIX}_Interpreter_FOUND) + if(NOT TARGET ${_PYTHON_PREFIX}::Interpreter) + add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::Interpreter + PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}") + endif() + if(${_PYTHON_PREFIX}_EXECUTABLE_DEBUG AND NOT TARGET ${_PYTHON_PREFIX}::InterpreterDebug) + add_executable (${_PYTHON_PREFIX}::InterpreterDebug IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::InterpreterDebug + PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}") + endif() + if(NOT TARGET ${_PYTHON_PREFIX}::InterpreterMultiConfig) + add_executable (${_PYTHON_PREFIX}::InterpreterMultiConfig IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::InterpreterMultiConfig + PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}") + if(${_PYTHON_PREFIX}_EXECUTABLE_DEBUG) + set_target_properties (${_PYTHON_PREFIX}::InterpreterMultiConfig + PROPERTIES IMPORTED_CONFIGURATIONS DEBUG + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}") + endif() + endif() endif() if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS @@ -3916,6 +4066,12 @@ set_property (TARGET ${__name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") + if (${_PYTHON_PREFIX}_DEFINITIONS) + set_property (TARGET ${__name} + PROPERTY INTERFACE_COMPILE_DEFINITIONS "${${_PYTHON_PREFIX}_DEFINITIONS}") + endif() + + if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE) # System manage shared libraries in two parts: import and runtime if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG) @@ -3972,6 +4128,11 @@ set_property (TARGET ${__name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") + if (${_PYTHON_PREFIX}_DEFINITIONS) + set_property (TARGET ${__name} + PROPERTY INTERFACE_COMPILE_DEFINITIONS "${${_PYTHON_PREFIX}_DEFINITIONS}") + endif() + # When available, enforce shared library generation with undefined symbols if (APPLE) set_property (TARGET ${__name} @@ -4090,6 +4251,9 @@ set_property (TARGET ${name} PROPERTY PREFIX "") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property (TARGET ${name} PROPERTY SUFFIX ".pyd") + if (${prefix}_DEBUG_POSTFIX) + set_property (TARGET ${name} PROPERTY DEBUG_POSTFIX "${${prefix}_DEBUG_POSTFIX}") + endif() endif() if (PYTHON_ADD_LIBRARY_WITH_SOABI)
diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake index 4c7ab5c..eb2b5a8 100644 --- a/Modules/FindPython2.cmake +++ b/Modules/FindPython2.cmake
@@ -71,9 +71,25 @@ :prop_gbl:`CMAKE_ROLE` is ``PROJECT``. ``Python2::Interpreter`` - Python 2 interpreter. Target defined if component ``Interpreter`` is found. + Python 2 interpreter. This target is defined only if the ``Interpreter`` + component is found. +``Python2::InterpreterDebug`` + .. versionadded:: 3.30 + + Python 2 debug interpreter. This target is defined only if the + ``Interpreter`` component is found and the ``Python2_EXECUTABLE_DEBUG`` + variable is defined. The target is only defined on the ``Windows`` platform. + +``Python2::InterpreterMultiConfig`` + .. versionadded:: 3.30 + + Python 2 interpreter. The release or debug version of the interpreter will be + used, based on the context (platform, configuration). + This target is defined only if the ``Interpreter`` component is found + ``Python2::Compiler`` - Python 2 compiler. Target defined if component ``Compiler`` is found. + Python 2 compiler. This target is defined only if the ``Compiler`` component + is found. ``Python2::Module`` .. versionadded:: 3.15 @@ -101,6 +117,20 @@ System has the Python 2 interpreter. ``Python2_EXECUTABLE`` Path to the Python 2 interpreter. +``Python2_EXECUTABLE_DEBUG`` + .. versionadded:: 3.30 + + Path to the debug Python 2 interpreter. It is only defined on the ``Windows`` + platform. + +``Python2_INTERPRETER`` + .. versionadded:: 3.30 + + Path to the Python 2 interpreter, defined as a + :manual:`generator expression <cmake-generator-expressions(7)>` selecting + the ``Python2_EXECUTABLE`` or ``Python2_EXECUTABLE_DEBUG`` variable based on + the context (platform, configuration). + ``Python2_INTERPRETER_ID`` A short string unique to the interpreter. Possible values include: * Python @@ -158,6 +188,12 @@ ``Python2_INCLUDE_DIRS`` The Python 2 include directories. +``Python2_DEBUG_POSTFIX`` + .. versionadded.. 3.30 + + Postfix of debug python module. This variable can be used to define the + :prop_tgt:`DEBUG_POSTFIX` target property. + ``Python2_LINK_OPTIONS`` .. versionadded:: 3.19 @@ -417,6 +453,10 @@ <source1> [<source2> ...]) If library type is not specified, ``MODULE`` is assumed. + +.. versionadded:: 3.30 + For ``MODULE`` type, the :prop_tgt:`DEBUG_POSTFIX` target property is + initialized with the value of ``Python2_DEBUG_POSTFIX`` variable if defined. #]=======================================================================]
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake index 901565b..19b0224 100644 --- a/Modules/FindPython3.cmake +++ b/Modules/FindPython3.cmake
@@ -78,9 +78,25 @@ :prop_gbl:`CMAKE_ROLE` is ``PROJECT``. ``Python3::Interpreter`` - Python 3 interpreter. Target defined if component ``Interpreter`` is found. + Python 3 interpreter. This target is defined only if the ``Interpreter`` + component is found. +``Python3::InterpreterDebug`` + .. versionadded:: 3.30 + + Python 3 debug interpreter. This target is defined only if the + ``Interpreter`` component is found and the ``Python3_EXECUTABLE_DEBUG`` + variable is defined. The target is only defined on the ``Windows`` platform. + +``Python3::InterpreterMultiConfig`` + .. versionadded:: 3.30 + + Python 3 interpreter. The release or debug version of the interpreter will be + used, based on the context (platform, configuration). + This target is defined only if the ``Interpreter`` component is found + ``Python3::Compiler`` - Python 3 compiler. Target defined if component ``Compiler`` is found. + Python 3 compiler. This target is defined only if the ``Compiler`` component + is found. ``Python3::Module`` .. versionadded:: 3.15 @@ -115,6 +131,20 @@ System has the Python 3 interpreter. ``Python3_EXECUTABLE`` Path to the Python 3 interpreter. +``Python3_EXECUTABLE_DEBUG`` + .. versionadded:: 3.30 + + Path to the debug Python 3 interpreter. It is only defined on ``Windows`` + platform. + +``Python3_INTERPRETER`` + .. versionadded:: 3.30 + + Path to the Python 3 interpreter, defined as a + :manual:`generator expression <cmake-generator-expressions(7)>` selecting + the ``Python3_EXECUTABLE`` or ``Python3_EXECUTABLE_DEBUG`` variable based on + the context (platform, configuration). + ``Python3_INTERPRETER_ID`` A short string unique to the interpreter. Possible values include: * Python @@ -196,6 +226,17 @@ The Python 3 include directories. +``Python3_DEFINITIONS`` + .. versionadded:: 3.30.3 + + The Python 3 preprocessor definitions. + +``Python3_DEBUG_POSTFIX`` + .. versionadded.. 3.30 + + Postfix of debug python module. This variable can be used to define the + :prop_tgt:`DEBUG_POSTFIX` target property. + ``Python3_LINK_OPTIONS`` .. versionadded:: 3.19 @@ -272,42 +313,54 @@ This variable defines which ABIs, as defined in :pep:`3149`, should be searched. - .. note:: + The ``Python3_FIND_ABI`` variable is a 4-tuple specifying, in that order, + ``pydebug`` (``d``), ``pymalloc`` (``m``), ``unicode`` (``u``) and + ``gil_disabled`` (``t``) flags. - If ``Python3_FIND_ABI`` is not defined, any ABI will be searched. + .. versionadded:: 3.30 + A fourth element, specifying the ``gil_disabled`` flag (i.e. free + threaded python), is added and is optional. If not specified, the value is + ``OFF``. - The ``Python3_FIND_ABI`` variable is a 3-tuple specifying, in that order, - ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags. Each element can be set to one of the following: * ``ON``: Corresponding flag is selected. * ``OFF``: Corresponding flag is not selected. * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched. - From this 3-tuple, various ABIs will be searched starting from the most - specialized to the most general. Moreover, ``debug`` versions will be - searched **after** ``non-debug`` ones. + .. note:: + + If ``Python3_FIND_ABI`` is not defined, any ABI, excluding the + ``gil_disabled`` flag, will be searched. + + From this 4-tuple, various ABIs will be searched starting from the most + specialized to the most general. Moreover, when ``ANY`` is specified for + ``pydebug`` and ``gil_disabled``, ``debug`` and ``free threaded`` versions + will be searched **after** ``non-debug`` and ``non-gil-disabled`` ones. For example, if we have:: - set (Python3_FIND_ABI "ON" "ANY" "ANY") + set (Python3_FIND_ABI "ON" "ANY" "ANY" "ON") The following flags combinations will be appended, in that order, to the - artifact names: ``dmu``, ``dm``, ``du``, and ``d``. + artifact names: ``tdmu``, ``tdm``, ``tdu``, and ``td``. And to search any possible ABIs:: - set (Python3_FIND_ABI "ANY" "ANY" "ANY") + set (Python3_FIND_ABI "ANY" "ANY" "ANY" "ANY") The following combinations, in that order, will be used: ``mu``, ``m``, - ``u``, ``<empty>``, ``dmu``, ``dm``, ``du`` and ``d``. + ``u``, ``<empty>``, ``dmu``, ``dm``, ``du``, ``d``, ``tmu``, ``tm``, ``tu``, + ``t``, ``tdmu``, ``tdm``, ``tdu``, and ``td``. .. note:: - This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems, - when ``Python3_FIND_ABI`` is defined, ``Python`` distributions from - `python.org <https://www.python.org/>`_ will be found only if value for - each flag is ``OFF`` or ``ANY``. + 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``). ``Python3_FIND_STRATEGY`` .. versionadded:: 3.15 @@ -535,6 +588,10 @@ When option ``WITH_SOABI`` is also specified, the module suffix will include the ``Python3_SOSABI`` value, if any. + +.. versionadded:: 3.30 + For ``MODULE`` type, the :prop_tgt:`DEBUG_POSTFIX` target property is + initialized with the value of ``Python3_DEBUG_POSTFIX`` variable if defined. #]=======================================================================]
diff --git a/Modules/FindPythonInterp.cmake b/Modules/FindPythonInterp.cmake index 443c6eb..e5bcb5f 100644 --- a/Modules/FindPythonInterp.cmake +++ b/Modules/FindPythonInterp.cmake
@@ -67,7 +67,7 @@ set(_PYTHON1_VERSIONS 1.6 1.5) set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) -set(_PYTHON3_VERSIONS 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +set(_PYTHON3_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) if(PythonInterp_FIND_VERSION) if(PythonInterp_FIND_VERSION_COUNT GREATER 1)
diff --git a/Modules/FindPythonLibs.cmake b/Modules/FindPythonLibs.cmake index b13d96b..467e8c2 100644 --- a/Modules/FindPythonLibs.cmake +++ b/Modules/FindPythonLibs.cmake
@@ -48,6 +48,9 @@ of PYTHON_LIBRARIES. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + cmake_policy(GET CMP0148 _FindPythonLibs_CMP0148) if(_FindPythonLibs_CMP0148 STREQUAL "NEW") message(FATAL_ERROR "The FindPythonLibs module has been removed by policy CMP0148.") @@ -55,6 +58,7 @@ if(_FindPythonLibs_testing) set(_FindPythonLibs_included TRUE) + cmake_policy(POP) return() endif() @@ -92,7 +96,7 @@ set(_PYTHON1_VERSIONS 1.6 1.5) set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) -set(_PYTHON3_VERSIONS 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +set(_PYTHON3_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) if(PythonLibs_FIND_VERSION) if(PythonLibs_FIND_VERSION_COUNT GREATER 1) @@ -410,3 +414,5 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_filenameTmp}" "${_filename}" OUTPUT_QUIET ERROR_QUIET) endfunction() + +cmake_policy(POP)
diff --git a/Modules/FindQt3.cmake b/Modules/FindQt3.cmake index da82e59..5a0037c 100644 --- a/Modules/FindQt3.cmake +++ b/Modules/FindQt3.cmake
@@ -34,6 +34,9 @@ library. This is only required by Qt3 on Windows. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # These are around for backwards compatibility # they will be set # QT_WRAP_CPP, set true if QT_MOC_EXECUTABLE is found @@ -47,6 +50,7 @@ if(NOT Qt3_FIND_QUIETLY) message( STATUS "Qt3 and Qt4 cannot be used together in one project.") endif() + cmake_policy(POP) return() endif() endif() @@ -304,3 +308,5 @@ QT_WRAP_CPP QT_WRAP_UI ) + +cmake_policy(POP)
diff --git a/Modules/FindSDL.cmake b/Modules/FindSDL.cmake index 7691eb7..a4c8668 100644 --- a/Modules/FindSDL.cmake +++ b/Modules/FindSDL.cmake
@@ -110,6 +110,9 @@ because not all systems place things in SDL/ (see FreeBSD). #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(SDL_INCLUDE_DIR SDL.h HINTS ENV SDLDIR @@ -235,3 +238,5 @@ INTERFACE_LINK_LIBRARIES "${SDL_LIBRARY}") endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindSDL_gfx.cmake b/Modules/FindSDL_gfx.cmake index 2dd96e9..82c2039 100644 --- a/Modules/FindSDL_gfx.cmake +++ b/Modules/FindSDL_gfx.cmake
@@ -24,6 +24,9 @@ ``./configure --prefix=$SDLDIR`` used in building SDL. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + find_path(SDL_GFX_INCLUDE_DIRS NAMES SDL_framerate.h @@ -84,3 +87,5 @@ INTERFACE_LINK_LIBRARIES "${SDL_GFX_LIBRARIES}") endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindSDL_image.cmake b/Modules/FindSDL_image.cmake index 324fef5..33a0695 100644 --- a/Modules/FindSDL_image.cmake +++ b/Modules/FindSDL_image.cmake
@@ -33,6 +33,9 @@ ./configure --prefix=$SDLDIR used in building SDL. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + if(NOT SDL_IMAGE_INCLUDE_DIR AND SDLIMAGE_INCLUDE_DIR) set(SDL_IMAGE_INCLUDE_DIR ${SDLIMAGE_INCLUDE_DIR} CACHE PATH "directory cache entry initialized from old variable name") @@ -95,3 +98,5 @@ set(SDLIMAGE_FOUND ${SDL_IMAGE_FOUND}) mark_as_advanced(SDL_IMAGE_LIBRARY SDL_IMAGE_INCLUDE_DIR) + +cmake_policy(POP)
diff --git a/Modules/FindSDL_mixer.cmake b/Modules/FindSDL_mixer.cmake index 8ed3cb4..f48015e 100644 --- a/Modules/FindSDL_mixer.cmake +++ b/Modules/FindSDL_mixer.cmake
@@ -33,6 +33,9 @@ ./configure --prefix=$SDLDIR used in building SDL. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + if(NOT SDL_MIXER_INCLUDE_DIR AND SDLMIXER_INCLUDE_DIR) set(SDL_MIXER_INCLUDE_DIR ${SDLMIXER_INCLUDE_DIR} CACHE PATH "directory cache entry initialized from old variable name") @@ -95,3 +98,5 @@ set(SDLMIXER_FOUND ${SDL_MIXER_FOUND}) mark_as_advanced(SDL_MIXER_LIBRARY SDL_MIXER_INCLUDE_DIR) + +cmake_policy(POP)
diff --git a/Modules/FindSDL_net.cmake b/Modules/FindSDL_net.cmake index 639e5bd..9d0b949 100644 --- a/Modules/FindSDL_net.cmake +++ b/Modules/FindSDL_net.cmake
@@ -32,6 +32,9 @@ ./configure --prefix=$SDLDIR used in building SDL. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + if(NOT SDL_NET_INCLUDE_DIR AND SDLNET_INCLUDE_DIR) set(SDL_NET_INCLUDE_DIR ${SDLNET_INCLUDE_DIR} CACHE PATH "directory cache entry initialized from old variable name") @@ -94,3 +97,5 @@ set(SDLNET_FOUND ${SDL_NET_FOUND}) mark_as_advanced(SDL_NET_LIBRARY SDL_NET_INCLUDE_DIR) + +cmake_policy(POP)
diff --git a/Modules/FindSDL_sound.cmake b/Modules/FindSDL_sound.cmake index 3443959..50b4b55 100644 --- a/Modules/FindSDL_sound.cmake +++ b/Modules/FindSDL_sound.cmake
@@ -84,6 +84,9 @@ libraries are needed. #]] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags") mark_as_advanced(SDL_SOUND_EXTRAS) @@ -371,3 +374,5 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_sound REQUIRED_VARS SDL_SOUND_LIBRARY SDL_SOUND_INCLUDE_DIR VERSION_VAR SDL_SOUND_VERSION_STRING) + +cmake_policy(POP)
diff --git a/Modules/FindSDL_ttf.cmake b/Modules/FindSDL_ttf.cmake index d67c089..8f3b02a 100644 --- a/Modules/FindSDL_ttf.cmake +++ b/Modules/FindSDL_ttf.cmake
@@ -32,6 +32,9 @@ ./configure --prefix=$SDLDIR used in building SDL. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + if(NOT SDL_TTF_INCLUDE_DIR AND SDLTTF_INCLUDE_DIR) set(SDL_TTF_INCLUDE_DIR ${SDLTTF_INCLUDE_DIR} CACHE PATH "directory cache entry initialized from old variable name") @@ -94,3 +97,5 @@ set(SDLTTF_FOUND ${SDL_TTF_FOUND}) mark_as_advanced(SDL_TTF_LIBRARY SDL_TTF_INCLUDE_DIR) + +cmake_policy(POP)
diff --git a/Modules/FindSQLite3.cmake b/Modules/FindSQLite3.cmake index 88c7dd2..308a444 100644 --- a/Modules/FindSQLite3.cmake +++ b/Modules/FindSQLite3.cmake
@@ -32,12 +32,24 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_SQLite3 QUIET sqlite3) + # Look for the necessary header -find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h) +find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h + HINTS + ${PC_SQLite3_INCLUDE_DIRS} +) mark_as_advanced(SQLite3_INCLUDE_DIR) # Look for the necessary library -find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite) +find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite + HINTS + ${PC_SQLite3_LIBRARY_DIRS} +) mark_as_advanced(SQLite3_LIBRARY) # Extract version information from the header file @@ -66,3 +78,5 @@ INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}") endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindSWIG.cmake b/Modules/FindSWIG.cmake index 370fff0..3104b2c 100644 --- a/Modules/FindSWIG.cmake +++ b/Modules/FindSWIG.cmake
@@ -58,6 +58,50 @@ #]=======================================================================] +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) + +function(_swig_get_version _swig_executable _swig_version) + unset(${_swig_version} PARENT_SCOPE) + # Determine SWIG version + execute_process(COMMAND "${_swig_executable}" -version + OUTPUT_VARIABLE _swig_output + ERROR_VARIABLE _swig_output + RESULT_VARIABLE _swig_result) + if(_swig_result) + set_property (CACHE _SWIG_REASON_FAILURE PROPERTY VALUE "Cannot use the executable \"${_swig_executable}\"") + if (_swig_output) + set_property (CACHE _SWIG_REASON_FAILURE APPEND_STRING PROPERTY VALUE ": ${_swig_output}") + endif() + else() + string(REGEX REPLACE ".*SWIG Version[^0-9.]*\([0-9.]+\).*" "\\1" + _swig_output "${_swig_output}") + set(${_swig_version} ${_swig_output} PARENT_SCOPE) + endif() +endfunction() + +function(_swig_validate_find_executable status executable) + _swig_get_version("${executable}" _swig_find_version) + if(NOT _swig_find_version) + # executable is unusable + set (${status} FALSE PARENT_SCOPE) + return() + endif() + if(NOT SWIG_FIND_VERSION) + return() + endif() + + find_package_check_version(${_swig_find_version} _swig_version_is_valid HANDLE_VERSION_RANGE) + if(_swig_version_is_valid) + unset(_SWIG_REASON_FAILURE CACHE) + else() + set (${status} FALSE PARENT_SCOPE) + set_property (CACHE _SWIG_REASON_FAILURE PROPERTY VALUE "Could NOT find SWIG: Found unsuitable version \"${_swig_find_version}\" for the executable \"${executable}\"") + endif() +endfunction() + +unset (_SWIG_REASON_FAILURE) +set (_SWIG_REASON_FAILURE CACHE INTERNAL "SWIG reason failure") + # compute list of possible names unset (_SWIG_NAMES) if (SWIG_FIND_VERSION_RANGE) @@ -86,7 +130,8 @@ set (_SWIG_NAMES swig4.0 swig3.0 swig2.0) endif() -find_program(SWIG_EXECUTABLE NAMES ${_SWIG_NAMES} swig) +find_program(SWIG_EXECUTABLE NAMES ${_SWIG_NAMES} swig NAMES_PER_DIR + VALIDATOR _swig_validate_find_executable) unset(_SWIG_NAMES) if(SWIG_EXECUTABLE AND NOT SWIG_DIR) @@ -115,17 +160,8 @@ if(SWIG_EXECUTABLE AND SWIG_DIR AND NOT SWIG_VERSION) # Determine SWIG version - execute_process(COMMAND "${SWIG_EXECUTABLE}" -version - OUTPUT_VARIABLE _swig_output - ERROR_VARIABLE _swig_output - RESULT_VARIABLE _swig_result) - if(_swig_result) - message(SEND_ERROR "Command \"${SWIG_EXECUTABLE} -version\" failed with output:\n${_swig_output}") - else() - string(REGEX REPLACE ".*SWIG Version[^0-9.]*\([0-9.]+\).*" "\\1" - _swig_output "${_swig_output}") - set(SWIG_VERSION ${_swig_output} CACHE STRING "Swig version" FORCE) - endif() + _swig_get_version("${SWIG_EXECUTABLE}" _swig_output) + set(SWIG_VERSION ${_swig_output} CACHE STRING "Swig version" FORCE) endif() if(SWIG_EXECUTABLE AND SWIG_FIND_COMPONENTS) @@ -145,16 +181,18 @@ endif() endif() -unset(_swig_output) -unset(_swig_error) -unset(_swig_result) - -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args( SWIG HANDLE_COMPONENTS REQUIRED_VARS SWIG_EXECUTABLE SWIG_DIR VERSION_VAR SWIG_VERSION - HANDLE_VERSION_RANGE) + HANDLE_VERSION_RANGE + FAIL_MESSAGE "${_SWIG_REASON_FAILURE}") + +unset(_swig_output) +unset(_swig_error) +unset(_swig_result) + +unset(_SWIG_REASON_FAILURE CACHE) if(SWIG_FOUND) set(SWIG_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseSWIG.cmake")
diff --git a/Modules/FindTIFF.cmake b/Modules/FindTIFF.cmake index fe1e94e..27beaa0 100644 --- a/Modules/FindTIFF.cmake +++ b/Modules/FindTIFF.cmake
@@ -67,6 +67,7 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> set(_TIFF_args) if (TIFF_FIND_VERSION)
diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake index 0f45b63..2262119 100644 --- a/Modules/FindVulkan.cmake +++ b/Modules/FindVulkan.cmake
@@ -221,6 +221,7 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> # Provide compatibility with a common invalid component request that # was silently ignored prior to CMake 3.24.
diff --git a/Modules/FindXalanC.cmake b/Modules/FindXalanC.cmake index a7fb766..338d0c0 100644 --- a/Modules/FindXalanC.cmake +++ b/Modules/FindXalanC.cmake
@@ -44,6 +44,9 @@ the Xalan library #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # Written by Roger Leigh <rleigh@codelibre.net> function(_XalanC_GET_VERSION version_hdr) @@ -153,3 +156,5 @@ set_target_properties(XalanC::XalanC PROPERTIES INTERFACE_LINK_LIBRARIES XercesC::XercesC) endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindXercesC.cmake b/Modules/FindXercesC.cmake index d39bbf6..ee6a401 100644 --- a/Modules/FindXercesC.cmake +++ b/Modules/FindXercesC.cmake
@@ -47,6 +47,9 @@ Debug and Release variants are found separately. #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # Written by Roger Leigh <rleigh@codelibre.net> function(_XercesC_GET_VERSION version_hdr) @@ -149,3 +152,5 @@ endif() endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FindZLIB.cmake b/Modules/FindZLIB.cmake index 4e7d5d6..0842f4c 100644 --- a/Modules/FindZLIB.cmake +++ b/Modules/FindZLIB.cmake
@@ -84,6 +84,9 @@ #]=======================================================================] +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + if(ZLIB_FIND_COMPONENTS AND NOT ZLIB_FIND_QUIETLY) message(AUTHOR_WARNING "ZLIB does not provide any COMPONENTS. Calling\n" @@ -232,3 +235,5 @@ endif() endif() endif() + +cmake_policy(POP)
diff --git a/Modules/FortranCInterface/CMakeLists.txt b/Modules/FortranCInterface/CMakeLists.txt index a0f1862..2e8e14f 100644 --- a/Modules/FortranCInterface/CMakeLists.txt +++ b/Modules/FortranCInterface/CMakeLists.txt
@@ -113,11 +113,15 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 12) target_compile_options(FortranCInterface PRIVATE "-fno-lto") - target_compile_options(myfort PRIVATE "-flto=auto" "-ffat-lto-objects") + if(NOT APPLE) + target_compile_options(myfort PRIVATE "-flto=auto" "-ffat-lto-objects") + endif() endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12) - target_compile_options(symbols PRIVATE "-flto=auto" "-ffat-lto-objects") + if(NOT APPLE) + target_compile_options(symbols PRIVATE "-flto=auto" "-ffat-lto-objects") + endif() endif() file(GENERATE OUTPUT exe-$<CONFIG>.cmake CONTENT [[
diff --git a/Modules/FortranCInterface/Detect.cmake b/Modules/FortranCInterface/Detect.cmake index d65c865..c975a4b 100644 --- a/Modules/FortranCInterface/Detect.cmake +++ b/Modules/FortranCInterface/Detect.cmake
@@ -76,8 +76,11 @@ # Load symbols from INFO:symbol[] strings in the executable. set(FortranCInterface_SYMBOLS) if(FortranCInterface_EXE) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${FortranCInterface_EXE}" _info_strings LIMIT_COUNT 8 REGEX "INFO:[A-Za-z0-9_]+\\[[^]]*\\]") + cmake_policy(POP) foreach(info ${_info_strings}) if("${info}" MATCHES "INFO:symbol\\[([^]]*)\\]") list(APPEND FortranCInterface_SYMBOLS ${CMAKE_MATCH_1})
diff --git a/Modules/GenerateExportHeader.cmake b/Modules/GenerateExportHeader.cmake index 9fdc783..c959168 100644 --- a/Modules/GenerateExportHeader.cmake +++ b/Modules/GenerateExportHeader.cmake
@@ -210,8 +210,8 @@ # TODO: Install this macro separately? macro(_check_c_compiler_attribute _ATTRIBUTE _RESULT) - check_source_compiles(C "${_ATTRIBUTE} int somefunc() { return 0; } - int main() { return somefunc();}" ${_RESULT} + check_source_compiles(C "${_ATTRIBUTE} int somefunc(void) { return 0; } + int main(void) { return somefunc();}" ${_RESULT} ) endmacro()
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index b62f839..e187804 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake
@@ -347,16 +347,51 @@ unset(testList) - set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*") + set(gtest_case_name_regex ".*\\([ \r\n\t]*([A-Za-z_0-9]+)[ \r\n\t]*,[ \r\n\t]*([A-Za-z_0-9]+)[ \r\n\t]*\\).*") set(gtest_test_type_regex "(TYPED_TEST|TEST)_?[FP]?") + set(each_line_regex "([^\r\n]*[\r\n])") foreach(source IN LISTS ARGS_SOURCES) if(NOT ARGS_SKIP_DEPENDENCY) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source}) endif() file(READ "${source}" contents) - string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests "${contents}") - foreach(hit ${found_tests}) + # Replace characters in file content that are special to CMake + string(REPLACE "[" "<OPEN_BRACKET>" contents "${contents}") + string(REPLACE "]" "<CLOSE_BRACKET>" contents "${contents}") + string(REPLACE ";" "\\;" contents "${contents}") + # Split into lines + string(REGEX MATCHALL "${each_line_regex}" content_lines "${contents}") + set(line "0") + # Stores the line number of the start of a test definition + set(accumulate_line "0") + # Stores accumulated lines to match multi-line test definitions + set(accumulated "") + # Iterate over each line in the file so that we know the line number of a test definition + foreach(line_str IN LISTS content_lines) + MATH(EXPR line "${line}+1") + # Check if the current line is the start of a test definition + string(REGEX MATCH "[ \t]*${gtest_test_type_regex}[ \t]*[\\(]*" accumlate_start_hit "${line_str}") + if(accumlate_start_hit) + set(accumulate_line "${line}") + endif() + # Append the current line to the accumulated string + set(accumulated "${accumulated}${line_str}") + # Attempt to match a complete test definition in the accumulated string + string(REGEX MATCH "${gtest_test_type_regex}[ \r\n\t]*\\(([A-Za-z_0-9 ,\r\n\t]+)\\)" hit "${accumulated}") + if(hit) + # Reset accumulated for the next match + set(accumulated "") + else() + # Continue accumulating lines + continue() + endif() + # At this point, the start line of the test definition is known + # Hence, we can set the test's DEF_SOURCE_LINE property with + # ${source}:${accumulate_line} below. + # VS Code CMake Tools extension looks for DEF_SOURCE_LINE + # to locate the test definition for its "Go to test" feature. + string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit}) # Parameterized tests have a different signature for the filter @@ -394,7 +429,8 @@ --gtest_filter=${gtest_test_name} ${ARGS_EXTRA_ARGS} ) - set_tests_properties(${ctest_test_name} PROPERTIES DISABLED TRUE) + set_tests_properties(${ctest_test_name} PROPERTIES DISABLED TRUE + DEF_SOURCE_LINE "${source}:${accumulate_line}") list(APPEND testList ${ctest_test_name}) endif() else() @@ -410,6 +446,7 @@ ${ctest_test_name} PROPERTIES SKIP_REGULAR_EXPRESSION "\\[ SKIPPED \\]" + DEF_SOURCE_LINE "${source}:${accumulate_line}" ) list(APPEND testList ${ctest_test_name}) endif()
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index 55a621c..d4a47e4 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -56,6 +56,65 @@ 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() + message(FATAL_ERROR "Readelf utility is not available.") + endif() +endfunction() + +#sanitizes the given directory name if required +function(get_sanitized_dirname dirname outvar) + # 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}") + string(MD5 santized_dirname "${dirname}") + set(${outvar} "${sanitized_dirname}" PARENT_SCOPE) + else() + set(${outvar} "${dirname}" PARENT_SCOPE) + endif() +endfunction() + +#retrieve packaging directories of components the current component depends on +# Note: May only be called from within 'cpack_deb_prepare_package_var'! +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!") + endif() + set(result_list) + foreach(dependency_name IN LISTS CPACK_COMPONENT_${_local_component_name}_DEPENDS) + get_sanitized_dirname("${dependency_name}" dependency_name) + cmake_path(APPEND_STRING WDIR "/../${dependency_name}" OUTPUT_VARIABLE dependency_packaging_dir) + cmake_path(NORMAL_PATH dependency_packaging_dir) + list(APPEND result_list "${dependency_packaging_dir}") + endforeach() + set(${outvar} "${result_list}" PARENT_SCOPE) # Set return variable. + else() + set(${outvar} "" PARENT_SCOPE) # Clear return variable. + endif() +endfunction() + function(cpack_deb_check_description SUMMARY LINES RESULT_VARIABLE) set(_result TRUE) @@ -310,16 +369,44 @@ set(IGNORE_MISSING_INFO_FLAG "--ignore-missing-info") endif() - if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS) + # Add -l option if the tool supports it? + if(DEFINED SHLIBDEPS_EXECUTABLE_VERSION AND SHLIBDEPS_EXECUTABLE_VERSION VERSION_GREATER_EQUAL 1.17.0) unset(PRIVATE_SEARCH_DIR_OPTIONS) - # Add -l option if the tool supports it - if(DEFINED SHLIBDEPS_EXECUTABLE_VERSION AND SHLIBDEPS_EXECUTABLE_VERSION VERSION_GREATER_EQUAL 1.17.0) - foreach(dir IN LISTS CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS) - list(APPEND PRIVATE_SEARCH_DIR_OPTIONS "-l${dir}") + + # Use directories provided via CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS + if(NOT "${CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS}" STREQUAL "") + foreach(path IN LISTS CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS) + cmake_path(NORMAL_PATH path) # Required for dpkg-shlibdeps! + list(APPEND PRIVATE_SEARCH_DIR_OPTIONS "-l${path}") endforeach() - else() - message(WARNING "CPackDeb: dkpg-shlibdeps is too old. \"CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS\" is therefore ignored.") endif() + + # Use directories extracted from RUNPATH/RPATH + 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) + # If RUNPATH is available, RPATH will be ignored. Therefore we have to do the same here! + if (NOT "${runpath}" STREQUAL "") + 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[}]") + 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}") + cmake_path(NORMAL_PATH path) # Required for dpkg-shlibdeps! + list(APPEND PRIVATE_SEARCH_DIR_OPTIONS "-l${path}") + endforeach() + endif() + endforeach() + endforeach() + + 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.") endif() # Execute dpkg-shlibdeps @@ -487,7 +574,7 @@ if(DEFINED ${_component_var}) set(CPACK_DEBIAN_PACKAGE_${value_type_} "${${_component_var}}") if(CPACK_DEBIAN_PACKAGE_DEBUG) - message("CPackDeb Debug: component '${_local_component_name}' ${value_type_} " + message("CPackDeb Debug: component '${CPACK_DEB_PACKAGE_COMPONENT}' ${value_type_} " "value set to '${CPACK_DEBIAN_PACKAGE_${value_type_}}'") endif() endif()
diff --git a/Modules/Internal/CPack/CPackNuGet.cmake b/Modules/Internal/CPack/CPackNuGet.cmake index 67f318a..83a8f43 100644 --- a/Modules/Internal/CPack/CPackNuGet.cmake +++ b/Modules/Internal/CPack/CPackNuGet.cmake
@@ -316,27 +316,21 @@ endif() # Handle dependencies - _cpack_nuget_variable_fallback(_deps DEPENDENCIES) - set(_collected_deps) - foreach(_dep IN LISTS _deps) - _cpack_nuget_debug(" checking dependency `${_dep}`") + # Primary deps (not specific to any framework) + _cpack_nuget_render_deps_group("" rendered_group) + string(APPEND _CPACK_NUGET_DEPENDENCIES_TAG "${rendered_group}") - _cpack_nuget_variable_fallback(_ver DEPENDENCIES_${_dep}_VERSION) - - if(NOT _ver) - string(TOUPPER "${_dep}" _dep_upper) - _cpack_nuget_variable_fallback(_ver DEPENDENCIES_${_dep_upper}_VERSION) - endif() - - if(_ver) - _cpack_nuget_debug(" got `${_dep}` dependency version ${_ver}") - string(CONCAT _collected_deps "${_collected_deps}" " <dependency id=\"${_dep}\" version=\"${_ver}\" />\n") - endif() + # Framework-specific deps + _cpack_nuget_variable_fallback(_tfms TFMS) + foreach(tfm IN LISTS _tfms) + _cpack_nuget_render_deps_group("${tfm}" rendered_group) + string(APPEND _CPACK_NUGET_DEPENDENCIES_TAG "${rendered_group}") endforeach() - # Render deps into the variable - if(_collected_deps) - string(CONCAT _CPACK_NUGET_DEPENDENCIES_TAG "<dependencies>\n" "${_collected_deps}" " </dependencies>") + # If there are any dependencies to include, wrap them with the appropriate tag + if(_CPACK_NUGET_DEPENDENCIES_TAG) + string(PREPEND _CPACK_NUGET_DEPENDENCIES_TAG "<dependencies>\n") + string(APPEND _CPACK_NUGET_DEPENDENCIES_TAG " </dependencies>") endif() # Render the spec file @@ -350,6 +344,109 @@ ) endfunction() +# Call this function once for each TWFM (e.g., 'net48') to generate the dependencies for +# that framework. It can also be called with an empty TWFM for the "general" set of +# dependencies, which are not specific to a framework. +function(_cpack_nuget_render_deps_group TFM OUTPUT_VAR_NAME) + _cpack_nuget_debug(" rendering deps for ${TFM}") + if(TFM) + set(_tfm "_${TFM}") + else() + set(_tfm "") + endif() + _cpack_nuget_variable_fallback(_deps DEPENDENCIES${_tfm}) + set(_collected_deps) + foreach(_dep IN LISTS _deps) + set(_ver) # Ensure we don't accidentally use the version from the previous dep in the list + _cpack_nuget_debug(" checking dependency `${_dep}`") + + _cpack_nuget_variable_fallback(_ver DEPENDENCIES${_tfm}_${_dep}_VERSION) + + if(NOT _ver) + string(TOUPPER "${_dep}" _dep_upper) + _cpack_nuget_variable_fallback(_ver DEPENDENCIES${_tfm}_${_dep_upper}_VERSION) + endif() + + if(_ver) + _cpack_nuget_debug(" got `${_dep}` dependency version ${_ver}") + string(APPEND _collected_deps " <dependency id=\"${_dep}\" version=\"${_ver}\" />\n") + endif() + endforeach() + + # Render deps into the variable + if(TFM) + _cpack_nuget_convert_tfm_to_frameworkname("${TFM}" framework_name) + _cpack_nuget_debug(" converted ${TFM} to ${framework_name}") + endif() + set(rendered_group) + if(_collected_deps) + if(TFM) + _cpack_nuget_debug(" rendering group for framework ${framework_name}") + string(CONCAT rendered_group " <group targetFramework=\"${framework_name}\">\n" "${_collected_deps}" " </group>\n") + else() + _cpack_nuget_debug(" rendering primary group") + string(CONCAT rendered_group " <group>\n" "${_collected_deps}" " </group>\n") + endif() + elseif(TFM) + _cpack_nuget_debug(" no deps for ${TFM}, rendering empty group") + # Insert an empty group for a framework that doesn't have any specific dependencies listed, as the existence + # of this group can be used by NuGet to see that the framework is supported. + string(CONCAT rendered_group " <group targetFramework=\"${framework_name}\" />\n") + endif() + set(${OUTPUT_VAR_NAME} "${rendered_group}" PARENT_SCOPE) +endfunction() + +# Tries to look up a Framework Name (e.g., '.NETFramework4.8') from a Target Framework Moniker (TFM) (e.g., 'net48') +function(_cpack_nuget_convert_tfm_to_frameworkname TFM OUTPUT_VAR_NAME) + # There are a few patterns to handle: + # 1a. net4 -> .NETFramework4 + # 1b. net5.0 -> net5.0 From version 5 onwards, the name just looks the same as the moniker, and both need to have a dot + # 2. netstandard13 -> .NETStandard1.3 + # 3. netcoreapp21 -> .NETCoreApp2.1 + # 4. dotnet50 -> .NETPlatform5.0 + # 5. native0.0 -> native0.0 Support for native C++ and mixed C++/CLI projects + if(TFM MATCHES "^net([1-4](.[\.0-9])?)$") # CMAKE_MATCH_1 holds the version part + _cpack_nuget_get_dotted_version("${CMAKE_MATCH_1}" dotted_version) + set(framework_name ".NETFramework${dotted_version}") + elseif(TFM MATCHES "^net[1-9](\.[0-9]+)+$") + set(framework_name "${TFM}") + elseif(TFM MATCHES "^netstandard([0-9]+(\.[0-9]+)*)$") + _cpack_nuget_get_dotted_version("${CMAKE_MATCH_1}" dotted_version) + set(framework_name ".NETStandard${dotted_version}") + elseif(TFM MATCHES "^netcoreapp([0-9]+(\.[0-9]+)*)$") + _cpack_nuget_get_dotted_version("${CMAKE_MATCH_1}" dotted_version) + set(framework_name ".NETCoreApp${dotted_version}") + elseif(TFM MATCHES "^dotnet([0-9]+(\.[0-9]+)*)$") + _cpack_nuget_get_dotted_version("${CMAKE_MATCH_1}" dotted_version) + set(framework_name ".NETPlatform${dotted_version}") + elseif(TFM STREQUAL "native0.0") + set(framework_name "${TFM}") + else() + message(FATAL_ERROR "Target Framework Moniker '${TFM}' not recognized") + endif() + set(${OUTPUT_VAR_NAME} ${framework_name} PARENT_SCOPE) +endfunction() + +function(_cpack_nuget_get_dotted_version VERSION OUTPUT_VAR_NAME) + if(VERSION MATCHES "\.") + # The version already has dots in it, just reuse the numbers given + set(dotted_version "${VERSION}") + else() + # No dots in the version, treat each digit as a version part + string(LENGTH "${VERSION}" length) + math(EXPR last_index "${length} - 1") + string(SUBSTRING "${VERSION}" 0 1 digit) + set(dotted_version "${digit}") + foreach(i RANGE 1 ${last_index}) + string(SUBSTRING "${VERSION}" ${i} 1 digit) + string(APPEND dotted_version ".${digit}") + endforeach() + endif() + # This would be a good place to remove any superfluous ".0"s from the end of the version string, but + # for now it should be fine to just expect the caller not to supply them in the first place. + set(${OUTPUT_VAR_NAME} "${dotted_version}" PARENT_SCOPE) +endfunction() + function(_cpack_nuget_make_files_tag) set(_files) foreach(_comp IN LISTS ARGN)
diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake index 23fb823..4a741ee 100644 --- a/Modules/Internal/CPack/CPackRPM.cmake +++ b/Modules/Internal/CPack/CPackRPM.cmake
@@ -711,7 +711,7 @@ endif() get_file_permissions("${WORKING_DIR}/${F}" permissions_) - if(NOT "USER_EXECUTE" IN_LIST permissions_ AND + if(NOT "OWNER_EXECUTE" IN_LIST permissions_ AND NOT "GROUP_EXECUTE" IN_LIST permissions_ AND NOT "WORLD_EXECUTE" IN_LIST permissions_) if(CPACK_RPM_INSTALL_WITH_EXEC) @@ -916,7 +916,7 @@ 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}") + 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"
diff --git a/Modules/Internal/CPack/CPackWIX.cmake b/Modules/Internal/CPack/CPackWIX.cmake index c40ddd5..9980bc2 100644 --- a/Modules/Internal/CPack/CPackWIX.cmake +++ b/Modules/Internal/CPack/CPackWIX.cmake
@@ -5,20 +5,30 @@ string(REPLACE "\\" "/" CPACK_WIX_ROOT "$ENV{WIX}") endif() -find_program(CPACK_WIX_CANDLE_EXECUTABLE candle - PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") +if(CPACK_WIX_VERSION VERSION_GREATER_EQUAL 4) + find_program(CPACK_WIX_EXECUTABLE NAMES wix + PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + if(NOT CPACK_WIX_EXECUTABLE) + message(FATAL_ERROR "Could not find the 'wix' executable.") + endif() -if(NOT CPACK_WIX_CANDLE_EXECUTABLE) - message(FATAL_ERROR "Could not find the WiX candle executable.") -endif() + if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE) + set(CPACK_WIX_INSTALL_SCOPE "perMachine") + endif() +else() + find_program(CPACK_WIX_CANDLE_EXECUTABLE candle + PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + if(NOT CPACK_WIX_CANDLE_EXECUTABLE) + message(FATAL_ERROR "Could not find the WiX candle executable.") + endif() -find_program(CPACK_WIX_LIGHT_EXECUTABLE light - PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + find_program(CPACK_WIX_LIGHT_EXECUTABLE light + PATHS "${CPACK_WIX_ROOT}" PATH_SUFFIXES "bin") + if(NOT CPACK_WIX_LIGHT_EXECUTABLE) + message(FATAL_ERROR "Could not find the WiX light executable.") + endif() -if(NOT CPACK_WIX_LIGHT_EXECUTABLE) - message(FATAL_ERROR "Could not find the WiX light executable.") -endif() - -if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE) - set(CPACK_WIX_INSTALL_SCOPE "NONE") + if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE) + set(CPACK_WIX_INSTALL_SCOPE "NONE") + endif() endif()
diff --git a/Modules/Internal/CPack/WIX-v3/WIX.template.in b/Modules/Internal/CPack/WIX-v3/WIX.template.in new file mode 100644 index 0000000..71ec5b2 --- /dev/null +++ b/Modules/Internal/CPack/WIX-v3/WIX.template.in
@@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?include "cpack_variables.wxi"?> + +<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"@CPACK_WIX_CUSTOM_XMLNS_EXPANDED@ + RequiredVersion="3.6.3303.0"> + + <Product Id="$(var.CPACK_WIX_PRODUCT_GUID)" + Name="$(var.CPACK_PACKAGE_NAME)" + Language="1033" + Version="$(var.CPACK_PACKAGE_VERSION)" + Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" + UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)"> + + + <?if $(var.CPACK_WIX_INSTALL_SCOPE) = "NONE" ?> + <Package InstallerVersion="301" Compressed="yes"/> + <?else?> + <Package InstallerVersion="301" Compressed="yes" InstallScope="$(var.CPACK_WIX_INSTALL_SCOPE)"/> + <?endif?> + + <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/> + + <MajorUpgrade + Schedule="afterInstallInitialize" + AllowSameVersionUpgrades="yes" + DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."/> + + <WixVariable Id="WixUILicenseRtf" Value="$(var.CPACK_WIX_LICENSE_RTF)"/> + <Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/> + + <?ifdef CPACK_WIX_PRODUCT_ICON?> + <Property Id="ARPPRODUCTICON">ProductIcon.ico</Property> + <Icon Id="ProductIcon.ico" SourceFile="$(var.CPACK_WIX_PRODUCT_ICON)"/> + <?endif?> + + <?ifdef CPACK_WIX_UI_BANNER?> + <WixVariable Id="WixUIBannerBmp" Value="$(var.CPACK_WIX_UI_BANNER)"/> + <?endif?> + + <?ifdef CPACK_WIX_UI_DIALOG?> + <WixVariable Id="WixUIDialogBmp" Value="$(var.CPACK_WIX_UI_DIALOG)"/> + <?endif?> + + <FeatureRef Id="ProductFeature"/> + + <UIRef Id="$(var.CPACK_WIX_UI_REF)" /> + <UIRef Id="WixUI_ErrorProgressText" /> + + <?include "properties.wxi"?> + <?include "product_fragment.wxi"?> + </Product> +</Wix>
diff --git a/Modules/Internal/CPack/WIX.template.in b/Modules/Internal/CPack/WIX.template.in index 95ba7fa..7cad186 100644 --- a/Modules/Internal/CPack/WIX.template.in +++ b/Modules/Internal/CPack/WIX.template.in
@@ -2,22 +2,22 @@ <?include "cpack_variables.wxi"?> -<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" @CPACK_WIX_CUSTOM_XMLNS_EXPANDED@ - RequiredVersion="3.6.3303.0"> +<Wix + xmlns="http://wixtoolset.org/schemas/v4/wxs"@CPACK_WIX_CUSTOM_XMLNS_EXPANDED@ + RequiredVersion="4.0" + > - <Product Id="$(var.CPACK_WIX_PRODUCT_GUID)" + <Package Name="$(var.CPACK_PACKAGE_NAME)" - Language="1033" Version="$(var.CPACK_PACKAGE_VERSION)" Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" - UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)"> - - - <?if $(var.CPACK_WIX_INSTALL_SCOPE) = "NONE" ?> - <Package InstallerVersion="301" Compressed="yes"/> - <?else?> - <Package InstallerVersion="301" Compressed="yes" InstallScope="$(var.CPACK_WIX_INSTALL_SCOPE)"/> - <?endif?> + UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)" + ProductCode="$(var.CPACK_WIX_PRODUCT_GUID)" + Scope="$(var.CPACK_WIX_INSTALL_SCOPE)" + InstallerVersion="500" + Language="1033" + Compressed="yes" + > <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/> @@ -30,7 +30,7 @@ <Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/> <?ifdef CPACK_WIX_PRODUCT_ICON?> - <Property Id="ARPPRODUCTICON">ProductIcon.ico</Property> + <Property Id="ARPPRODUCTICON" Value="ProductIcon.ico" /> <Icon Id="ProductIcon.ico" SourceFile="$(var.CPACK_WIX_PRODUCT_ICON)"/> <?endif?> @@ -44,10 +44,10 @@ <FeatureRef Id="ProductFeature"/> - <UIRef Id="$(var.CPACK_WIX_UI_REF)" /> + <ui:WixUI Id="$(var.CPACK_WIX_UI_REF)" /> <UIRef Id="WixUI_ErrorProgressText" /> <?include "properties.wxi"?> <?include "product_fragment.wxi"?> - </Product> + </Package> </Wix>
diff --git a/Modules/Internal/FeatureTesting.cmake b/Modules/Internal/FeatureTesting.cmake index c2bd14f..64d477b 100644 --- a/Modules/Internal/FeatureTesting.cmake +++ b/Modules/Internal/FeatureTesting.cmake
@@ -52,8 +52,11 @@ if (_result EQUAL 0) if(EXISTS "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.bin") + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.bin" features REGEX "${lang}_FEATURE:.*") + cmake_policy(POP) foreach(info ${features}) string(REPLACE "${lang}_FEATURE:" "" info ${info}) string(SUBSTRING ${info} 0 1 has_feature)
diff --git a/Modules/KDE3Macros.cmake b/Modules/KDE3Macros.cmake index 1c353ba..42f6d2c 100644 --- a/Modules/KDE3Macros.cmake +++ b/Modules/KDE3Macros.cmake
@@ -213,7 +213,10 @@ if (EXISTS ${_abs_FILE} AND NOT _skip) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS ${_abs_FILE} _match REGEX "#include +[^ ]+\\.moc[\">]") + cmake_policy(POP) get_filename_component(_abs_PATH ${_abs_FILE} PATH)
diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index 55a6680..f6616fb 100644 --- a/Modules/Platform/AIX-GNU.cmake +++ b/Modules/Platform/AIX-GNU.cmake
@@ -17,7 +17,7 @@ set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v") set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1) unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY)
diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake index c225de9..cd202cb 100644 --- a/Modules/Platform/AIX-XL.cmake +++ b/Modules/Platform/AIX-XL.cmake
@@ -17,7 +17,7 @@ set(CMAKE_SHARED_MODULE_${lang}_FLAGS " ") set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) set(_OBJECTS " <OBJECTS>") if(DEFINED CMAKE_XL_CreateExportList AND CMAKE_XL_CreateExportList STREQUAL "")
diff --git a/Modules/Platform/AIX/ExportImportList b/Modules/Platform/AIX/ExportImportList index 5e16fcb..7c269fd 100755 --- a/Modules/Platform/AIX/ExportImportList +++ b/Modules/Platform/AIX/ExportImportList
@@ -63,7 +63,7 @@ V["EXPORTED"]=" export" V["PROTECTED"]=" protected" } - /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / { + /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|tdata|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / { if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) { print $NF V[$(NF-1)] }
diff --git a/Modules/Platform/Android-Determine.cmake b/Modules/Platform/Android-Determine.cmake index 307e4c9..342586d 100644 --- a/Modules/Platform/Android-Determine.cmake +++ b/Modules/Platform/Android-Determine.cmake
@@ -32,6 +32,7 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> # If using Android tools for Visual Studio, compile a sample project to get the # NDK path and set the processor from the generator platform. @@ -374,21 +375,21 @@ if(CMAKE_ANDROID_ARCH_ABI AND NOT DEFINED "NDK_ABI_${CMAKE_ANDROID_ARCH_ABI}_PROC") message(FATAL_ERROR "Android: Unknown ABI CMAKE_ANDROID_ARCH_ABI='${CMAKE_ANDROID_ARCH_ABI}'.") endif() -if(CMAKE_SYSTEM_PROCESSOR AND NOT DEFINED "NDK_PROC_${CMAKE_SYSTEM_PROCESSOR}_ABI") - message(FATAL_ERROR "Android: Unknown processor CMAKE_SYSTEM_PROCESSOR='${CMAKE_SYSTEM_PROCESSOR}'.") -endif() -if(_ANDROID_SYSROOT_ARCH AND NOT DEFINED "NDK_ARCH_${_ANDROID_SYSROOT_ARCH}_ABI") - message(FATAL_ERROR - "Android: Unknown architecture '${_ANDROID_SYSROOT_ARCH}' specified in CMAKE_SYSROOT:\n" - " ${CMAKE_SYSROOT}" - ) -endif() # Select an ABI. if(NOT CMAKE_ANDROID_ARCH_ABI) if(CMAKE_SYSTEM_PROCESSOR) + if(NOT DEFINED "NDK_PROC_${CMAKE_SYSTEM_PROCESSOR}_ABI") + message(FATAL_ERROR "Android: Unknown processor CMAKE_SYSTEM_PROCESSOR='${CMAKE_SYSTEM_PROCESSOR}'.") + endif() set(CMAKE_ANDROID_ARCH_ABI "${NDK_PROC_${CMAKE_SYSTEM_PROCESSOR}_ABI}") elseif(_ANDROID_SYSROOT_ARCH) + if(NOT DEFINED "NDK_ARCH_${_ANDROID_SYSROOT_ARCH}_ABI") + message(FATAL_ERROR + "Android: Unknown architecture '${_ANDROID_SYSROOT_ARCH}' specified in CMAKE_SYSROOT:\n" + " ${CMAKE_SYSROOT}" + ) + endif() set(CMAKE_ANDROID_ARCH_ABI "${NDK_ARCH_${_ANDROID_SYSROOT_ARCH}_ABI}") elseif(_INCLUDED_ABIS) # Default to the oldest ARM ABI.
diff --git a/Modules/Platform/Android/Determine-Compiler-NDK.cmake b/Modules/Platform/Android/Determine-Compiler-NDK.cmake index a4d67c4..4e58d69 100644 --- a/Modules/Platform/Android/Determine-Compiler-NDK.cmake +++ b/Modules/Platform/Android/Determine-Compiler-NDK.cmake
@@ -46,6 +46,9 @@ # -target <triple> # -gcc-toolchain <ndk>/toolchains/<triple-or-arch>-<gcc-version> +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + # Glob available toolchains in the NDK, restricted by any version request. if(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION STREQUAL "clang") set(_ANDROID_TOOL_PATTERNS "*-clang" "*-clang[0-9].[0-9]") @@ -269,3 +272,5 @@ unset(_ANDROID_TOOL_CLANG_NAME) unset(_ANDROID_TOOL_CLANG_VERS) unset(_ANDROID_TOOL_LLVM_NAME) + +cmake_policy(POP)
diff --git a/Modules/Platform/Apple-Apple-Swift.cmake b/Modules/Platform/Apple-Apple-Swift.cmake index 237f6e4..9f27efa 100644 --- a/Modules/Platform/Apple-Apple-Swift.cmake +++ b/Modules/Platform/Apple-Apple-Swift.cmake
@@ -7,8 +7,11 @@ set(CMAKE_Swift_USING_LINKER_APPLE_CLASSIC "-fuse-ld=ld" "LINKER:-ld_classic") set(CMAKE_Swift_USING_LINKER_LLD "-fuse-ld=lld") set(CMAKE_Swift_USING_LINKER_SYSTEM "-fuse-ld=ld") + set(CMAKE_SHARED_MODULE_LOADER_Swift_FLAG "-Wl,-bundle_loader,") else() set(CMAKE_Swift_USING_LINKER_APPLE_CLASSIC "-use-ld=ld" "LINKER:-ld_classic") set(CMAKE_Swift_USING_LINKER_LLD "-use-ld=lld") set(CMAKE_Swift_USING_LINKER_SYSTEM "-use-ld=ld") + set(CMAKE_SHARED_MODULE_LOADER_Swift_FLAG "-Xclang-linker -Wl,-bundle_loader,") + set(CMAKE_SHARED_MODULE_CREATE_Swift_FLAGS "-Xlinker -bundle") endif()
diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake index bd5ba9a..40fed44 100644 --- a/Modules/Platform/Apple-Clang.cmake +++ b/Modules/Platform/Apple-Clang.cmake
@@ -15,10 +15,11 @@ set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") endif() - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>") set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) + set(CMAKE_${lang}_LINK_LIBRARY_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) # linker selection set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=ld")
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake index 15f6a71..bc4147e 100644 --- a/Modules/Platform/Apple-GNU.cmake +++ b/Modules/Platform/Apple-GNU.cmake
@@ -17,6 +17,7 @@ set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>") set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) + set(CMAKE_LINK_LIBRARY_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) set(CMAKE_${lang}_USING_LINKER_SYSTEM "") set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "LINKER:-ld_classic")
diff --git a/Modules/Platform/Apple-NAG-Fortran.cmake b/Modules/Platform/Apple-NAG-Fortran.cmake index 8d3e741..828d7ab 100644 --- a/Modules/Platform/Apple-NAG-Fortran.cmake +++ b/Modules/Platform/Apple-NAG-Fortran.cmake
@@ -3,12 +3,17 @@ set(CMAKE_Fortran_VERBOSE_FLAG "-Wl,-v") # Runs gcc under the hood. -# Need -fpp explicitly on case-insensitive filesystem. -set(CMAKE_Fortran_COMPILE_OBJECT - "<CMAKE_Fortran_COMPILER> -fpp -o <OBJECT> <DEFINES> <INCLUDES> <FLAGS> -c <SOURCE>") +# FIXME(#25900): We need -fpp explicitly on case-insensitive filesystems, +# but this does not work with the Ninja generator's separate preprocessing +# and compilation steps. +if(NOT CMAKE_GENERATOR MATCHES "^Ninja") + set(CMAKE_Fortran_COMPILE_OBJECT + "<CMAKE_Fortran_COMPILER> -fpp -o <OBJECT> <DEFINES> <INCLUDES> <FLAGS> -c <SOURCE>") +endif() set(CMAKE_Fortran_OSX_COMPATIBILITY_VERSION_FLAG "-Wl,-compatibility_version -Wl,") set(CMAKE_Fortran_OSX_CURRENT_VERSION_FLAG "-Wl,-current_version -Wl,") +set(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS "-Wl,-bundle") set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS "-Wl,-shared") set(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG "-Wl,-install_name -Wl,") set(CMAKE_Fortran_CREATE_SHARED_LIBRARY
diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake index 070b24d..01b5f91 100644 --- a/Modules/Platform/CYGWIN-GNU.cmake +++ b/Modules/Platform/CYGWIN-GNU.cmake
@@ -40,6 +40,7 @@ "LINKER:--no-whole-archive") endif() set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) macro(__cygwin_compiler_gnu lang)
diff --git a/Modules/Platform/CrayLinuxEnvironment.cmake b/Modules/Platform/CrayLinuxEnvironment.cmake index 648806f..daff647 100644 --- a/Modules/Platform/CrayLinuxEnvironment.cmake +++ b/Modules/Platform/CrayLinuxEnvironment.cmake
@@ -6,7 +6,10 @@ elseif(DEFINED ENV{XTOS_VERSION}) set(CMAKE_SYSTEM_VERSION "$ENV{XTOS_VERSION}") elseif(EXISTS /etc/opt/cray/release/cle-release) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS /etc/opt/cray/release/cle-release release REGEX "^RELEASE=.*") + cmake_policy(POP) string(REGEX REPLACE "^RELEASE=(.*)$" "\\1" CMAKE_SYSTEM_VERSION "${release}") unset(release) elseif(EXISTS /etc/opt/cray/release/clerelease)
diff --git a/Modules/Platform/Darwin-Initialize.cmake b/Modules/Platform/Darwin-Initialize.cmake index 8d5bf8c..1b9cece 100644 --- a/Modules/Platform/Darwin-Initialize.cmake +++ b/Modules/Platform/Darwin-Initialize.cmake
@@ -159,7 +159,10 @@ # Newer SDKs ship text based dylib stub files which contain the architectures supported by the # library in text form. if(EXISTS "${system_lib_tbd_path}") + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${system_lib_tbd_path}" tbd_lines REGEX "^(archs|targets): +\\[.+\\]") + cmake_policy(POP) if(NOT tbd_lines) set(${ret_failed} TRUE PARENT_SCOPE) return()
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index 533b9ce..b26912d 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake
@@ -114,29 +114,37 @@ # Defines LINK_LIBRARY features for frameworks set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) set(CMAKE_LINK_LIBRARY_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_NEEDED_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) set(CMAKE_LINK_LIBRARY_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_REEXPORT_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WEAK_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) # Defines LINK_LIBRARY features for libraries set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library,<LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}") set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_NEEDED_LIBRARY_ATTRIBUTES LIBRARY_TYPE=SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library,<LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}") set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_REEXPORT_LIBRARY_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library,<LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}") set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WEAK_LIBRARY_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) # Defines LINK_LIBRARY feature to Force loading of all members of an archive set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,<LIB_ITEM>") set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) # default to searching for frameworks first if(NOT DEFINED CMAKE_FIND_FRAMEWORK)
diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake index bd5a786..891422e 100644 --- a/Modules/Platform/FreeBSD.cmake +++ b/Modules/Platform/FreeBSD.cmake
@@ -51,6 +51,7 @@ "LINKER:--no-whole-archive") endif() set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/Linux-Apple-Swift.cmake b/Modules/Platform/Linux-Apple-Swift.cmake index 22f0554..248d2de 100644 --- a/Modules/Platform/Linux-Apple-Swift.cmake +++ b/Modules/Platform/Linux-Apple-Swift.cmake
@@ -1,3 +1,5 @@ +set(CMAKE_EXE_EXPORTS_Swift_FLAG "-Xclang-linker -Wl,--export-dynamic") + # Linker Selection # BFD is known to mislink Swift objects resulting in missing type info set(CMAKE_Swift_USING_LINKER_SYSTEM "")
diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake index 97a116f..b9801ee 100644 --- a/Modules/Platform/Linux.cmake +++ b/Modules/Platform/Linux.cmake
@@ -44,6 +44,7 @@ "LINKER:--no-whole-archive") endif() set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) # Features for LINK_GROUP generator expression ## RESCAN: request the linker to rescan static libraries until there is
diff --git a/Modules/Platform/NetBSD.cmake b/Modules/Platform/NetBSD.cmake index ab85923..892d55b 100644 --- a/Modules/Platform/NetBSD.cmake +++ b/Modules/Platform/NetBSD.cmake
@@ -37,6 +37,7 @@ "LINKER:--no-whole-archive") endif() set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/OHOS.cmake b/Modules/Platform/OHOS.cmake new file mode 100644 index 0000000..d0ea1d9 --- /dev/null +++ b/Modules/Platform/OHOS.cmake
@@ -0,0 +1,2 @@ +# Used for cross-compiling to OpenHarmony OS +include(Platform/Linux)
diff --git a/Modules/Platform/SunOS.cmake b/Modules/Platform/SunOS.cmake index b8a302c..25e95e6 100644 --- a/Modules/Platform/SunOS.cmake +++ b/Modules/Platform/SunOS.cmake
@@ -20,6 +20,7 @@ "LINKER:-z,defaultextract") endif() set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake index b9e6394..e3725cb 100644 --- a/Modules/Platform/Windows-Clang.cmake +++ b/Modules/Platform/Windows-Clang.cmake
@@ -55,7 +55,7 @@ set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "") set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD UNICITY=ALL) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) # linker selection set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld-link") @@ -140,6 +140,7 @@ ## WHOLE_ARCHIVE: Force loading all members of an archive set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>") set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) endif() enable_language(RC)
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index 9f81882..1a4159b 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake
@@ -88,6 +88,7 @@ "LINKER:--no-whole-archive") endif() set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) # Features for LINK_GROUP generator expression ## RESCAN: request the linker to rescan static libraries until there is
diff --git a/Modules/Platform/Windows-IntelLLVM.cmake b/Modules/Platform/Windows-IntelLLVM.cmake index b1a336b..517d959 100644 --- a/Modules/Platform/Windows-IntelLLVM.cmake +++ b/Modules/Platform/Windows-IntelLLVM.cmake
@@ -37,6 +37,7 @@ ## WHOLE_ARCHIVE: Force loading all members of an archive set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) endif() set(CMAKE_${lang}_LINK_EXECUTABLE
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index ef57031..5809379 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake
@@ -358,6 +358,7 @@ ## WHOLE_ARCHIVE: Force loading all members of an archive set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "/WHOLEARCHIVE:<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) endif() @@ -513,7 +514,7 @@ set(CMAKE_${lang}_DEPFILE_FORMAT msvc) endif() - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD UNICITY=ALL) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) # linker selection set(CMAKE_${lang}_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}")
diff --git a/Modules/Platform/Windows3x-OpenWatcom.cmake b/Modules/Platform/Windows3x-OpenWatcom.cmake index 6fcceea..0882df6 100644 --- a/Modules/Platform/Windows3x-OpenWatcom.cmake +++ b/Modules/Platform/Windows3x-OpenWatcom.cmake
@@ -8,8 +8,8 @@ if(DEFINED CMAKE_SYSTEM_PROCESSOR AND CMAKE_SYSTEM_PROCESSOR STREQUAL "I86") string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " system windows") - string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " system windows") - string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " system windows") + string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " system windows_dll") + string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " system windows_dll") else() string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " system win386") string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " system win386")
diff --git a/Modules/ProcessorCount.cmake b/Modules/ProcessorCount.cmake index 47e266d..9e1d473 100644 --- a/Modules/ProcessorCount.cmake +++ b/Modules/ProcessorCount.cmake
@@ -207,7 +207,10 @@ # Systems with /proc/cpuinfo: set(cpuinfo_file /proc/cpuinfo) if(EXISTS "${cpuinfo_file}") + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file(STRINGS "${cpuinfo_file}" procs REGEX "^processor.: [0-9]+$") + cmake_policy(POP) list(LENGTH procs count) #message("ProcessorCount: trying cpuinfo '${cpuinfo_file}'") endif()
diff --git a/Modules/TestBigEndian.cmake b/Modules/TestBigEndian.cmake index 12b6816..03c8588 100644 --- a/Modules/TestBigEndian.cmake +++ b/Modules/TestBigEndian.cmake
@@ -91,12 +91,17 @@ if(HAVE_${VARIABLE}) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + file(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" CMAKE_TEST_ENDIANESS_STRINGS_LE LIMIT_COUNT 1 REGEX "THIS IS LITTLE ENDIAN") file(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" CMAKE_TEST_ENDIANESS_STRINGS_BE LIMIT_COUNT 1 REGEX "THIS IS BIG ENDIAN") + cmake_policy(POP) + # on mac, if there are universal binaries built both will be true # return the result depending on the machine on which cmake runs if(CMAKE_TEST_ENDIANESS_STRINGS_BE AND CMAKE_TEST_ENDIANESS_STRINGS_LE)
diff --git a/Modules/UseJava/ClearClassFiles.cmake b/Modules/UseJava/ClearClassFiles.cmake index f3115c6..2c41665 100644 --- a/Modules/UseJava/ClearClassFiles.cmake +++ b/Modules/UseJava/ClearClassFiles.cmake
@@ -3,6 +3,9 @@ # This script deletes compiled Java class files. +cmake_policy(PUSH) +cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> + if(CMAKE_JAVA_CLASS_OUTPUT_PATH) if(EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist") file(STRINGS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist" classes) @@ -15,3 +18,5 @@ else() message(FATAL_ERROR "Can't find CMAKE_JAVA_CLASS_OUTPUT_PATH") endif() + +cmake_policy(POP)
diff --git a/Modules/UseJava/javaTargets.cmake.in b/Modules/UseJava/javaTargets.cmake.in index f3670c2..dc20c82 100644 --- a/Modules/UseJava/javaTargets.cmake.in +++ b/Modules/UseJava/javaTargets.cmake.in
@@ -1,5 +1,5 @@ cmake_policy(PUSH) -cmake_policy(VERSION 2.8.12...3.27) +cmake_policy(VERSION 2.8.12...3.28) #---------------------------------------------------------------- # Generated CMake Java target import file.
diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake index f264fb6..c58511a 100644 --- a/Modules/UseSWIG.cmake +++ b/Modules/UseSWIG.cmake
@@ -466,7 +466,10 @@ # try to get module name from "%module foo" syntax if ( EXISTS "${infile}" ) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file ( STRINGS "${infile}" module_basename REGEX "[ ]*%module[ ]*[a-zA-Z0-9_]+.*" ) + cmake_policy(POP) endif () if ( module_basename ) string ( REGEX REPLACE "[ ]*%module[ ]*([a-zA-Z0-9_]+).*" "\\1" module_basename "${module_basename}" ) @@ -474,7 +477,10 @@ else () # try to get module name from "%module (options=...) foo" syntax if ( EXISTS "${infile}" ) + cmake_policy(PUSH) + cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n> file ( STRINGS "${infile}" module_basename REGEX "[ ]*%module[ ]*\\(.*\\)[ ]*[a-zA-Z0-9_]+.*" ) + cmake_policy(POP) endif () if ( module_basename ) string ( REGEX REPLACE "[ ]*%module[ ]*\\(.*\\)[ ]*([a-zA-Z0-9_]+).*" "\\1" module_basename "${module_basename}" )
diff --git a/Modules/exportheader.cmake.in b/Modules/exportheader.cmake.in index c518b3d..7659958 100644 --- a/Modules/exportheader.cmake.in +++ b/Modules/exportheader.cmake.in
@@ -33,6 +33,7 @@ # define @DEPRECATED_MACRO_NAME@_NO_EXPORT @NO_EXPORT_MACRO_NAME@ @DEPRECATED_MACRO_NAME@ #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if @DEFINE_NO_DEPRECATED@ /* DEFINE_NO_DEPRECATED */ # ifndef @NO_DEPRECATED_MACRO_NAME@ # define @NO_DEPRECATED_MACRO_NAME@
diff --git a/README.rst b/README.rst index 6ada17f..e793d27 100644 --- a/README.rst +++ b/README.rst
@@ -47,6 +47,23 @@ .. _`CMake Discourse Forum`: https://discourse.cmake.org +Building CMake with CMake +------------------------- + +You can build CMake as any other project with a CMake-based build system: +run the installed CMake on the sources of this CMake with your preferred +options and generators. Then build it and install it. +For instructions how to do this, see documentation on `Running CMake`_. + +.. _`Running CMake`: https://cmake.org/runningcmake + +To build the documentation, install `Sphinx`_ and configure CMake with +``-DSPHINX_HTML=ON`` and/or ``-DSPHINX_MAN=ON`` to enable the "html" or +"man" builder. Add ``-DSPHINX_EXECUTABLE=/path/to/sphinx-build`` if the +tool is not found automatically. + +.. _`Sphinx`: https://sphinx-doc.org + Building CMake from Scratch --------------------------- @@ -79,7 +96,7 @@ 1. Compile with MSVC from VS 2015 or later. You need to download and install a binary release of CMake. You can get these releases from the `CMake Download Page`_. Then proceed with the - instructions below for `Building CMake with CMake`_. + instructions above for `Building CMake with CMake`_. 2. Bootstrap with MinGW under MSYS2. Download and install `MSYS2`_. Then install the required build tools:: @@ -91,23 +108,6 @@ .. _`CMake Download Page`: https://cmake.org/download .. _`MSYS2`: https://www.msys2.org/ -Building CMake with CMake -------------------------- - -You can build CMake as any other project with a CMake-based build system: -run the installed CMake on the sources of this CMake with your preferred -options and generators. Then build it and install it. -For instructions how to do this, see documentation on `Running CMake`_. - -.. _`Running CMake`: https://cmake.org/runningcmake - -To build the documentation, install `Sphinx`_ and configure CMake with -``-DSPHINX_HTML=ON`` and/or ``-DSPHINX_MAN=ON`` to enable the "html" or -"man" builder. Add ``-DSPHINX_EXECUTABLE=/path/to/sphinx-build`` if the -tool is not found automatically. - -.. _`Sphinx`: https://sphinx-doc.org - Reporting Bugs ==============
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 8c57762..185ebfe 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt
@@ -285,6 +285,14 @@ cmGeneratorExpression.h cmGeneratorTarget.cxx cmGeneratorTarget.h + cmGeneratorTarget_CompatibleInterface.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 cmGetPipes.cxx @@ -437,6 +445,8 @@ cmTargetPropertyComputer.cxx cmTargetPropertyComputer.h cmTargetExport.h + cmTargetTraceDependencies.cxx + cmTargetTraceDependencies.h cmTest.cxx cmTest.h cmTestGenerator.cxx @@ -547,6 +557,8 @@ cmFLTKWrapUICommand.h cmFileCommand.cxx cmFileCommand.h + cmFileCommand_ReadMacho.cxx + cmFileCommand_ReadMacho.h cmFindBase.cxx cmFindBase.h cmFindCommon.cxx @@ -886,8 +898,6 @@ cmGlobalVisualStudio7Generator.h cmGlobalVisualStudio8Generator.cxx cmGlobalVisualStudio8Generator.h - cmGlobalVisualStudio9Generator.cxx - cmGlobalVisualStudio9Generator.h cmVisualStudioGeneratorOptions.h cmVisualStudioGeneratorOptions.cxx cmVsProjectType.h
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index f36eedb..f76aec4 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) -set(CMake_VERSION_MINOR 29) -set(CMake_VERSION_PATCH 8) +set(CMake_VERSION_MINOR 30) +set(CMake_VERSION_PATCH 5) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.cxx b/Source/CPack/IFW/cmCPackIFWGenerator.cxx index 5724175..7c82668 100644 --- a/Source/CPack/IFW/cmCPackIFWGenerator.cxx +++ b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
@@ -55,7 +55,6 @@ std::vector<std::string> cmCPackIFWGenerator::BuildRepogenCommand() { std::vector<std::string> ifwCmd; - std::string ifwArg; ifwCmd.emplace_back(this->RepoGen); @@ -104,7 +103,7 @@ if (!this->OnlineOnly && !this->DownloadedPackages.empty()) { ifwCmd.emplace_back("-i"); auto it = this->DownloadedPackages.begin(); - ifwArg = (*it)->Name; + std::string ifwArg = (*it)->Name; ++it; while (it != this->DownloadedPackages.end()) { ifwArg += "," + (*it)->Name; @@ -417,10 +416,10 @@ } if (cmValue ifwDownloadAll = this->GetOption("CPACK_IFW_DOWNLOAD_ALL")) { - this->OnlineOnly = cmIsOn(ifwDownloadAll); + this->OnlineOnly = ifwDownloadAll.IsOn(); } else if (cmValue cpackDownloadAll = this->GetOption("CPACK_DOWNLOAD_ALL")) { - this->OnlineOnly = cmIsOn(cpackDownloadAll); + this->OnlineOnly = cpackDownloadAll.IsOn(); } else { this->OnlineOnly = false; } @@ -462,7 +461,7 @@ return this->Superclass::InitializeInternal(); } -std::string cmCPackIFWGenerator::GetComponentInstallDirNameSuffix( +std::string cmCPackIFWGenerator::GetComponentInstallSuffix( const std::string& componentName) { const std::string prefix = "packages/"; @@ -476,6 +475,22 @@ this->GetComponentPackageName(&this->Components[componentName]) + suffix; } +std::string cmCPackIFWGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + const std::string prefix = "packages/"; + const std::string suffix = "/data"; + + if (this->componentPackageMethod == this->ONE_PACKAGE) { + return cmStrCat(prefix, this->GetRootPackageName(), suffix); + } + + return prefix + + this->GetSanitizedDirOrFileName( + this->GetComponentPackageName(&this->Components[componentName])) + + suffix; +} + cmCPackComponent* cmCPackIFWGenerator::GetComponent( const std::string& projectName, const std::string& componentName) {
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.h b/Source/CPack/IFW/cmCPackIFWGenerator.h index 86b4993..e125be0 100644 --- a/Source/CPack/IFW/cmCPackIFWGenerator.h +++ b/Source/CPack/IFW/cmCPackIFWGenerator.h
@@ -67,6 +67,8 @@ */ const char* GetOutputExtension() override; + std::string GetComponentInstallSuffix( + const std::string& componentName) override; std::string GetComponentInstallDirNameSuffix( const std::string& componentName) override;
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index 6918b5e..8532b3e 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -74,7 +74,7 @@ if (!status || returnValue) { cmCPackLogger(cmCPackLog::LOG_ERROR, - "Problem running WiX candle. " + "Problem running WiX. " "Please check '" << logFileName << "' for errors." << std::endl); @@ -166,6 +166,16 @@ bool cmCPackWIXGenerator::InitializeWiXConfiguration() { + if (cmValue wixVersion = GetOption("CPACK_WIX_VERSION")) { + if (!cmStrToULong(*wixVersion, &this->WixVersion) || + this->WixVersion < 3 || this->WixVersion > 4) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_WIX_VERSION has unknown value '" + << *wixVersion << "'" << std::endl); + return false; + } + } + if (!ReadListFile("Internal/CPack/CPackWIX.cmake")) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while executing CPackWIX.cmake" << std::endl); @@ -232,14 +242,22 @@ SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact); } - CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions); - CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions); + if (this->WixVersion >= 4) { + CollectExtensions("CPACK_WIX_EXTENSIONS", this->WixExtensions); + if (!GetOption("CPACK_WIX_SKIP_WIX_UI_EXTENSION").IsOn()) { + this->WixExtensions.insert("WixToolset.UI.wixext"); + } + } else { + CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions); + CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions); - if (!cmIsOn(GetOption("CPACK_WIX_SKIP_WIX_UI_EXTENSION"))) { - this->LightExtensions.insert("WixUIExtension"); + if (!GetOption("CPACK_WIX_SKIP_WIX_UI_EXTENSION").IsOn()) { + this->LightExtensions.insert("WixUIExtension"); + } + CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); + CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); } - CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); - CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); + CollectXmlNamespaces("CPACK_WIX_CUSTOM_XMLNS", this->CustomXmlNamespaces); cmValue patchFilePath = GetOption("CPACK_WIX_PATCH_FILE"); @@ -255,7 +273,7 @@ // if install folder is supposed to be set absolutely, the default // component guid "*" cannot be used - if (cmIsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) { + if (GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER").IsOn()) { this->ComponentGuidType = cmWIXSourceWriter::CMAKE_GENERATED_GUID; } @@ -278,6 +296,53 @@ AppendUserSuppliedExtraSources(); + return this->WixVersion >= 4 ? this->PackageWithWix() + : this->PackageWithWix3(); +} + +bool cmCPackWIXGenerator::PackageWithWix() +{ + std::string wixExecutable; + if (!RequireOption("CPACK_WIX_EXECUTABLE", wixExecutable)) { + return false; + } + + std::string arch; + if (cmValue archOpt = GetOption("CPACK_WIX_ARCHITECTURE")) { + arch = *archOpt; + } else { + arch = GetArchitecture(); + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, + "CPACK_WIX_ARCHITECTURE was not set. Invoking WiX with architecture " + << arch << ". " << std::endl); + } + + std::ostringstream command; + command << QuotePath(wixExecutable) << " build" + << " -arch " << arch << " -out " + << QuotePath(CMakeToWixPath(packageFileNames.at(0))); + + for (std::string const& ext : this->WixExtensions) { + command << " -ext " << QuotePath(ext); + } + + cmList cultures{ GetOption("CPACK_WIX_CULTURES") }; + for (std::string const& culture : cultures) { + command << " -culture \"" << culture << "\""; + } + + AddCustomFlags("CPACK_WIX_BUILD_EXTRA_FLAGS", command); + + for (std::string const& sourceFilename : this->WixSources) { + command << " -src " << QuotePath(CMakeToWixPath(sourceFilename)); + } + + return RunWiXCommand(command.str()); +} + +bool cmCPackWIXGenerator::PackageWithWix3() +{ std::set<std::string> usedBaseNames; std::ostringstream objectFiles; @@ -341,8 +406,8 @@ std::string includeFilename = cmStrCat(this->CPackTopLevel, "/cpack_variables.wxi"); - cmWIXSourceWriter includeFile(this->Logger, includeFilename, - this->ComponentGuidType, + cmWIXSourceWriter includeFile(this->WixVersion, this->Logger, + includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); InjectXmlNamespaces(includeFile); @@ -367,8 +432,8 @@ std::string includeFilename = cmStrCat(this->CPackTopLevel, "/properties.wxi"); - cmWIXSourceWriter includeFile(this->Logger, includeFilename, - this->ComponentGuidType, + cmWIXSourceWriter includeFile(this->WixVersion, this->Logger, + includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); InjectXmlNamespaces(includeFile); @@ -417,8 +482,8 @@ std::string includeFilename = cmStrCat(this->CPackTopLevel, "/product_fragment.wxi"); - cmWIXSourceWriter includeFile(this->Logger, includeFilename, - this->ComponentGuidType, + cmWIXSourceWriter includeFile(this->WixVersion, this->Logger, + includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); InjectXmlNamespaces(includeFile); @@ -459,7 +524,8 @@ this->WixSources.push_back(directoryDefinitionsFilename); cmWIXDirectoriesSourceWriter directoryDefinitions( - this->Logger, directoryDefinitionsFilename, this->ComponentGuidType); + this->WixVersion, this->Logger, directoryDefinitionsFilename, + this->ComponentGuidType); InjectXmlNamespaces(directoryDefinitions); directoryDefinitions.BeginElement("Fragment"); @@ -468,11 +534,13 @@ return false; } - directoryDefinitions.BeginElement("Directory"); - directoryDefinitions.AddAttribute("Id", "TARGETDIR"); - directoryDefinitions.AddAttribute("Name", "SourceDir"); + if (this->WixVersion == 3) { + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", "TARGETDIR"); + directoryDefinitions.AddAttribute("Name", "SourceDir"); + } - size_t installRootSize = + auto installationPrefixDirectory = directoryDefinitions.BeginInstallationPrefixDirectory(GetRootFolderId(), installRoot); @@ -481,7 +549,8 @@ this->WixSources.push_back(fileDefinitionsFilename); - cmWIXFilesSourceWriter fileDefinitions(this->Logger, fileDefinitionsFilename, + cmWIXFilesSourceWriter fileDefinitions(this->WixVersion, this->Logger, + fileDefinitionsFilename, this->ComponentGuidType); InjectXmlNamespaces(fileDefinitions); @@ -492,8 +561,9 @@ this->WixSources.push_back(featureDefinitionsFilename); - cmWIXFeaturesSourceWriter featureDefinitions( - this->Logger, featureDefinitionsFilename, this->ComponentGuidType); + cmWIXFeaturesSourceWriter featureDefinitions(this->WixVersion, this->Logger, + featureDefinitionsFilename, + this->ComponentGuidType); InjectXmlNamespaces(featureDefinitions); featureDefinitions.BeginElement("Fragment"); @@ -501,7 +571,11 @@ featureDefinitions.BeginElement("Feature"); featureDefinitions.AddAttribute("Id", "ProductFeature"); featureDefinitions.AddAttribute("Display", "expand"); - featureDefinitions.AddAttribute("Absent", "disallow"); + if (this->WixVersion >= 4) { + featureDefinitions.AddAttribute("AllowAbsent", "no"); + } else { + featureDefinitions.AddAttribute("Absent", "disallow"); + } featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT"); std::string cpackPackageName; @@ -583,7 +657,8 @@ featureDefinitions.EndElement("Fragment"); fileDefinitions.EndElement("Fragment"); - directoryDefinitions.EndInstallationPrefixDirectory(installRootSize); + directoryDefinitions.EndInstallationPrefixDirectory( + installationPrefixDirectory); if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) != emittedShortcutTypes.end()) { @@ -601,7 +676,9 @@ directoryDefinitions.EmitStartupFolder(); } - directoryDefinitions.EndElement("Directory"); + if (this->WixVersion == 3) { + directoryDefinitions.EndElement("Directory"); + } directoryDefinitions.EndElement("Fragment"); if (!GenerateMainSourceFileFromTemplate()) { @@ -613,15 +690,19 @@ std::string cmCPackWIXGenerator::GetRootFolderId() const { - if (cmIsOn(GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER"))) { - return ""; - } + std::string result; - std::string result = "ProgramFiles<64>Folder"; + if (GetOption("CPACK_WIX_SKIP_PROGRAM_FOLDER").IsOn()) { + return result; + } cmValue rootFolderId = GetOption("CPACK_WIX_ROOT_FOLDER_ID"); if (rootFolderId) { result = *rootFolderId; + } else if (this->WixVersion >= 4) { + result = "ProgramFiles6432Folder"; + } else { + result = "ProgramFiles<64>Folder"; } if (GetArchitecture() == "x86"_s) { @@ -635,9 +716,15 @@ bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate() { - std::string wixTemplate = FindTemplate("WIX.template.in"); + std::string wixTemplate; if (cmValue wixtpl = GetOption("CPACK_WIX_TEMPLATE")) { wixTemplate = *wixtpl; + } else { + cm::optional<cm::string_view> alt; + if (this->WixVersion == 3) { + alt = "WIX-v3/"_s; + } + wixTemplate = FindTemplate("WIX.template.in"_s, alt); } if (wixTemplate.empty()) { @@ -764,21 +851,31 @@ cmWIXFeaturesSourceWriter& featureDefinitions) { std::string directoryId; + std::string directoryRef = "DirectoryRef"; switch (type) { case cmWIXShortcuts::START_MENU: { cmValue cpackWixProgramMenuFolder = GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER"); if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == "."_s) { directoryId = "ProgramMenuFolder"; + if (this->WixVersion >= 4) { + directoryRef = "StandardDirectory"; + } } else { directoryId = "PROGRAM_MENU_FOLDER"; } } break; case cmWIXShortcuts::DESKTOP: directoryId = "DesktopFolder"; + if (this->WixVersion >= 4) { + directoryRef = "StandardDirectory"; + } break; case cmWIXShortcuts::STARTUP: directoryId = "StartupFolder"; + if (this->WixVersion >= 4) { + directoryRef = "StandardDirectory"; + } break; default: return false; @@ -810,7 +907,7 @@ componentId += idSuffix; - fileDefinitions.BeginElement("DirectoryRef"); + fileDefinitions.BeginElement(directoryRef); fileDefinitions.AddAttribute("Id", directoryId); fileDefinitions.BeginElement("Component"); @@ -840,7 +937,7 @@ } fileDefinitions.EndElement("Component"); - fileDefinitions.EndElement("DirectoryRef"); + fileDefinitions.EndElement(directoryRef); featureDefinitions.EmitComponentRef(componentId); featureDefinitions.EndElement("FeatureRef"); @@ -1180,12 +1277,7 @@ void cmCPackWIXGenerator::CollectXmlNamespaces(std::string const& variableName, xmlns_map_t& namespaces) { - cmValue variableContent = GetOption(variableName); - if (!variableContent) { - return; - } - - cmList list{ variableContent }; + cmList list{ GetOption(variableName) }; for (std::string const& str : list) { auto pos = str.find('='); if (pos != std::string::npos) { @@ -1199,12 +1291,18 @@ << str << '"' << std::endl); } } - std::ostringstream oss; - for (auto& ns : namespaces) { - oss << " xmlns:" << ns.first << "=\"" - << cmWIXSourceWriter::EscapeAttributeValue(ns.second) << '"'; + std::string xmlns; + if (this->WixVersion >= 4 && + cm::contains(this->WixExtensions, "WixToolset.UI.wixext") && + !cm::contains(namespaces, "ui")) { + xmlns = cmStrCat( + xmlns, "\n xmlns:ui=\"http://wixtoolset.org/schemas/v4/wxs/ui\""); } - SetOption("CPACK_WIX_CUSTOM_XMLNS_EXPANDED", oss.str()); + for (auto& ns : namespaces) { + xmlns = cmStrCat(xmlns, "\n xmlns:", ns.first, "=\"", + cmWIXSourceWriter::EscapeAttributeValue(ns.second), '"'); + } + SetOption("CPACK_WIX_CUSTOM_XMLNS_EXPANDED", xmlns); } void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName,
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h index 8609cf3..63530d4 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.h +++ b/Source/CPack/WiX/cmCPackWIXGenerator.h
@@ -59,6 +59,8 @@ bool InitializeWiXConfiguration(); bool PackageFilesImpl(); + bool PackageWithWix(); + bool PackageWithWix3(); void CreateWiXVariablesIncludeFile(); @@ -160,6 +162,7 @@ id_map_t PathToIdMap; ambiguity_map_t IdAmbiguityCounter; + extension_set_t WixExtensions; extension_set_t CandleExtensions; extension_set_t LightExtensions; xmlns_map_t CustomXmlNamespaces; @@ -168,5 +171,7 @@ std::unique_ptr<cmWIXPatch> Patch; + unsigned long WixVersion = 3; + cmWIXSourceWriter::GuidType ComponentGuidType; };
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx index a655d86..b1e7bd2 100644 --- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
@@ -5,15 +5,16 @@ #include <cmext/string_view> cmWIXDirectoriesSourceWriter::cmWIXDirectoriesSourceWriter( - cmCPackLog* logger, std::string const& filename, GuidType componentGuidType) - : cmWIXSourceWriter(logger, filename, componentGuidType) + unsigned long wixVersion, cmCPackLog* logger, std::string const& filename, + GuidType componentGuidType) + : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType) { } void cmWIXDirectoriesSourceWriter::EmitStartMenuFolder( std::string const& startMenuFolder) { - BeginElement("Directory"); + BeginElement_StandardDirectory(); AddAttribute("Id", "ProgramMenuFolder"); if (startMenuFolder != "."_s) { @@ -23,34 +24,39 @@ EndElement("Directory"); } - EndElement("Directory"); + EndElement_StandardDirectory(); } void cmWIXDirectoriesSourceWriter::EmitDesktopFolder() { - BeginElement("Directory"); + BeginElement_StandardDirectory(); AddAttribute("Id", "DesktopFolder"); - AddAttribute("Name", "Desktop"); - EndElement("Directory"); + if (this->WixVersion == 3) { + AddAttribute("Name", "Desktop"); + } + EndElement_StandardDirectory(); } void cmWIXDirectoriesSourceWriter::EmitStartupFolder() { - BeginElement("Directory"); + BeginElement_StandardDirectory(); AddAttribute("Id", "StartupFolder"); - AddAttribute("Name", "Startup"); - EndElement("Directory"); + if (this->WixVersion == 3) { + AddAttribute("Name", "Startup"); + } + EndElement_StandardDirectory(); } -size_t cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( +cmWIXDirectoriesSourceWriter::InstallationPrefixDirectory +cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( std::string const& programFilesFolderId, std::string const& installRootString) { - size_t offset = 1; + InstallationPrefixDirectory installationPrefixDirectory; if (!programFilesFolderId.empty()) { - BeginElement("Directory"); + installationPrefixDirectory.HasStandardDirectory = true; + this->BeginElement_StandardDirectory(); AddAttribute("Id", programFilesFolderId); - offset = 0; } std::vector<std::string> installRoot; @@ -62,6 +68,7 @@ } for (size_t i = 1; i < installRoot.size(); ++i) { + ++installationPrefixDirectory.Depth; BeginElement("Directory"); if (i == installRoot.size() - 1) { @@ -75,12 +82,16 @@ AddAttribute("Name", installRoot[i]); } - return installRoot.size() - offset; + return installationPrefixDirectory; } -void cmWIXDirectoriesSourceWriter::EndInstallationPrefixDirectory(size_t size) +void cmWIXDirectoriesSourceWriter::EndInstallationPrefixDirectory( + InstallationPrefixDirectory installationPrefixDirectory) { - for (size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < installationPrefixDirectory.Depth; ++i) { EndElement("Directory"); } + if (installationPrefixDirectory.HasStandardDirectory) { + this->EndElement_StandardDirectory(); + } }
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h index 0af3094..b0aa1e2 100644 --- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h
@@ -13,7 +13,8 @@ class cmWIXDirectoriesSourceWriter : public cmWIXSourceWriter { public: - cmWIXDirectoriesSourceWriter(cmCPackLog* logger, std::string const& filename, + cmWIXDirectoriesSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType); void EmitStartMenuFolder(std::string const& startMenuFolder); @@ -22,9 +23,16 @@ void EmitStartupFolder(); - size_t BeginInstallationPrefixDirectory( + struct InstallationPrefixDirectory + { + bool HasStandardDirectory = false; + size_t Depth = 0; + }; + + InstallationPrefixDirectory BeginInstallationPrefixDirectory( std::string const& programFilesFolderId, std::string const& installRootString); - void EndInstallationPrefixDirectory(size_t size); + void EndInstallationPrefixDirectory( + InstallationPrefixDirectory installationPrefixDirectory); };
diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx index 78c2208..0d2e6e8 100644 --- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx
@@ -5,8 +5,9 @@ #include "cmStringAlgorithms.h" cmWIXFeaturesSourceWriter::cmWIXFeaturesSourceWriter( - cmCPackLog* logger, std::string const& filename, GuidType componentGuidType) - : cmWIXSourceWriter(logger, filename, componentGuidType) + unsigned long wixVersion, cmCPackLog* logger, std::string const& filename, + GuidType componentGuidType) + : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType) { } @@ -69,7 +70,11 @@ AddAttributeUnlessEmpty("Description", component.Description); if (component.IsRequired) { - AddAttribute("Absent", "disallow"); + if (this->WixVersion >= 4) { + AddAttribute("AllowAbsent", "no"); + } else { + AddAttribute("Absent", "disallow"); + } } if (component.IsHidden) {
diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h index 0facf97..95de8fe 100644 --- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h
@@ -12,7 +12,8 @@ class cmWIXFeaturesSourceWriter : public cmWIXSourceWriter { public: - cmWIXFeaturesSourceWriter(cmCPackLog* logger, std::string const& filename, + cmWIXFeaturesSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType); void CreateCMakePackageRegistryEntry(std::string const& package,
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx index b4085d5..87ac6ac 100644 --- a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx
@@ -15,10 +15,11 @@ #include "cmUuid.h" #include "cmWIXAccessControlList.h" -cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(cmCPackLog* logger, +cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(unsigned long wixVersion, + cmCPackLog* logger, std::string const& filename, GuidType componentGuidType) - : cmWIXSourceWriter(logger, filename, componentGuidType) + : cmWIXSourceWriter(wixVersion, logger, filename, componentGuidType) { }
diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.h b/Source/CPack/WiX/cmWIXFilesSourceWriter.h index 60dddd4..f560304 100644 --- a/Source/CPack/WiX/cmWIXFilesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.h
@@ -13,7 +13,8 @@ class cmWIXFilesSourceWriter : public cmWIXSourceWriter { public: - cmWIXFilesSourceWriter(cmCPackLog* logger, std::string const& filename, + cmWIXFilesSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType); void EmitShortcut(std::string const& id, cmWIXShortcut const& shortcut,
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx index ef6712a..33e8a70 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.cxx +++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx
@@ -8,11 +8,13 @@ #include "cmCryptoHash.h" #include "cmUuid.h" -cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger, +cmWIXSourceWriter::cmWIXSourceWriter(unsigned long wixVersion, + cmCPackLog* logger, std::string const& filename, GuidType componentGuidType, RootElementType rootElementType) - : Logger(logger) + : WixVersion(wixVersion) + , Logger(logger) , File(filename.c_str()) , State(DEFAULT) , SourceFilename(filename) @@ -26,7 +28,11 @@ BeginElement("Wix"); } - AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); + if (this->WixVersion >= 4) { + AddAttribute("xmlns", "http://wixtoolset.org/schemas/v4/wxs"); + } else { + AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); + } } cmWIXSourceWriter::~cmWIXSourceWriter() @@ -42,6 +48,24 @@ EndElement(Elements.back()); } +void cmWIXSourceWriter::BeginElement_StandardDirectory() +{ + if (this->WixVersion >= 4) { + BeginElement("StandardDirectory"); + } else { + BeginElement("Directory"); + } +} + +void cmWIXSourceWriter::EndElement_StandardDirectory() +{ + if (this->WixVersion >= 4) { + EndElement("StandardDirectory"); + } else { + EndElement("Directory"); + } +} + void cmWIXSourceWriter::BeginElement(std::string const& name) { if (State == BEGIN) {
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h index f643acd..1089cf5 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.h +++ b/Source/CPack/WiX/cmWIXSourceWriter.h
@@ -27,12 +27,15 @@ INCLUDE_ELEMENT_ROOT }; - cmWIXSourceWriter(cmCPackLog* logger, std::string const& filename, - GuidType componentGuidType, + cmWIXSourceWriter(unsigned long wixVersion, cmCPackLog* logger, + std::string const& filename, GuidType componentGuidType, RootElementType rootElementType = WIX_ELEMENT_ROOT); ~cmWIXSourceWriter(); + void BeginElement_StandardDirectory(); + void EndElement_StandardDirectory(); + void BeginElement(std::string const& name); void EndElement(std::string const& name); @@ -52,6 +55,7 @@ static std::string EscapeAttributeValue(std::string const& value); protected: + unsigned long WixVersion; cmCPackLog* Logger; private:
diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx index b7b6785..8bd3aa0 100644 --- a/Source/CPack/cmCPackArchiveGenerator.cxx +++ b/Source/CPack/cmCPackArchiveGenerator.cxx
@@ -234,7 +234,7 @@ " - packaging component: " << component->Name << std::endl); // Add the files of this component to the archive std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); - localToplevel += "/" + component->Name; + localToplevel += "/" + this->GetSanitizedDirOrFileName(component->Name); // Change to local toplevel cmWorkingDirectory workdir(localToplevel); if (workdir.Failed()) { @@ -357,11 +357,7 @@ << comp.second.Name << "> does not belong to any group, package it separately." << std::endl); - std::string localToplevel( - this->GetOption("CPACK_TEMPORARY_DIRECTORY")); std::string packageFileName = std::string(this->toplevel); - - localToplevel += "/" + comp.first; packageFileName += "/" + this->GetArchiveComponentFileName(comp.first, false); @@ -379,10 +375,7 @@ // We build 1 package per component else { for (auto& comp : this->Components) { - std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); std::string packageFileName = std::string(this->toplevel); - - localToplevel += "/" + comp.first; packageFileName += "/" + this->GetArchiveComponentFileName(comp.first, false);
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 8d16428..31191a9 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -225,7 +225,7 @@ // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application // should not add XXX/application orderedFiles.insert(currentPath); - currentPath = cmSystemTools::CollapseFullPath("..", currentPath); + currentPath = cmSystemTools::GetFilenamePath(currentPath); } } @@ -527,7 +527,7 @@ int cmCPackDebGenerator::InitializeInternal() { this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); - if (cmIsOff(this->GetOption("CPACK_SET_DESTDIR"))) { + if (this->GetOption("CPACK_SET_DESTDIR").IsOff()) { this->SetOption("CPACK_SET_DESTDIR", "I_ON"); } return this->Superclass::InitializeInternal(); @@ -536,6 +536,11 @@ int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, std::string const& packageName) { + // Determine the sanitized packaging directory-name that can be used on the + // file-system. + std::string sanitizedPkgDirName = + this->GetSanitizedDirOrFileName(packageName); + // Begin the archive for this pack std::string localToplevel(initialTopLevel); std::string packageFileName( @@ -543,7 +548,7 @@ std::string outputFileName(*this->GetOption("CPACK_PACKAGE_FILE_NAME") + "-" + packageName + this->GetOutputExtension()); - localToplevel += "/" + packageName; + localToplevel += "/" + sanitizedPkgDirName; /* replace the TEMP DIRECTORY with the component one */ this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel); packageFileName += "/" + outputFileName; @@ -554,7 +559,7 @@ // Tell CPackDeb.cmake the name of the component GROUP. this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName); // Tell CPackDeb.cmake the path where the component is. - std::string component_path = cmStrCat('/', packageName); + std::string component_path = cmStrCat('/', sanitizedPkgDirName); this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", component_path); if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) { cmCPackLogger(cmCPackLog::LOG_ERROR, @@ -894,7 +899,7 @@ return this->IsOn("CPACK_DEB_COMPONENT_INSTALL"); } -std::string cmCPackDebGenerator::GetComponentInstallDirNameSuffix( +std::string cmCPackDebGenerator::GetComponentInstallSuffix( const std::string& componentName) { if (this->componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) { @@ -913,3 +918,10 @@ } return componentName; } + +std::string cmCPackDebGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + return this->GetSanitizedDirOrFileName( + this->GetComponentInstallSuffix(componentName)); +}
diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h index 11561a4..2a8c194 100644 --- a/Source/CPack/cmCPackDebGenerator.h +++ b/Source/CPack/cmCPackDebGenerator.h
@@ -59,6 +59,8 @@ int PackageFiles() override; const char* GetOutputExtension() override { return ".deb"; } bool SupportsComponentInstallation() const override; + std::string GetComponentInstallSuffix( + const std::string& componentName) override; std::string GetComponentInstallDirNameSuffix( const std::string& componentName) override;
diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx index b4cada3..4c518ac 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.cxx +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx
@@ -182,13 +182,14 @@ } // component install - std::vector<std::string> package_files; + std::vector<std::pair<std::string, std::string>> package_files; std::map<std::string, cmCPackComponent>::iterator compIt; for (compIt = this->Components.begin(); compIt != this->Components.end(); ++compIt) { - std::string name = GetComponentInstallDirNameSuffix(compIt->first); - package_files.push_back(name); + std::string dirName = GetComponentInstallDirNameSuffix(compIt->first); + std::string fileName = GetComponentInstallSuffix(compIt->first); + package_files.emplace_back(fileName, dirName); } std::sort(package_files.begin(), package_files.end()); package_files.erase(std::unique(package_files.begin(), package_files.end()), @@ -198,15 +199,15 @@ packageFileNames.clear(); for (auto const& package_file : package_files) { std::string full_package_name = cmStrCat(toplevel, '/'); - if (package_file == "ALL_IN_ONE"_s) { + if (package_file.first == "ALL_IN_ONE"_s) { full_package_name += this->GetOption("CPACK_PACKAGE_FILE_NAME"); } else { - full_package_name += package_file; + full_package_name += package_file.first; } full_package_name += GetOutputExtension(); packageFileNames.push_back(full_package_name); - std::string src_dir = cmStrCat(toplevel, '/', package_file); + std::string src_dir = cmStrCat(toplevel, '/', package_file.second); if (0 == this->CreateDMG(src_dir, full_package_name)) { return 0; @@ -707,7 +708,7 @@ return true; } -std::string cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix( +std::string cmCPackDragNDropGenerator::GetComponentInstallSuffix( const std::string& componentName) { // we want to group components together that go in the same dmg package @@ -747,6 +748,13 @@ return GetComponentPackageFileName(package_file_name, componentName, false); } +std::string cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + return this->GetSanitizedDirOrFileName( + this->GetComponentInstallSuffix(componentName)); +} + void cmCPackDragNDropGenerator::WriteRezXML(std::string const& file, RezDoc const& rez) {
diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h index 6089ae5..249f73e 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.h +++ b/Source/CPack/cmCPackDragNDropGenerator.h
@@ -35,6 +35,8 @@ bool CreateEmptyFile(std::ostringstream& target, size_t size); bool RunCommand(std::string const& command, std::string* output = nullptr); + std::string GetComponentInstallSuffix( + const std::string& componentName) override; std::string GetComponentInstallDirNameSuffix( const std::string& componentName) override;
diff --git a/Source/CPack/cmCPackExternalGenerator.cxx b/Source/CPack/cmCPackExternalGenerator.cxx index 8ba015c..76cb876 100644 --- a/Source/CPack/cmCPackExternalGenerator.cxx +++ b/Source/CPack/cmCPackExternalGenerator.cxx
@@ -15,6 +15,7 @@ #include "cmCPackComponentGroup.h" #include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" #include "cmList.h" #include "cmMakefile.h" #include "cmSystemTools.h" @@ -48,17 +49,19 @@ filename = this->packageFileNames[0]; } - cmsys::ofstream fout(filename.c_str()); - std::unique_ptr<Json::StreamWriter> jout(builder.newStreamWriter()); + { + cmGeneratedFileStream fout(filename); + std::unique_ptr<Json::StreamWriter> jout(builder.newStreamWriter()); - Json::Value root(Json::objectValue); + Json::Value root(Json::objectValue); - if (!this->Generator->WriteToJSON(root)) { - return 0; - } + if (!this->Generator->WriteToJSON(root)) { + return 0; + } - if (jout->write(root, &fout)) { - return 0; + if (jout->write(root, &fout)) { + return 0; + } } cmValue packageScript = this->GetOption("CPACK_EXTERNAL_PACKAGE_SCRIPT"); @@ -156,7 +159,7 @@ bool cmCPackExternalGenerator::StagingEnabled() const { - return !cmIsOff(this->GetOption("CPACK_EXTERNAL_ENABLE_STAGING")); + return !this->GetOption("CPACK_EXTERNAL_ENABLE_STAGING").IsOff(); } cmCPackExternalGenerator::cmCPackExternalVersionGenerator:: @@ -221,7 +224,7 @@ root["setDestdir"] = false; } - root["stripFiles"] = !cmIsOff(this->Parent->GetOption("CPACK_STRIP_FILES")); + root["stripFiles"] = !this->Parent->GetOption("CPACK_STRIP_FILES").IsOff(); root["warnOnAbsoluteInstallDestination"] = this->Parent->IsOn("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION"); root["errorOnAbsoluteInstallDestination"] =
diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx index 0840e33..6beb644 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.cxx +++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx
@@ -405,8 +405,7 @@ return 0; } - const std::string output_dir = - cmSystemTools::CollapseFullPath("../", toplevel); + const std::string output_dir = cmSystemTools::GetFilenamePath(toplevel); PkgCreate package(output_dir, toplevel, manifestname); if (package.isValid()) { if (!package.Create()) {
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index 11d90c0..b025570 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx
@@ -7,6 +7,8 @@ #include <memory> #include <utility> +#include <cmext/string_view> + #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" #include "cmsys/RegularExpression.hxx" @@ -77,51 +79,60 @@ } } - std::string tempDirectory = - cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/_CPack_Packages/"); - cmValue toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG"); - if (toplevelTag) { - tempDirectory += *toplevelTag; - tempDirectory += "/"; + // Determine package-directory. + cmValue pkgDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY"); + if (!pkgDirectory) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_DIRECTORY not specified" << std::endl); + return 0; } - tempDirectory += *this->GetOption("CPACK_GENERATOR"); - std::string topDirectory = tempDirectory; - cmValue pfname = this->GetOption("CPACK_PACKAGE_FILE_NAME"); - if (!pfname) { + // Determine base-filename of the package. + cmValue pkgBaseFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + if (!pkgBaseFileName) { cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_PACKAGE_FILE_NAME not specified" << std::endl); return 0; } - std::string outName = *pfname; - tempDirectory += "/" + outName; + // Determine filename of the package. if (!this->GetOutputExtension()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "No output extension specified" << std::endl); return 0; } - outName += this->GetOutputExtension(); - cmValue pdir = this->GetOption("CPACK_PACKAGE_DIRECTORY"); - if (!pdir) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "CPACK_PACKAGE_DIRECTORY not specified" << std::endl); - return 0; + std::string pkgFileName = + cmStrCat(pkgBaseFileName, this->GetOutputExtension()); + // Determine path to the package. + std::string pkgFilePath = cmStrCat(pkgDirectory, "/", pkgFileName); + // Determine top-level directory for packaging. + std::string topDirectory = cmStrCat(pkgDirectory, "/_CPack_Packages/"); + { + cmValue toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG"); + if (toplevelTag) { + topDirectory += cmStrCat(toplevelTag, "/"); + } } + topDirectory += *this->GetOption("CPACK_GENERATOR"); + // Determine temporary packaging-directory. + std::string tmpDirectory = cmStrCat(topDirectory, "/", pkgBaseFileName); + // Determine path to temporary package file. + std::string tmpPkgFilePath = topDirectory + "/" + pkgFileName; - std::string destFile = *pdir; - this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", destFile); - destFile += "/" + outName; - std::string outFile = topDirectory + "/" + outName; + // Set CPack variables which are not set already. + this->SetOptionIfNotSet("CPACK_REMOVE_TOPLEVEL_DIRECTORY", "1"); this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory); - this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory); - this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName); - this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", destFile); - this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", outFile); + this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tmpDirectory); + this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", tmpDirectory); + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", pkgDirectory); + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", pkgFileName); + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", pkgFilePath); + this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", tmpPkgFilePath); this->SetOptionIfNotSet("CPACK_INSTALL_DIRECTORY", this->GetInstallPath()); this->SetOptionIfNotSet( "CPACK_NATIVE_INSTALL_DIRECTORY", cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath())); - this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", tempDirectory); + // Determine description of the package and set as CPack variable, + // if not already set. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl); cmValue descFileName = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE"); @@ -141,14 +152,14 @@ << std::endl); return 0; } - std::ostringstream ostr; - std::string line; - cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Read description file: " << *descFileName << std::endl); + std::ostringstream ostr; + std::string line; while (ifs && cmSystemTools::GetLineFromStream(ifs, line)) { ostr << cmXMLSafe(line) << std::endl; } + this->SetOption("CPACK_PACKAGE_DESCRIPTION", ostr.str()); cmValue defFileName = this->GetOption("CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE"); @@ -164,6 +175,8 @@ << std::endl); return 0; } + + // Check algorithm for calculating the checksum of the package. cmValue algoSignature = this->GetOption("CPACK_PACKAGE_CHECKSUM"); if (algoSignature) { if (!cmCryptoHash::New(*algoSignature)) { @@ -174,8 +187,6 @@ } } - this->SetOptionIfNotSet("CPACK_REMOVE_TOPLEVEL_DIRECTORY", "1"); - return 1; } @@ -185,21 +196,21 @@ this->CleanTemporaryDirectory(); std::string bareTempInstallDirectory = - this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); - std::string tempInstallDirectoryStr = bareTempInstallDirectory; - bool setDestDir = cmIsOn(this->GetOption("CPACK_SET_DESTDIR")) || + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + std::string tempInstallDirectory = bareTempInstallDirectory; + bool setDestDir = this->GetOption("CPACK_SET_DESTDIR").IsOn() || cmIsInternallyOn(this->GetOption("CPACK_SET_DESTDIR")); if (!setDestDir) { - tempInstallDirectoryStr += this->GetPackagingInstallPrefix(); + tempInstallDirectory += this->GetPackagingInstallPrefix(); } - const char* tempInstallDirectory = tempInstallDirectoryStr.c_str(); int res = 1; if (!cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Problem creating temporary directory: " - << (tempInstallDirectory ? tempInstallDirectory : "(NULL)") - << std::endl); + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << (!tempInstallDirectory.empty() ? tempInstallDirectory : "(NULL)") + << std::endl); return 0; } @@ -360,7 +371,7 @@ cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); cmsys::Glob gl; std::string top = *it; - it++; + ++it; std::string subdir = *it; std::string findExpr = cmStrCat(top, "/*"); cmCPackLogger(cmCPackLog::LOG_OUTPUT, @@ -750,6 +761,7 @@ // instead of the default // one install directory for each component. tempInstallDirectory += this->GetComponentInstallDirNameSuffix(component); + if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) { tempInstallDirectory += "/"; tempInstallDirectory += *this->GetOption("CPACK_PACKAGE_FILE_NAME"); @@ -855,7 +867,7 @@ // strip on TRUE, ON, 1, one or several file names, but not on // FALSE, OFF, 0 and an empty string - if (!cmIsOff(this->GetOption("CPACK_STRIP_FILES"))) { + if (!this->GetOption("CPACK_STRIP_FILES").IsOff()) { mf.AddDefinition("CMAKE_INSTALL_DO_STRIP", "1"); } // Remember the list of files before installation @@ -879,7 +891,7 @@ if (this->IsOn("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")) { mf.AddDefinition("CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION", "1"); } - // If current CPack generator does support + // If current CPack generator does not support // ABSOLUTE INSTALL DESTINATION or CPack has been asked for // then ask cmake_install.cmake script to error out // as soon as it occurs (before installing file) @@ -954,7 +966,7 @@ if (componentInstall) { std::string absoluteDestFileComponent = std::string("CPACK_ABSOLUTE_DESTINATION_FILES") + "_" + - this->GetComponentInstallDirNameSuffix(component); + this->GetComponentInstallSuffix(component); if (nullptr != this->GetOption(absoluteDestFileComponent)) { std::string absoluteDestFilesListComponent = cmStrCat(this->GetOption(absoluteDestFileComponent), ';', *d); @@ -972,6 +984,48 @@ return 1; } +bool cmCPackGenerator::GenerateChecksumFile(cmCryptoHash& crypto, + cm::string_view filename) const +{ + std::string packageFileName = + cmStrCat(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"), "/", filename); + std::string hashFile = cmStrCat( + packageFileName, "." + cmSystemTools::LowerCase(crypto.GetHashAlgoName())); + cmsys::ofstream outF(hashFile.c_str()); + if (!outF) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot create checksum file: " << hashFile << std::endl); + return false; + } + outF << crypto.HashFile(packageFileName) << " " << filename << "\n"; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- checksum file: " << hashFile << " generated." << std::endl); + return true; +} + +bool cmCPackGenerator::CopyPackageFile(const std::string& srcFilePath, + cm::string_view filename) const +{ + std::string destFilePath = + cmStrCat(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"), "/", filename); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Copy final package(s): " + << (!srcFilePath.empty() ? srcFilePath : "(NULL)") << " to " + << (!destFilePath.empty() ? destFilePath : "(NULL)") + << std::endl); + if (!cmSystemTools::CopyFileIfDifferent(srcFilePath, destFilePath)) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Problem copying the package: " + << (!srcFilePath.empty() ? srcFilePath : "(NULL)") << " to " + << (!destFilePath.empty() ? destFilePath : "(NULL)") << std::endl); + return false; + } + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- package: " << destFilePath << " generated." << std::endl); + return true; +} + bool cmCPackGenerator::ReadListFile(const char* moduleName) { bool retval; @@ -1041,7 +1095,8 @@ return 0; } - if (cmIsOn(this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY"))) { + // Possibly remove the top-level packaging-directory. + if (this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY").IsOn()) { cmValue toplevelDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); if (toplevelDirectory && cmSystemTools::FileExists(*toplevelDirectory)) { cmCPackLogger(cmCPackLog::LOG_VERBOSE, @@ -1055,55 +1110,68 @@ } } } + + // Install the project (to the temporary install-directory). cmCPackLogger(cmCPackLog::LOG_DEBUG, "About to install project " << std::endl); - if (!this->InstallProject()) { return 0; } cmCPackLogger(cmCPackLog::LOG_DEBUG, "Done install project " << std::endl); - cmValue tempPackageFileName = - this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME"); + // Determine the temporary directory whose content shall be packaged. cmValue tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + // Determine and store internally the list of files to be installed. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); - cmsys::Glob gl; - std::string findExpr = cmStrCat(tempDirectory, "/*"); - gl.RecurseOn(); - gl.SetRecurseListDirs(true); - gl.SetRecurseThroughSymlinks(false); - if (!gl.FindFiles(findExpr)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Cannot find any files in the packaging tree" << std::endl); - return 0; + { + cmsys::Glob gl; + std::string findExpr = cmStrCat(tempDirectory, "/*"); + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + gl.SetRecurseThroughSymlinks(false); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the packaging tree" + << std::endl); + return 0; + } + this->files = gl.GetFiles(); } - cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl); - cmCPackLogger(cmCPackLog::LOG_VERBOSE, - "Package files to: " - << (tempPackageFileName ? *tempPackageFileName : "(NULL)") - << std::endl); - if (tempPackageFileName && cmSystemTools::FileExists(*tempPackageFileName)) { - cmCPackLogger(cmCPackLog::LOG_VERBOSE, - "Remove old package file" << std::endl); - cmSystemTools::RemoveFile(*tempPackageFileName); - } - if (cmIsOn(this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) { + // Determine and store internally the directory that shall be packaged. + if (this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) { tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); } - - // The files to be installed - this->files = gl.GetFiles(); - - this->packageFileNames.clear(); - /* Put at least one file name into the list of - * wanted packageFileNames. The specific generator - * may update this during PackageFiles. - * (either putting several names or updating the provided one) - */ - this->packageFileNames.emplace_back(tempPackageFileName); this->toplevel = *tempDirectory; + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl); + + // Determine and store internally the list of packages to create. + // Note: Initially, this only contains a single package. + { + cmValue tempPackageFileName = + this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME"); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Package files to: " + << (tempPackageFileName ? *tempPackageFileName : "(NULL)") + << std::endl); + if (tempPackageFileName && + cmSystemTools::FileExists(*tempPackageFileName)) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Remove old package file" << std::endl); + cmSystemTools::RemoveFile(*tempPackageFileName); + } + this->packageFileNames.clear(); + /* Put at least one file name into the list of + * wanted packageFileNames. The specific generator + * may update this during PackageFiles. + * (either putting several names or updating the provided one) + */ + this->packageFileNames.emplace_back(tempPackageFileName); + } + + // Do package the files (using the derived CPack generators. { // scope that enables package generators to run internal scripts with // latest CMake policies enabled cmMakefile::ScopePushPop pp{ this->MakefileMap }; @@ -1116,6 +1184,7 @@ return 0; } } + // Run post-build actions cmValue postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS"); if (postBuildScripts) { @@ -1151,44 +1220,15 @@ << "]:" << std::endl); /* now copy package one by one */ for (std::string const& pkgFileName : this->packageFileNames) { - std::string tmpPF(this->GetOption("CPACK_OUTPUT_FILE_PREFIX")); std::string filename(cmSystemTools::GetFilenameName(pkgFileName)); - tempPackageFileName = cmValue(pkgFileName); - tmpPF += "/" + filename; - const char* packageFileName = tmpPF.c_str(); - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "Copy final package(s): " - << (tempPackageFileName ? *tempPackageFileName : "(NULL)") - << " to " << (packageFileName ? packageFileName : "(NULL)") - << std::endl); - if (!cmSystemTools::CopyFileIfDifferent(pkgFileName, tmpPF)) { - cmCPackLogger( - cmCPackLog::LOG_ERROR, - "Problem copying the package: " - << (tempPackageFileName ? *tempPackageFileName : "(NULL)") << " to " - << (packageFileName ? packageFileName : "(NULL)") << std::endl); + if (!this->CopyPackageFile(pkgFileName, filename)) { return 0; } - cmCPackLogger(cmCPackLog::LOG_OUTPUT, - "- package: " << packageFileName << " generated." - << std::endl); - /* Generate checksum file */ if (crypto) { - std::string hashFile(this->GetOption("CPACK_OUTPUT_FILE_PREFIX")); - hashFile += "/" + filename; - hashFile += "." + cmSystemTools::LowerCase(algo); - cmsys::ofstream outF(hashFile.c_str()); - if (!outF) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Cannot create checksum file: " << hashFile - << std::endl); + if (!this->GenerateChecksumFile(*crypto, filename)) { return 0; } - outF << crypto->HashFile(packageFileName) << " " << filename << "\n"; - cmCPackLogger(cmCPackLog::LOG_OUTPUT, - "- checksum file: " << hashFile << " generated." - << std::endl); } } @@ -1216,6 +1256,60 @@ // it, the default value should be: this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/"); + // Special handling for CPACK_TEMPORARY[_INSTALL]_DIRECTORY. + // Note: Make sure that if only one of these variables is already set, the + // other will be set to the same value. If they are set to different + // values, however, we cannot proceed. + cmValue val1 = + this->MakefileMap->GetDefinition("CPACK_TEMPORARY_INSTALL_DIRECTORY"); + cmValue val2 = this->MakefileMap->GetDefinition("CPACK_TEMPORARY_DIRECTORY"); + if (val1 != val2) { + // One variable is set but not the other? + // Then set the other variable to the same value (even if it is invalid). + if (val1.Get() != nullptr && val2.Get() == nullptr) { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "Variable CPACK_TEMPORARY_INSTALL_DIRECTORY is set, which " + "is not recommended. For backwards-compatibility we will " + "also set CPACK_TEMPORARY_DIRECTORY to the same value and " + "proceed. However, better set neither of them!" + << std::endl); + this->MakefileMap->AddDefinition("CPACK_TEMPORARY_DIRECTORY", val1); + } else if (val1.Get() == nullptr && val2.Get() != nullptr) { + cmCPackLogger( + cmCPackLog::LOG_WARNING, + "Variable CPACK_TEMPORARY_DIRECTORY is set, which is not recommended." + << std::endl); + cmCPackLogger( + cmCPackLog::LOG_DEBUG, + "For backwards-compatibility we will set " + "CPACK_TEMPORARY_INSTALL_DIRECTORY to the same value as " + "CPACK_TEMPORARY_DIRECTORY. However, better set neither of them!" + << std::endl); + this->MakefileMap->AddDefinition("CPACK_TEMPORARY_INSTALL_DIRECTORY", + val2); + } else { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "CPACK_TEMPORARY_INSTALL_DIRECTORY is already set to: " + << val1 << std::endl); + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, + "CPACK_TEMPORARY_DIRECTORY is already set to: " << val2 << std::endl); + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Variables CPACK_TEMPORARY_DIRECTORY and " + "CPACK_TEMPORARY_INSTALL_DIRECTORY are both set but to " + "different values. This is not supported!" + << std::endl); + return 0; + } + } else if (val1.Get() != nullptr && val2.Get() != nullptr) { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "Variables CPACK_TEMPORARY_DIRECTORY and " + "CPACK_TEMPORARY_INSTALL_DIRECTORY are both set. Because " + "they are set to the same value we can still proceed. " + "However, better set neither of them!" + << std::endl); + } + return result; } @@ -1231,14 +1325,14 @@ bool cmCPackGenerator::IsOn(const std::string& name) const { - return cmIsOn(this->GetOption(name)); + return this->GetOption(name).IsOn(); } bool cmCPackGenerator::IsSetToOff(const std::string& op) const { cmValue ret = this->MakefileMap->GetDefinition(op); if (cmNonempty(ret)) { - return cmIsOff(*ret); + return ret.IsOff(); } return false; } @@ -1315,17 +1409,17 @@ return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX")->c_str(); } -std::string cmCPackGenerator::FindTemplate(const char* name) +std::string cmCPackGenerator::FindTemplate(cm::string_view name, + cm::optional<cm::string_view> alt) { cmCPackLogger(cmCPackLog::LOG_DEBUG, - "Look for template: " << (name ? name : "(NULL)") - << std::endl); + "Look for template: " << name << std::endl); // Search CMAKE_MODULE_PATH for a custom template. std::string ffile = this->MakefileMap->GetModulesFile(name); if (ffile.empty()) { // Fall back to our internal builtin default. ffile = cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/Internal/CPack/", - name); + alt ? *alt : ""_s, name); cmSystemTools::ConvertToUnixSlashes(ffile); if (!cmSystemTools::FileExists(ffile)) { ffile.clear(); @@ -1354,7 +1448,7 @@ int cmCPackGenerator::CleanTemporaryDirectory() { std::string tempInstallDirectory = - this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); if (cmsys::SystemTools::FileExists(tempInstallDirectory)) { cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Clean temporary : " << tempInstallDirectory << std::endl); @@ -1439,8 +1533,8 @@ this->componentPackageMethod = method; } - const char* method_names[] = { "ALL_COMPONENTS_IN_ONE", "IGNORE_GROUPS", - "ONE_PER_GROUP" }; + const char* method_names[] = { "ALL_COMPONENTS_IN_ONE", "IGNORE", + "ONE_PER_GROUP", "UNKNOWN" }; cmCPackLogger(cmCPackLog::LOG_VERBOSE, "[" << this->Name << "]" @@ -1451,11 +1545,63 @@ return 1; } -std::string cmCPackGenerator::GetComponentInstallDirNameSuffix( +std::string cmCPackGenerator::GetSanitizedDirOrFileName( + const std::string& name, bool isFullName) const +{ + if (isFullName) { +#ifdef _WIN32 + // Given name matches a reserved name (on Windows)? + // Then return it prepended with an underscore. + // See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file + cmsys::RegularExpression reserved_pattern("^(" + "[Cc][Oo][Nn]|" + "[Pp][Rr][Nn]|" + "[Aa][Uu][Xx]|" + "[Nn][Uu][Ll]|" + "[Cc][Oo][Mm][1-9]|" + "[Ll][Pp][Tt][1-9]" + ")[.]?$"); + if (reserved_pattern.find(name)) { + return "_" + name; + } + // Given name ends in a dot (on Windows)? + // Then return it appended with an underscore. + if (name.back() == '.') { + return name + '_'; + } +#endif + } + +#ifndef _WIN32 + constexpr const char* prohibited_chars = "<>\"/\\|?*`"; +#else + // Note: Windows also excludes the colon. + constexpr const char* prohibited_chars = "<>\"/\\|?*`:"; +#endif + // Given name contains non-supported character? + // Then return its MD5 hash. + if (name.find_first_of(prohibited_chars) != std::string::npos) { + cmCryptoHash hasher(cmCryptoHash::AlgoMD5); + return hasher.HashString(name); + } + + // Otherwise return unmodified. + return name; +} + +std::string cmCPackGenerator::GetComponentInstallSuffix( const std::string& componentName) { return componentName; } + +std::string cmCPackGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + return this->GetSanitizedDirOrFileName( + this->GetComponentInstallSuffix(componentName)); +} + std::string cmCPackGenerator::GetComponentPackageFileName( const std::string& initialPackageFileName, const std::string& groupOrComponentName, bool isGroupName) @@ -1560,7 +1706,7 @@ component->IsRequired = this->IsOn(macroPrefix + "_REQUIRED"); component->IsDisabledByDefault = this->IsOn(macroPrefix + "_DISABLED"); component->IsDownloaded = this->IsOn(macroPrefix + "_DOWNLOADED") || - cmIsOn(this->GetOption("CPACK_DOWNLOAD_ALL")); + this->GetOption("CPACK_DOWNLOAD_ALL").IsOn(); cmValue archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE"); if (cmNonempty(archiveFile)) {
diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h index 8078d9f..980ab8f 100644 --- a/Source/CPack/cmCPackGenerator.h +++ b/Source/CPack/cmCPackGenerator.h
@@ -9,6 +9,9 @@ #include <string> #include <vector> +#include <cm/optional> +#include <cm/string_view> + #include "cm_sys_stat.h" #include "cmCPackComponentGroup.h" @@ -16,6 +19,7 @@ #include "cmValue.h" class cmCPackLog; +class cmCryptoHash; class cmGlobalGenerator; class cmInstalledFile; class cmMakefile; @@ -141,6 +145,18 @@ virtual int PrepareGroupingKind(); /** + * Ensures that the given name only contains characters that can cleanly be + * used as directory or file name and returns this sanitized name. Possibly, + * this name might be replaced by its hash. + * @param[in] name the name for a directory or file that shall be sanitized. + * @param[in] isFullName true if the result is used as the full name for a + * directory or file. (Defaults to true.) + * @return the sanitized name. + */ + virtual std::string GetSanitizedDirOrFileName(const std::string& name, + bool isFullName = true) const; + + /** * Some CPack generators may prefer to have * CPack install all components belonging to the same * [component] group to be install in the same directory. @@ -150,6 +166,16 @@ * @return the name suffix the generator wants for the specified component * default is "componentName" */ + virtual std::string GetComponentInstallSuffix( + const std::string& componentName); + + /** + * The value that GetComponentInstallSuffix returns, but sanitized. + * @param[in] componentName the name of the component to be installed + * @return the name suffix the generator wants for the specified component + * (but sanitized, so that it can be used on the file-system). + * default is "componentName". + */ virtual std::string GetComponentInstallDirNameSuffix( const std::string& componentName); @@ -179,7 +205,13 @@ virtual const char* GetInstallPath(); virtual const char* GetPackagingInstallPrefix(); - virtual std::string FindTemplate(const char* name); + bool GenerateChecksumFile(cmCryptoHash& crypto, + cm::string_view filename) const; + bool CopyPackageFile(const std::string& srcFilePath, + cm::string_view filename) const; + + std::string FindTemplate(cm::string_view name, + cm::optional<cm::string_view> alt = cm::nullopt); virtual bool ConfigureFile(const std::string& inName, const std::string& outName, bool copyOnly = false);
diff --git a/Source/CPack/cmCPackInnoSetupGenerator.cxx b/Source/CPack/cmCPackInnoSetupGenerator.cxx index bf90b06..17ea89f 100644 --- a/Source/CPack/cmCPackInnoSetupGenerator.cxx +++ b/Source/CPack/cmCPackInnoSetupGenerator.cxx
@@ -7,7 +7,6 @@ #include <cctype> #include <cstdlib> #include <ostream> -#include <stack> #include <utility> #include "cmsys/RegularExpression.hxx" @@ -25,17 +24,12 @@ bool cmCPackInnoSetupGenerator::CanGenerate() { - // Inno Setup is only available for Windows -#ifdef _WIN32 return true; -#else - return false; -#endif } int cmCPackInnoSetupGenerator::InitializeInternal() { - if (cmIsOn(GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) { + if (GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) { cmCPackLogger(cmCPackLog::LOG_WARNING, "Inno Setup Generator cannot work with " "CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. " @@ -64,7 +58,8 @@ return 0; } - const std::string isccCmd = cmStrCat(QuotePath(isccPath), "/?"); + const std::string isccCmd = + cmStrCat(QuotePath(isccPath, PathType::Native), "/?"); cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test Inno Setup version: " << isccCmd << std::endl); std::string output; @@ -613,7 +608,6 @@ // Components std::vector<cmCPackComponent*> downloadedComponents; - std::stack<cmCPackComponentGroup*> groups; for (auto& i : Components) { cmCPackInnoSetupKeyValuePairs params; cmCPackComponent* component = &i.second; @@ -871,8 +865,8 @@ } const std::string& isccCmd = - cmStrCat(QuotePath(GetOption("CPACK_INSTALLER_PROGRAM")), ' ', - cmJoin(isccArgs, " "), ' ', QuotePath(isScriptFile)); + cmStrCat(QuotePath(GetOption("CPACK_INSTALLER_PROGRAM"), PathType::Native), + ' ', cmJoin(isccArgs, " "), ' ', QuotePath(isScriptFile)); cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << isccCmd << std::endl); @@ -940,7 +934,7 @@ // Build the list of files to go into this archive const std::string& zipListFileName = cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), "/winZip.filelist"); - const bool needQuotesInFile = cmIsOn(GetOption("CPACK_ZIP_NEED_QUOTES")); + const bool needQuotesInFile = GetOption("CPACK_ZIP_NEED_QUOTES").IsOn(); { // the scope is needed for cmGeneratedFileStream cmGeneratedFileStream out(zipListFileName); for (const std::string& i : component->Files) { @@ -1138,8 +1132,16 @@ return cmStrCat('"', nString, '"'); } -std::string cmCPackInnoSetupGenerator::QuotePath(const std::string& path) +std::string cmCPackInnoSetupGenerator::QuotePath(const std::string& path, + PathType type) { +#ifdef _WIN32 + static_cast<void>(type); +#else + if (type == PathType::Native) { + return Quote(cmSystemTools::ConvertToUnixOutputPath(path)); + } +#endif return Quote(cmSystemTools::ConvertToWindowsOutputPath(path)); }
diff --git a/Source/CPack/cmCPackInnoSetupGenerator.h b/Source/CPack/cmCPackInnoSetupGenerator.h index 342f227..e057e6c 100644 --- a/Source/CPack/cmCPackInnoSetupGenerator.h +++ b/Source/CPack/cmCPackInnoSetupGenerator.h
@@ -49,6 +49,12 @@ inline bool SupportsComponentInstallation() const override { return true; } private: + enum class PathType + { + Windows, + Native, + }; + bool ProcessSetupSection(); bool ProcessFiles(); bool ProcessComponents(); @@ -92,7 +98,8 @@ * Paths are converted into the format used by Windows before. */ std::string Quote(const std::string& string); - std::string QuotePath(const std::string& path); + std::string QuotePath(const std::string& path, + PathType type = PathType::Windows); /** * This function replaces the following 5 characters with their %-encoding:
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 7749b29..155bc9e 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -365,7 +365,7 @@ if (anyDownloadedComponents) { defines += "!define CPACK_USES_DOWNLOAD\n"; - if (cmIsOn(this->GetOption("CPACK_ADD_REMOVE"))) { + if (this->GetOption("CPACK_ADD_REMOVE").IsOn()) { defines += "!define CPACK_NSIS_ADD_REMOVE\n"; } } @@ -412,7 +412,7 @@ int cmCPackNSISGenerator::InitializeInternal() { - if (cmIsOn(this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) { + if (this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) { cmCPackLogger( cmCPackLog::LOG_WARNING, "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. " @@ -801,7 +801,7 @@ // size of the installed component. std::string zipListFileName = cmStrCat( this->GetOption("CPACK_TEMPORARY_DIRECTORY"), "/winZip.filelist"); - bool needQuotesInFile = cmIsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES")); + bool needQuotesInFile = this->GetOption("CPACK_ZIP_NEED_QUOTES").IsOn(); unsigned long totalSize = 0; { // the scope is needed for cmGeneratedFileStream cmGeneratedFileStream out(zipListFileName); @@ -863,8 +863,8 @@ /* clang-format on */ componentCode += out.str(); } else { - componentCode += - " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n"; + componentCode += " File /r \"${INST_DIR}\\" + + this->GetSanitizedDirOrFileName(component->Name) + "\\*.*\"\n"; } componentCode += "SectionEnd\n";
diff --git a/Source/CPack/cmCPackRPMGenerator.cxx b/Source/CPack/cmCPackRPMGenerator.cxx index 3d4d05e..8dfb159 100644 --- a/Source/CPack/cmCPackRPMGenerator.cxx +++ b/Source/CPack/cmCPackRPMGenerator.cxx
@@ -12,6 +12,7 @@ #include "cmCPackComponentGroup.h" #include "cmCPackGenerator.h" #include "cmCPackLog.h" +#include "cmCryptoHash.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" @@ -62,7 +63,15 @@ int cmCPackRPMGenerator::PackageOnePack(std::string const& initialToplevel, std::string const& packageName) { - int retval = 1; + // Determine the sanitized package name that can be used in file-names on + // the file-system. + std::string sanitizedPkgNameSuffix = + this->GetSanitizedDirOrFileName(packageName, false); + // Determine the sanitized packaging directory-name that can be used on the + // file-system. + std::string sanitizedPkgDirName = + this->GetSanitizedDirOrFileName(packageName); + // Begin the archive for this pack std::string localToplevel(initialToplevel); std::string packageFileName( @@ -72,7 +81,7 @@ this->GetOption("CPACK_PACKAGE_FILE_NAME"), packageName, true) + this->GetOutputExtension()); - localToplevel += "/" + packageName; + localToplevel += "/" + sanitizedPkgDirName; /* replace the TEMP DIRECTORY with the component one */ this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel); packageFileName += "/" + outputFileName; @@ -82,16 +91,34 @@ this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName); // Tell CPackRPM.cmake the name of the component NAME. this->SetOption("CPACK_RPM_PACKAGE_COMPONENT", packageName); + // Tell CPackRPM.cmake the suffix for the component NAME. + this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_NAME", + sanitizedPkgNameSuffix); // Tell CPackRPM.cmake the path where the component is. - std::string component_path = cmStrCat('/', packageName); + std::string component_path = cmStrCat('/', sanitizedPkgDirName); this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", component_path); if (!this->ReadListFile("Internal/CPack/CPackRPM.cmake")) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while execution CPackRPM.cmake" << std::endl); - retval = 0; + return 0; } - return retval; + return 1; +} + +std::string cmCPackRPMGenerator::GetSanitizedDirOrFileName( + const std::string& name, bool isFullName) const +{ + auto sanitizedName = + this->cmCPackGenerator::GetSanitizedDirOrFileName(name, isFullName); + if (sanitizedName == name && !isFullName) { + // Make sure to also sanitize if name contains a colon (':'). + if (name.find_first_of(':') != std::string::npos) { + cmCryptoHash hasher(cmCryptoHash::AlgoMD5); + return hasher.HashString(name); + } + } + return sanitizedName; } int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup) @@ -417,7 +444,7 @@ return this->IsOn("CPACK_RPM_COMPONENT_INSTALL"); } -std::string cmCPackRPMGenerator::GetComponentInstallDirNameSuffix( +std::string cmCPackRPMGenerator::GetComponentInstallSuffix( const std::string& componentName) { if (this->componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) { @@ -432,7 +459,14 @@ std::string groupVar = "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP"; if (nullptr != this->GetOption(groupVar)) { - return std::string(this->GetOption(groupVar)); + return *this->GetOption(groupVar); } return componentName; } + +std::string cmCPackRPMGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + return this->GetSanitizedDirOrFileName( + this->GetComponentInstallSuffix(componentName)); +}
diff --git a/Source/CPack/cmCPackRPMGenerator.h b/Source/CPack/cmCPackRPMGenerator.h index 886afb1..0e23503 100644 --- a/Source/CPack/cmCPackRPMGenerator.h +++ b/Source/CPack/cmCPackRPMGenerator.h
@@ -61,7 +61,11 @@ */ int PackageComponentsAllInOne(const std::string& compInstDirName); const char* GetOutputExtension() override { return ".rpm"; } + std::string GetSanitizedDirOrFileName(const std::string& name, + bool isFullName = true) const override; bool SupportsComponentInstallation() const override; + std::string GetComponentInstallSuffix( + const std::string& componentName) override; std::string GetComponentInstallDirNameSuffix( const std::string& componentName) override;
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx index 1248d17..2673fee 100644 --- a/Source/CPack/cmCPackSTGZGenerator.cxx +++ b/Source/CPack/cmCPackSTGZGenerator.cxx
@@ -3,7 +3,6 @@ #include "cmCPackSTGZGenerator.h" #include <cstdio> -#include <sstream> #include <string> #include <vector> @@ -71,7 +70,6 @@ int cmCPackSTGZGenerator::GenerateHeader(std::ostream* os) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Writing header" << std::endl); - std::ostringstream str; int counter = 0; std::string inLicFile = this->GetOption("CPACK_RESOURCE_FILE_LICENSE");
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 00c8fa2..39a8e85 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx
@@ -404,7 +404,9 @@ "Read CPack config file: " << cpackConfigFile << '\n'); bool cpackConfigFileSpecified = true; - if (cpackConfigFile.empty()) { + if (!cpackConfigFile.empty()) { + cpackConfigFile = cmSystemTools::CollapseFullPath(cpackConfigFile); + } else { cpackConfigFile = cmStrCat(cmSystemTools::GetCurrentWorkingDirectory(), "/CPackConfig.cmake"); cpackConfigFileSpecified = false; @@ -446,7 +448,6 @@ } if (cmSystemTools::FileExists(cpackConfigFile)) { - cpackConfigFile = cmSystemTools::CollapseFullPath(cpackConfigFile); cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, "Read CPack configuration file: " << cpackConfigFile << '\n'); @@ -475,32 +476,26 @@ if (!cpackProjectVendor.empty()) { globalMF.AddDefinition("CPACK_PACKAGE_VENDOR", cpackProjectVendor); } - // if this is not empty it has been set on the command line - // go for it. Command line override values set in config file. if (!cpackProjectDirectory.empty()) { - globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory); - } - // The value has not been set on the command line - else { - // get a default value (current working directory) - cpackProjectDirectory = cmSystemTools::GetCurrentWorkingDirectory(); - // use default value if no value has been provided by the config file - if (!globalMF.IsSet("CPACK_PACKAGE_DIRECTORY")) { - globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", - cpackProjectDirectory); + // The value has been set on the command line. Ensure it is absolute. + cpackProjectDirectory = + cmSystemTools::CollapseFullPath(cpackProjectDirectory); + } else { + // The value has not been set on the command line. Check config file. + if (cmValue pd = globalMF.GetDefinition("CPACK_PACKAGE_DIRECTORY")) { + // The value has been set in the config file. Ensure it is absolute. + cpackProjectDirectory = cmSystemTools::CollapseFullPath(*pd); + } else { + // Default to the current working directory. + cpackProjectDirectory = cmSystemTools::GetCurrentWorkingDirectory(); } } + globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory); + for (auto const& cd : definitions) { globalMF.AddDefinition(cd.first, cd.second); } - // Force CPACK_PACKAGE_DIRECTORY as absolute path - cpackProjectDirectory = - globalMF.GetSafeDefinition("CPACK_PACKAGE_DIRECTORY"); - cpackProjectDirectory = - cmSystemTools::CollapseFullPath(cpackProjectDirectory); - globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory); - cmValue cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH"); if (cpackModulesPath) { globalMF.AddDefinition("CMAKE_MODULE_PATH", *cpackModulesPath);
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index bb6ccc3..9faabf7 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -365,8 +365,9 @@ int cmCTestBuildAndTestHandler::ProcessCommandLineArguments( const std::string& currentArg, size_t& idx, - const std::vector<std::string>& allArgs) + const std::vector<std::string>& allArgs, bool& validArg) { + bool buildAndTestArg = true; // --build-and-test options if (cmHasLiteralPrefix(currentArg, "--build-and-test") && idx < allArgs.size() - 1) { @@ -385,78 +386,68 @@ << std::endl); return 0; } - } - if (cmHasLiteralPrefix(currentArg, "--build-target") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--build-target") && + idx < allArgs.size() - 1) { idx++; this->BuildTargets.push_back(allArgs[idx]); - } - if (cmHasLiteralPrefix(currentArg, "--build-nocmake")) { + } else if (cmHasLiteralPrefix(currentArg, "--build-nocmake")) { this->BuildNoCMake = true; - } - if (cmHasLiteralPrefix(currentArg, "--build-run-dir") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--build-run-dir") && + idx < allArgs.size() - 1) { idx++; this->BuildRunDir = allArgs[idx]; - } - if (cmHasLiteralPrefix(currentArg, "--build-two-config")) { + } else if (cmHasLiteralPrefix(currentArg, "--build-two-config")) { this->BuildTwoConfig = true; - } - if (cmHasLiteralPrefix(currentArg, "--build-exe-dir") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--build-exe-dir") && + idx < allArgs.size() - 1) { idx++; this->ExecutableDirectory = allArgs[idx]; - } - if (cmHasLiteralPrefix(currentArg, "--test-timeout") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--test-timeout") && + idx < allArgs.size() - 1) { idx++; this->Timeout = cmDuration(atof(allArgs[idx].c_str())); - } - if (currentArg == "--build-generator" && idx < allArgs.size() - 1) { + } else if (currentArg == "--build-generator" && idx < allArgs.size() - 1) { idx++; this->BuildGenerator = allArgs[idx]; - } - if (currentArg == "--build-generator-platform" && idx < allArgs.size() - 1) { + } else if (currentArg == "--build-generator-platform" && + idx < allArgs.size() - 1) { idx++; this->BuildGeneratorPlatform = allArgs[idx]; - } - if (currentArg == "--build-generator-toolset" && idx < allArgs.size() - 1) { + } else if (currentArg == "--build-generator-toolset" && + idx < allArgs.size() - 1) { idx++; this->BuildGeneratorToolset = allArgs[idx]; - } - if (cmHasLiteralPrefix(currentArg, "--build-project") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--build-project") && + idx < allArgs.size() - 1) { idx++; this->BuildProject = allArgs[idx]; - } - if (cmHasLiteralPrefix(currentArg, "--build-makeprogram") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--build-makeprogram") && + idx < allArgs.size() - 1) { idx++; this->BuildMakeProgram = allArgs[idx]; - } - if (cmHasLiteralPrefix(currentArg, "--build-config-sample") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--build-config-sample") && + idx < allArgs.size() - 1) { idx++; this->ConfigSample = allArgs[idx]; - } - if (cmHasLiteralPrefix(currentArg, "--build-noclean")) { + } else if (cmHasLiteralPrefix(currentArg, "--build-noclean")) { this->BuildNoClean = true; - } - if (cmHasLiteralPrefix(currentArg, "--build-options")) { + } else if (cmHasLiteralPrefix(currentArg, "--build-options")) { while (idx + 1 < allArgs.size() && allArgs[idx + 1] != "--build-target" && allArgs[idx + 1] != "--test-command") { ++idx; this->BuildOptions.push_back(allArgs[idx]); } - } - if (cmHasLiteralPrefix(currentArg, "--test-command") && - idx < allArgs.size() - 1) { + } else if (cmHasLiteralPrefix(currentArg, "--test-command") && + idx < allArgs.size() - 1) { ++idx; this->TestCommand = allArgs[idx]; while (idx + 1 < allArgs.size()) { ++idx; this->TestCommandArgs.push_back(allArgs[idx]); } + } else { + buildAndTestArg = false; } + validArg = validArg || buildAndTestArg; return 1; }
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.h b/Source/CTest/cmCTestBuildAndTestHandler.h index e022e68..60b3a11 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.h +++ b/Source/CTest/cmCTestBuildAndTestHandler.h
@@ -29,9 +29,9 @@ int ProcessHandler() override; //! Set all the build and test arguments - int ProcessCommandLineArguments( - const std::string& currentArg, size_t& idx, - const std::vector<std::string>& allArgs) override; + int ProcessCommandLineArguments(const std::string& currentArg, size_t& idx, + const std::vector<std::string>& allArgs, + bool& validArg) override; /* * Get the output variable
diff --git a/Source/CTest/cmCTestCurl.cxx b/Source/CTest/cmCTestCurl.cxx index 8f7d581..7137e63 100644 --- a/Source/CTest/cmCTestCurl.cxx +++ b/Source/CTest/cmCTestCurl.cxx
@@ -9,22 +9,20 @@ #include "cmCTest.h" #include "cmCurl.h" +#include "cmList.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmValue.h" cmCTestCurl::cmCTestCurl(cmCTest* ctest) + : CTest(ctest) + , CurlOpts(ctest) { - this->CTest = ctest; this->SetProxyType(); - this->UseHttp10 = false; + cmCurlInitOnce(); // In windows, this will init the winsock stuff ::curl_global_init(CURL_GLOBAL_ALL); - // default is to verify https - this->VerifyPeerOff = false; - this->VerifyHostOff = false; - this->Quiet = false; - this->TimeOutSeconds = 0; - this->Curl = curl_easy_init(); + this->Curl = cm_curl_easy_init(); } cmCTestCurl::~cmCTestCurl() @@ -59,14 +57,23 @@ } } -void cmCTestCurl::SetCurlOptions(std::vector<std::string> const& args) +cmCTestCurlOpts::cmCTestCurlOpts(cmCTest* ctest) { - for (std::string const& arg : args) { - if (arg == "CURLOPT_SSL_VERIFYPEER_OFF") { - this->VerifyPeerOff = true; - } - if (arg == "CURLOPT_SSL_VERIFYHOST_OFF") { - this->VerifyHostOff = true; + this->TLSVersionOpt = + cmCurlParseTLSVersion(ctest->GetCTestConfiguration("TLSVersion")); + + std::string tlsVerify = ctest->GetCTestConfiguration("TLSVerify"); + if (!tlsVerify.empty()) { + this->TLSVerifyOpt = cmIsOn(tlsVerify); + } else { + cmList args{ ctest->GetCTestConfiguration("CurlOptions") }; + for (std::string const& arg : args) { + if (arg == "CURLOPT_SSL_VERIFYPEER_OFF") { + this->TLSVerifyOpt = false; + } + if (arg == "CURLOPT_SSL_VERIFYHOST_OFF") { + this->VerifyHostOff = true; + } } } } @@ -77,10 +84,15 @@ return false; } cmCurlSetCAInfo(this->Curl); - if (this->VerifyPeerOff) { - curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYPEER, 0); + if (this->CurlOpts.TLSVersionOpt) { + curl_easy_setopt(this->Curl, CURLOPT_SSLVERSION, + *this->CurlOpts.TLSVersionOpt); } - if (this->VerifyHostOff) { + if (this->CurlOpts.TLSVerifyOpt) { + curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYPEER, + *this->CurlOpts.TLSVerifyOpt ? 1 : 0); + } + if (this->CurlOpts.VerifyHostOff) { curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYHOST, 0); } if (!this->HTTPProxy.empty()) {
diff --git a/Source/CTest/cmCTestCurl.h b/Source/CTest/cmCTestCurl.h index d9aa916..7836f4b 100644 --- a/Source/CTest/cmCTestCurl.h +++ b/Source/CTest/cmCTestCurl.h
@@ -7,10 +7,20 @@ #include <string> #include <vector> +#include <cm/optional> + #include <cm3p/curl/curl.h> class cmCTest; +struct cmCTestCurlOpts +{ + cmCTestCurlOpts(cmCTest* ctest); + cm::optional<int> TLSVersionOpt; + cm::optional<bool> TLSVerifyOpt; + bool VerifyHostOff = false; +}; + class cmCTestCurl { public: @@ -22,9 +32,6 @@ std::string const& fields, std::string& response); bool HttpRequest(std::string const& url, std::string const& fields, std::string& response); - // currently only supports CURLOPT_SSL_VERIFYPEER_OFF - // and CURLOPT_SSL_VERIFYHOST_OFF - void SetCurlOptions(std::vector<std::string> const& args); void SetHttpHeaders(std::vector<std::string> const& v) { this->HttpHeaders = v; @@ -40,14 +47,13 @@ private: cmCTest* CTest; - CURL* Curl; + cmCTestCurlOpts CurlOpts; + CURL* Curl = nullptr; std::vector<std::string> HttpHeaders; std::string HTTPProxyAuth; std::string HTTPProxy; curl_proxytype HTTPProxyType; - bool VerifyHostOff; - bool VerifyPeerOff; - bool UseHttp10; - bool Quiet; - int TimeOutSeconds; + bool UseHttp10 = false; + bool Quiet = false; + int TimeOutSeconds = 0; };
diff --git a/Source/CTest/cmCTestGenericHandler.h b/Source/CTest/cmCTestGenericHandler.h index a0197d6..a189d60 100644 --- a/Source/CTest/cmCTestGenericHandler.h +++ b/Source/CTest/cmCTestGenericHandler.h
@@ -48,7 +48,7 @@ */ virtual int ProcessCommandLineArguments( const std::string& /*currentArg*/, size_t& /*idx*/, - const std::vector<std::string>& /*allArgs*/) + const std::vector<std::string>& /*allArgs*/, bool& /*valid*/) { return 1; }
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 773a305..84ea32b 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -90,7 +90,7 @@ cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() = default; // Set the tests -void cmCTestMultiProcessHandler::SetTests(TestMap tests, +bool cmCTestMultiProcessHandler::SetTests(TestMap tests, PropertiesMap properties) { this->PendingTests = std::move(tests); @@ -102,10 +102,11 @@ this->HasInvalidGeneratedResourceSpec = !this->CheckGeneratedResourceSpec(); if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) { - return; + return false; } this->CreateTestCostList(); } + return true; } // Set the max number of tests that can be run at the same time. @@ -1215,6 +1216,11 @@ properties.append( DumpCTestProperty("WORKING_DIRECTORY", testProperties.Directory)); } + if (!testProperties.CustomProperties.empty()) { + for (auto const& it : testProperties.CustomProperties) { + properties.append(DumpCTestProperty(it.first, it.second)); + } + } return properties; }
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index f491815..fd6c17f 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -61,7 +61,7 @@ cmCTestMultiProcessHandler(cmCTest* ctest, cmCTestTestHandler* handler); virtual ~cmCTestMultiProcessHandler(); // Set the tests - void SetTests(TestMap tests, PropertiesMap properties); + bool SetTests(TestMap tests, PropertiesMap properties); // Set the max number of tests that can be run at the same time. void SetParallelLevel(cm::optional<size_t> level); void SetTestLoad(unsigned long load);
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 0beee67..2003ce6 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -563,10 +563,8 @@ int cmCTestScriptHandler::CheckOutSourceDir() { - std::string command; std::string output; int retVal; - bool res; if (!cmSystemTools::FileExists(this->SourceDir) && !this->CVSCheckOut.empty()) { @@ -574,7 +572,7 @@ output.clear(); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run cvs: " << this->CVSCheckOut << std::endl); - res = cmSystemTools::RunSingleCommand( + bool res = cmSystemTools::RunSingleCommand( this->CVSCheckOut, &output, &output, &retVal, this->CTestRoot.c_str(), this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/); if (!res || retVal != 0) { @@ -587,8 +585,6 @@ int cmCTestScriptHandler::BackupDirectories() { - int retVal; - // compute the backup names this->BackupSourceDir = cmStrCat(this->SourceDir, "_CMakeBackup"); this->BackupBinaryDir = cmStrCat(this->BinaryDir, "_CMakeBackup"); @@ -608,7 +604,7 @@ rename(this->BinaryDir.c_str(), this->BackupBinaryDir.c_str()); // we must now checkout the src dir - retVal = this->CheckOutSourceDir(); + int retVal = this->CheckOutSourceDir(); if (retVal) { this->RestoreBackupDirectories(); return retVal;
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index a92f9f2..029f81f 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -55,6 +55,50 @@ this->Makefile, "DropLocation", "CTEST_DROP_LOCATION", this->Quiet); } + if (!this->CTest->SetCTestConfigurationFromCMakeVariable( + this->Makefile, "TLSVersion", "CTEST_TLS_VERSION", this->Quiet)) { + if (cmValue tlsVersionVar = + this->Makefile->GetDefinition("CMAKE_TLS_VERSION")) { + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + "SetCTestConfiguration from CMAKE_TLS_VERSION:TLSVersion:" + << *tlsVersionVar << std::endl, + this->Quiet); + this->CTest->SetCTestConfiguration("TLSVersion", *tlsVersionVar, + this->Quiet); + } else if (cm::optional<std::string> tlsVersionEnv = + cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) { + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + "SetCTestConfiguration from ENV{CMAKE_TLS_VERSION}:TLSVersion:" + << *tlsVersionEnv << std::endl, + this->Quiet); + this->CTest->SetCTestConfiguration("TLSVersion", *tlsVersionEnv, + this->Quiet); + } + } + if (!this->CTest->SetCTestConfigurationFromCMakeVariable( + this->Makefile, "TLSVerify", "CTEST_TLS_VERIFY", this->Quiet)) { + if (cmValue tlsVerifyVar = + this->Makefile->GetDefinition("CMAKE_TLS_VERIFY")) { + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + "SetCTestConfiguration from CMAKE_TLS_VERIFY:TLSVerify:" + << *tlsVerifyVar << std::endl, + this->Quiet); + this->CTest->SetCTestConfiguration("TLSVerify", *tlsVerifyVar, + this->Quiet); + } else if (cm::optional<std::string> tlsVerifyEnv = + cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) { + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + "SetCTestConfiguration from ENV{CMAKE_TLS_VERIFY}:TLSVerify:" + << *tlsVerifyEnv << std::endl, + this->Quiet); + this->CTest->SetCTestConfiguration("TLSVerify", *tlsVerifyEnv, + this->Quiet); + } + } this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "CurlOptions", "CTEST_CURL_OPTIONS", this->Quiet); this->CTest->SetCTestConfigurationFromCMakeVariable(
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index db8a054..fdf3178 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -8,6 +8,7 @@ #include <sstream> #include <cm/iomanip> +#include <cm/optional> #include <cmext/algorithm> #include <cm3p/curl/curl.h> @@ -22,7 +23,6 @@ #include "cmCurl.h" #include "cmDuration.h" #include "cmGeneratedFileStream.h" -#include "cmList.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -140,13 +140,14 @@ int cmCTestSubmitHandler::ProcessCommandLineArguments( const std::string& currentArg, size_t& idx, - const std::vector<std::string>& allArgs) + const std::vector<std::string>& allArgs, bool& validArg) { if (cmHasLiteralPrefix(currentArg, "--http-header") && idx < allArgs.size() - 1) { ++idx; this->HttpHeaders.push_back(allArgs[idx]); this->CommandLineHttpHeaders.push_back(allArgs[idx]); + validArg = true; } return 1; } @@ -170,32 +171,35 @@ headers = ::curl_slist_append(headers, h.c_str()); } + cmCurlInitOnce(); /* In windows, this will init the winsock stuff */ ::curl_global_init(CURL_GLOBAL_ALL); - std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions")); - cmList args{ curlopt }; - bool verifyPeerOff = false; - bool verifyHostOff = false; - for (std::string const& arg : args) { - if (arg == "CURLOPT_SSL_VERIFYPEER_OFF") { - verifyPeerOff = true; - } - if (arg == "CURLOPT_SSL_VERIFYHOST_OFF") { - verifyHostOff = true; - } - } + cmCTestCurlOpts curlOpts(this->CTest); for (std::string const& file : files) { /* get a curl handle */ - curl = curl_easy_init(); + curl = cm_curl_easy_init(); if (curl) { cmCurlSetCAInfo(curl); - if (verifyPeerOff) { - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - " Set CURLOPT_SSL_VERIFYPEER to off\n", - this->Quiet); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + if (curlOpts.TLSVersionOpt) { + cm::optional<std::string> tlsVersionStr = + cmCurlPrintTLSVersion(*curlOpts.TLSVersionOpt); + cmCTestOptionalLog( + this->CTest, HANDLER_VERBOSE_OUTPUT, + " Set CURLOPT_SSLVERSION to " + << (tlsVersionStr ? *tlsVersionStr : "unknown value") << "\n", + this->Quiet); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, *curlOpts.TLSVersionOpt); } - if (verifyHostOff) { + if (curlOpts.TLSVerifyOpt) { + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + " Set CURLOPT_SSL_VERIFYPEER to " + << (*curlOpts.TLSVerifyOpt ? "on" : "off") + << "\n", + this->Quiet); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, + *curlOpts.TLSVerifyOpt ? 1 : 0); + } + if (curlOpts.VerifyHostOff) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Set CURLOPT_SSL_VERIFYHOST to off\n", this->Quiet); @@ -295,7 +299,7 @@ upload_as += "&MD5="; - if (cmIsOn(this->GetOption("InternalTest"))) { + if (this->GetOption("InternalTest").IsOn()) { upload_as += "ffffffffffffffffffffffffffffffff"; } else { cmCryptoHash hasher(cmCryptoHash::AlgoMD5); @@ -518,9 +522,6 @@ } cmCTestCurl curl(this->CTest); curl.SetQuiet(this->Quiet); - std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions")); - cmList args{ curlopt }; - curl.SetCurlOptions(args); auto submitInactivityTimeout = this->GetSubmitInactivityTimeout(); if (submitInactivityTimeout != 0) { curl.SetTimeOutSeconds(submitInactivityTimeout); @@ -540,7 +541,7 @@ fields = url.substr(pos + 1); url.erase(pos); } - bool internalTest = cmIsOn(this->GetOption("InternalTest")); + bool internalTest = this->GetOption("InternalTest").IsOn(); // Get RETRY_COUNT and RETRY_DELAY values if they were set. std::string retryDelayString = *this->GetOption("RetryDelay");
diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h index e8eb38a..d152b71 100644 --- a/Source/CTest/cmCTestSubmitHandler.h +++ b/Source/CTest/cmCTestSubmitHandler.h
@@ -35,9 +35,9 @@ void Initialize() override; //! Set all the submit arguments - int ProcessCommandLineArguments( - const std::string& currentArg, size_t& idx, - const std::vector<std::string>& allArgs) override; + int ProcessCommandLineArguments(const std::string& currentArg, size_t& idx, + const std::vector<std::string>& allArgs, + bool& validArg) override; /** Specify a set of parts (by name) to submit. */ void SelectParts(std::set<cmCTest::Part> const& parts);
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 9184c4a..2018b73 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -522,8 +522,8 @@ { // Update internal data structure from generic one this->SetTestsToRunInformation(this->GetOption("TestsToRunInformation")); - this->SetUseUnion(cmIsOn(this->GetOption("UseUnion"))); - if (cmIsOn(this->GetOption("ScheduleRandom"))) { + this->SetUseUnion(this->GetOption("UseUnion").IsOn()); + if (this->GetOption("ScheduleRandom").IsOn()) { this->CTest->SetScheduleType("Random"); } if (cmValue repeat = this->GetOption("Repeat")) { @@ -609,7 +609,7 @@ if (val) { this->ExcludeTestListFile = val; } - this->SetRerunFailed(cmIsOn(this->GetOption("RerunFailed"))); + this->SetRerunFailed(this->GetOption("RerunFailed").IsOn()); return true; } @@ -934,7 +934,6 @@ // Now create a final list of tests to run int cnt = 0; inREcnt = 0; - std::string last_directory; ListOfTests finalList; for (cmCTestTestProperties& tp : this->TestList) { cnt++; @@ -1423,7 +1422,9 @@ properties[p.Index] = &p; } parallel->SetResourceSpecFile(this->ResourceSpecFile); - parallel->SetTests(std::move(tests), std::move(properties)); + if (!parallel->SetTests(std::move(tests), std::move(properties))) { + return false; + } parallel->SetPassFailVectors(&passed, &failed); this->TestResults.clear(); parallel->SetTestResults(&this->TestResults); @@ -2448,6 +2449,8 @@ rt.TimeoutRegularExpressions.emplace_back(cr, cr); } } + } else { + rt.CustomProperties[key] = val; } } }
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 6932800..dd1bc59 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h
@@ -145,6 +145,7 @@ std::vector<std::pair<cmsys::RegularExpression, std::string>> TimeoutRegularExpressions; std::map<std::string, std::string> Measurements; + std::map<std::string, std::string> CustomProperties; bool IsInBasedOnREOptions = true; bool WillFail = false; bool Disabled = false;
diff --git a/Source/CTest/cmCTestUploadHandler.cxx b/Source/CTest/cmCTestUploadHandler.cxx index 9d92aad..59e2b88 100644 --- a/Source/CTest/cmCTestUploadHandler.cxx +++ b/Source/CTest/cmCTestUploadHandler.cxx
@@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestUploadHandler.h" +#include <chrono> #include <ostream> #include <string> @@ -54,6 +55,7 @@ std::string("ctest-") + cmVersion::GetCMakeVersion()); this->CTest->AddSiteProperties(xml); xml.StartElement("Upload"); + xml.Element("Time", std::chrono::system_clock::now()); for (std::string const& file : this->Files) { cmCTestOptionalLog(this->CTest, OUTPUT,
diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt index 6f5f145..5d0e240 100644 --- a/Source/Checks/Curses/CMakeLists.txt +++ b/Source/Checks/Curses/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR) project(CheckCurses C) set(CURSES_NEED_NCURSES TRUE)
diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index 0262746..0a03f3a 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake
@@ -43,7 +43,7 @@ # Filter out MSVC output that looks like a command-line warning. string(REGEX REPLACE "[^\n]*warning D[0-9][0-9][0-9][0-9][^\n]*" "" check_output "${check_output}") # Filter out warnings caused by user flags. - string(REGEX REPLACE "[^\n]*warning:[^\n]*-Winvalid-command-line-argument[^\n]*" "" check_output "${check_output}") + string(REGEX REPLACE "[^\n]*warning:[^\n]*-W(invalid|unused)-command-line-argument[^\n]*" "" check_output "${check_output}") # Filter out warnings caused by local configuration. string(REGEX REPLACE "[^\n]*warning:[^\n]*directory not found for option[^\n]*" "" check_output "${check_output}") string(REGEX REPLACE "[^\n]*warning:[^\n]*object file compiled with -mlong-branch which is no longer needed[^\n]*" "" check_output "${check_output}") @@ -53,10 +53,14 @@ string(REGEX REPLACE "[^\n]*libhugetlbfs [^\n]*: WARNING[^\n]*" "" check_output "${check_output}") # Filter out xcodebuild warnings. string(REGEX REPLACE "[^\n]* xcodebuild\\[[0-9]*:[0-9]*\\][^\n]*[Ww]arning: [^\n]*" "" check_output "${check_output}") - # Filter out icpc warnings + # Filter out Intel classic warnings about overridden flags string(REGEX REPLACE "[^\n]*icpc: command line warning #10121: overriding [^\n]*" "" check_output "${check_output}") + # Filter out Intel oneAPI warnings about overridden flags + string(REGEX REPLACE "[^\n]*icpx: warning: overriding [^\n]*" "" check_output "${check_output}") # Filter out ld warnings. string(REGEX REPLACE "[^\n]*ld: warning: [^\n]*" "" check_output "${check_output}") + # Filter out distcc. + string(REGEX REPLACE "[^\n]*distcc\\[[0-9]+\\][^\n]*[Ww]arning:[^\n]*" "" check_output "${check_output}") # If using the feature causes warnings, treat it as broken/unavailable. if(check_output MATCHES "(^|[ :])[Ww][Aa][Rr][Nn][Ii][Nn][Gg]") set(CMake_HAVE_CXX_${FEATURE} OFF CACHE INTERNAL "TRY_COMPILE" FORCE)
diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx index 0b104ea..3a47485 100644 --- a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx +++ b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
@@ -54,7 +54,7 @@ switch (state->GetCacheEntryType(key)) { case cmStateEnums::BOOL: { auto bw = cm::make_unique<cmCursesBoolWidget>(this->EntryWidth, 1, 1, 1); - bw->SetValueAsBool(cmIsOn(*value)); + bw->SetValueAsBool(value.IsOn()); this->Entry = std::move(bw); break; }
diff --git a/Source/LexerParser/cmGccDepfileLexer.cxx b/Source/LexerParser/cmGccDepfileLexer.cxx index ca5577e..d926c8b 100644 --- a/Source/LexerParser/cmGccDepfileLexer.cxx +++ b/Source/LexerParser/cmGccDepfileLexer.cxx
@@ -548,8 +548,8 @@ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; -#define YY_NUM_RULES 12 -#define YY_END_OF_BUFFER 13 +#define YY_NUM_RULES 13 +#define YY_END_OF_BUFFER 14 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -557,11 +557,12 @@ flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static const flex_int16_t yy_accept[31] = +static const flex_int16_t yy_accept[32] = { 0, - 0, 0, 13, 11, 9, 6, 11, 10, 11, 11, - 11, 9, 0, 6, 10, 1, 8, 7, 0, 0, - 5, 0, 3, 2, 0, 8, 0, 4, 0, 0 + 0, 0, 14, 12, 10, 7, 12, 11, 12, 12, + 12, 10, 0, 7, 11, 1, 9, 8, 0, 0, + 6, 0, 4, 2, 3, 0, 9, 0, 5, 0, + 0 } ; static const YY_CHAR yy_ec[256] = @@ -601,40 +602,40 @@ 1, 2, 1, 1, 2, 1, 1, 1, 1, 3 } ; -static const flex_int16_t yy_base[33] = +static const flex_int16_t yy_base[34] = { 0, - 0, 0, 36, 46, 25, 46, 31, 27, 18, 9, + 0, 0, 39, 46, 26, 46, 32, 28, 25, 9, 17, 15, 25, 46, 17, 46, 0, 46, 15, 27, - 46, 14, 46, 46, 27, 46, 13, 46, 33, 46, - 42, 13 + 46, 14, 46, 46, 46, 27, 46, 13, 46, 33, + 46, 42, 13 } ; -static const flex_int16_t yy_def[33] = +static const flex_int16_t yy_def[34] = { 0, - 30, 1, 30, 30, 31, 30, 30, 30, 30, 30, - 30, 31, 30, 30, 30, 30, 32, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, - 30, 30 + 31, 1, 31, 31, 32, 31, 31, 31, 31, 31, + 31, 32, 31, 31, 31, 31, 33, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 0, 31, 31 } ; static const flex_int16_t yy_nxt[57] = { 0, 4, 5, 6, 7, 5, 8, 4, 9, 10, 11, - 17, 18, 19, 17, 17, 26, 21, 18, 20, 21, - 22, 23, 15, 24, 13, 16, 25, 21, 22, 26, - 27, 28, 15, 14, 13, 30, 29, 23, 30, 30, - 30, 30, 25, 12, 12, 3, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30 + 17, 18, 19, 17, 17, 27, 21, 18, 20, 21, + 22, 23, 15, 24, 13, 25, 26, 21, 22, 27, + 28, 29, 16, 15, 14, 13, 30, 23, 31, 31, + 31, 31, 26, 12, 12, 3, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31 } ; static const flex_int16_t yy_chk[57] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 10, 10, 10, 10, 32, 27, 22, 19, 10, 11, - 11, 11, 15, 11, 12, 9, 11, 13, 13, 20, - 20, 25, 8, 7, 5, 3, 25, 29, 0, 0, - 0, 0, 29, 31, 31, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30 + 10, 10, 10, 10, 33, 28, 22, 19, 10, 11, + 11, 11, 15, 11, 12, 11, 11, 13, 13, 20, + 20, 26, 9, 8, 7, 5, 26, 30, 3, 0, + 0, 0, 30, 32, 32, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31 } ; /* The intent behind this definition is that it'll catch @@ -930,7 +931,7 @@ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 31 ) + if ( yy_current_state >= 32 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -977,6 +978,13 @@ case 3: YY_RULE_SETUP { + // Unescape the colon. + yyextra->addToCurrentPath(":"); + } + YY_BREAK +case 4: +YY_RULE_SETUP +{ // 2N+1 backslashes plus space -> N backslashes plus space. size_t c = (strlen(yytext) - 1) / 2; std::string s(c, '\\'); @@ -984,7 +992,7 @@ yyextra->addToCurrentPath(s.c_str()); } YY_BREAK -case 4: +case 5: YY_RULE_SETUP { // 2N backslashes plus space -> 2N backslashes, end of filename. @@ -993,24 +1001,24 @@ yyextra->newDependency(); } YY_BREAK -case 5: -/* rule 5 can match eol */ +case 6: +/* rule 6 can match eol */ YY_RULE_SETUP { // A line continuation ends the current file name. yyextra->newRuleOrDependency(); } YY_BREAK -case 6: -/* rule 6 can match eol */ +case 7: +/* rule 7 can match eol */ YY_RULE_SETUP { // A newline ends the current file name and the current rule. yyextra->newEntry(); } YY_BREAK -case 7: -/* rule 7 can match eol */ +case 8: +/* rule 8 can match eol */ YY_RULE_SETUP { // A colon ends the rules @@ -1019,8 +1027,8 @@ yyextra->newEntry(); } YY_BREAK -case 8: -/* rule 8 can match eol */ +case 9: +/* rule 9 can match eol */ YY_RULE_SETUP { // A colon followed by space or line continuation ends the rules @@ -1028,28 +1036,28 @@ yyextra->newDependency(); } YY_BREAK -case 9: +case 10: YY_RULE_SETUP { // Rules and dependencies are separated by blocks of whitespace. yyextra->newRuleOrDependency(); } YY_BREAK -case 10: +case 11: YY_RULE_SETUP { // Got a span of plain text. yyextra->addToCurrentPath(yytext); } YY_BREAK -case 11: +case 12: YY_RULE_SETUP { // Got an otherwise unmatched character. yyextra->addToCurrentPath(yytext); } YY_BREAK -case 12: +case 13: YY_RULE_SETUP ECHO; YY_BREAK @@ -1351,7 +1359,7 @@ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 31 ) + if ( yy_current_state >= 32 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -1380,11 +1388,11 @@ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 31 ) + if ( yy_current_state >= 32 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 30); + yy_is_jam = (yy_current_state == 31); (void)yyg; return yy_is_jam ? 0 : yy_current_state;
diff --git a/Source/LexerParser/cmGccDepfileLexer.in.l b/Source/LexerParser/cmGccDepfileLexer.in.l index 6336b5f..a0a0f22 100644 --- a/Source/LexerParser/cmGccDepfileLexer.in.l +++ b/Source/LexerParser/cmGccDepfileLexer.in.l
@@ -27,6 +27,10 @@ // Unescape the hash. yyextra->addToCurrentPath("#"); } +\\: { + // Unescape the colon. + yyextra->addToCurrentPath(":"); + } (\\\\)*\\[ ] { // 2N+1 backslashes plus space -> N backslashes plus space. size_t c = (strlen(yytext) - 1) / 2;
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index e6e41ec..e4b53fd 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt
@@ -48,6 +48,11 @@ endif() endif() +if(MSVC) + # QTBUG-118993: Qt uses deprecated stdext::checked_array_iterator + add_compile_definitions(_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING) +endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt${INSTALLED_QT_VERSION}Widgets_EXECUTABLE_COMPILE_FLAGS}") if(CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES)
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index 2a6a831..af6cf74 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -350,8 +350,24 @@ if (!this->SourceDirectory->text().isEmpty() && !this->DeferredPreset.isNull()) { this->onSourceDirectoryChanged(this->SourceDirectory->text()); + if (!this->BinaryDirectory->lineEdit()->text().isEmpty()) { + this->onBinaryDirectoryChanged( + this->BinaryDirectory->lineEdit()->text()); + } } else if (!this->SourceDirectory->text().isEmpty() || !this->BinaryDirectory->lineEdit()->text().isEmpty()) { + if (this->SourceDirectory->text().isEmpty() && + !cmSystemTools::FileIsFullPath( + this->BinaryDirectory->lineEdit()->text().toStdString())) { + // If the binary directory is relative, load the previous source path + // from the config + QSettings settings; + settings.beginGroup("Settings/StartPath"); + QString srcDir = settings.value(QString("WhereSource")).toString(); + this->SourceDirectory->blockSignals(true); + this->SourceDirectory->setText(srcDir); + this->SourceDirectory->blockSignals(false); + } this->onSourceDirectoryChanged(this->SourceDirectory->text()); this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text()); } else { @@ -411,8 +427,13 @@ } } - // remember path - this->addBinaryPath(dir.absolutePath()); + // remember paths + this->addBinaryPath( + this->CMakeThread->cmakeInstance()->relativeBinaryDirectory()); + QSettings settings; + settings.beginGroup("Settings/StartPath"); + settings.setValue("WhereSource", + this->CMakeThread->cmakeInstance()->sourceDirectory()); return true; } @@ -742,8 +763,15 @@ void CMakeSetupDialog::doBinaryBrowse() { + QString abs_path = this->BinaryDirectory->currentText(); + if (!cmSystemTools::FileIsFullPath(abs_path.toStdString())) { + if (!this->SourceDirectory->text().endsWith("/")) { + abs_path = "/" + abs_path; + } + abs_path = this->SourceDirectory->text() + abs_path; + } QString dir = QFileDialog::getExistingDirectory( - this, tr("Enter Path to Build"), this->BinaryDirectory->currentText(), + this, tr("Enter Path to Build"), abs_path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (!dir.isEmpty() && dir != this->BinaryDirectory->currentText()) { this->setBinaryDirectory(dir);
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 8d63f6d..45c4717 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx
@@ -94,6 +94,10 @@ emit this->sourceDirChanged(this->SourceDirectory); this->loadPresets(); this->setPreset(QString{}); + if (!cmSystemTools::FileIsFullPath( + this->MaybeRelativeBinaryDirectory.toStdString())) { + this->setBinaryDirectory(this->MaybeRelativeBinaryDirectory); + } } } @@ -101,9 +105,23 @@ { QString dir = QString::fromStdString( cmSystemTools::GetActualCaseForPath(_dir.toStdString())); - if (this->BinaryDirectory != dir) { - this->BinaryDirectory = QDir::fromNativeSeparators(dir); - emit this->binaryDirChanged(this->BinaryDirectory); + + QString absDir = dir; + + if (!cmSystemTools::FileIsFullPath(absDir.toStdString())) { + if (!this->SourceDirectory.isEmpty()) { + if (!this->SourceDirectory.endsWith("/")) { + absDir = "/" + absDir; + } + absDir = this->SourceDirectory + absDir; + } + } + + if (this->BinaryDirectory != absDir || + this->MaybeRelativeBinaryDirectory != dir) { + this->MaybeRelativeBinaryDirectory = QDir::fromNativeSeparators(dir); + this->BinaryDirectory = QDir::fromNativeSeparators(absDir); + emit this->binaryDirChanged(this->MaybeRelativeBinaryDirectory); cmState* state = this->CMakeInstance->GetState(); this->setGenerator(QString()); this->setToolset(QString()); @@ -238,10 +256,16 @@ #ifdef Q_OS_WIN UINT lastErrorMode = SetErrorMode(0); #endif + // Apply the same transformations that the command-line invocation does + auto sanitizePath = [](QString const& value) -> std::string { + std::string path = cmSystemTools::CollapseFullPath(value.toStdString()); + cmSystemTools::ConvertToUnixSlashes(path); + return path; + }; - this->CMakeInstance->SetHomeDirectory(this->SourceDirectory.toStdString()); + this->CMakeInstance->SetHomeDirectory(sanitizePath(this->SourceDirectory)); this->CMakeInstance->SetHomeOutputDirectory( - this->BinaryDirectory.toStdString()); + sanitizePath(this->BinaryDirectory)); this->CMakeInstance->SetGlobalGenerator( this->CMakeInstance->CreateGlobalGenerator( this->Generator.toStdString())); @@ -594,6 +618,11 @@ return this->BinaryDirectory; } +QString QCMake::relativeBinaryDirectory() const +{ + return this->MaybeRelativeBinaryDirectory; +} + QString QCMake::sourceDirectory() const { return this->SourceDirectory;
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index 0890558..d42dac4 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h
@@ -130,6 +130,8 @@ QCMakePropertyList properties() const; /// get the current binary directory QString binaryDirectory() const; + /// get the current binary directory, possibly a relative path + QString relativeBinaryDirectory() const; /// get the current source directory QString sourceDirectory() const; /// get the current generator @@ -196,6 +198,7 @@ bool WarnUninitializedMode; QString SourceDirectory; QString BinaryDirectory; + QString MaybeRelativeBinaryDirectory; QString Generator; QString Platform; QString Toolset;
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index ea97287..d943011 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx
@@ -357,13 +357,18 @@ cc->SetDepends(depends); cc->SetImplicitDepends(implicit_depends); mf.AddCustomCommandToOutput(std::move(cc)); - } else if (!byproducts.empty()) { - status.SetError("BYPRODUCTS may not be specified with SOURCE signatures"); - return false; - } else if (uses_terminal) { - status.SetError("USES_TERMINAL may not be used with SOURCE signatures"); - return false; } else { + if (!byproducts.empty()) { + status.SetError( + "BYPRODUCTS may not be specified with SOURCE signatures"); + return false; + } + + if (uses_terminal) { + status.SetError("USES_TERMINAL may not be used with SOURCE signatures"); + return false; + } + bool issueMessage = true; std::ostringstream e; MessageType messageType = MessageType::AUTHOR_WARNING;
diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index 29fc09b..9113dfd 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx
@@ -11,6 +11,7 @@ #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" #include "cmValue.h" @@ -26,7 +27,7 @@ // Library type defaults to value of BUILD_SHARED_LIBS, if it exists, // otherwise it defaults to static library. cmStateEnums::TargetType type = cmStateEnums::SHARED_LIBRARY; - if (cmIsOff(mf.GetDefinition("BUILD_SHARED_LIBS"))) { + if (mf.GetDefinition("BUILD_SHARED_LIBS").IsOff()) { type = cmStateEnums::STATIC_LIBRARY; } bool excludeFromAll = false; @@ -226,14 +227,35 @@ if ((type == cmStateEnums::SHARED_LIBRARY || type == cmStateEnums::MODULE_LIBRARY) && !mf.GetState()->GetGlobalPropertyAsBool("TARGET_SUPPORTS_SHARED_LIBS")) { - mf.IssueMessage( - MessageType::AUTHOR_WARNING, - cmStrCat( - "ADD_LIBRARY called with ", - (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"), - " option but the target platform does not support dynamic linking. ", - "Building a STATIC library instead. This may lead to problems.")); - type = cmStateEnums::STATIC_LIBRARY; + switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0164)) { + case cmPolicies::WARN: + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat( + "ADD_LIBRARY called with ", + (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"), + " option but the target platform does not support dynamic " + "linking. ", + "Building a STATIC library instead. This may lead to problems.")); + CM_FALLTHROUGH; + case cmPolicies::OLD: + type = cmStateEnums::STATIC_LIBRARY; + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + mf.IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "ADD_LIBRARY called with ", + (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"), + " option but the target platform does not support dynamic " + "linking.")); + cmSystemTools::SetFatalErrorOccurred(); + return false; + default: + break; + } } // Handle imported target creation.
diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h index 7daae09..3e0fae4 100644 --- a/Source/cmArgumentParserTypes.h +++ b/Source/cmArgumentParserTypes.h
@@ -4,7 +4,7 @@ #include "cmConfigure.h" // IWYU pragma: keep -#if defined(__SUNPRO_CC) +#if defined(__SUNPRO_CC) || defined(__EDG__) # include <string> # include <vector>
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx index 90e0891..7db7cb0 100644 --- a/Source/cmBinUtilsMacOSMachOLinker.cxx +++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -126,10 +126,16 @@ this->Archive->AddResolvedPath(filename, path, unique, dep_file_info->rpaths); - if (unique && - !this->ScanDependencies(path, dep_file_info->libs, - dep_file_info->rpaths, executablePath)) { - return false; + if (unique) { + std::vector<std::string> combinedParentRpaths = + dep_file_info->rpaths; + combinedParentRpaths.insert(combinedParentRpaths.end(), + rpaths.begin(), rpaths.end()); + if (!this->ScanDependencies(path, dep_file_info->libs, + combinedParentRpaths, + executablePath)) { + return false; + } } } } else {
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index 9ffc363..fe9257e 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx
@@ -345,27 +345,18 @@ auto const& featureName = expandedArgs[1]; auto const& variableName = expandedArgs[2]; - auto feature = cmExperimental::Feature::Sentinel; - for (std::size_t i = 0; - i < static_cast<std::size_t>(cmExperimental::Feature::Sentinel); i++) { - if (cmExperimental::DataForFeature(static_cast<cmExperimental::Feature>(i)) - .Name == featureName) { - feature = static_cast<cmExperimental::Feature>(i); - break; + if (auto feature = cmExperimental::FeatureByName(featureName)) { + if (cmExperimental::HasSupportEnabled(makefile, *feature)) { + makefile.AddDefinition(variableName, "TRUE"); + } else { + makefile.AddDefinition(variableName, "FALSE"); } - } - if (feature == cmExperimental::Feature::Sentinel) { + } else { return FatalError(status, cmStrCat("Experimental feature name \"", featureName, "\" does not exist.")); } - if (cmExperimental::HasSupportEnabled(makefile, feature)) { - makefile.AddDefinition(variableName, "TRUE"); - } else { - makefile.AddDefinition(variableName, "FALSE"); - } - return true; } }
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index d37c8a4..008f2dd 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx
@@ -10,6 +10,7 @@ #include <iterator> #include <utility> +#include <cm/memory> #include <cm/string_view> #include "cmsys/RegularExpression.hxx" @@ -49,6 +50,11 @@ using PresetPair = cmCMakePresetsGraph::PresetPair<T>; using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult; using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander; +using MacroExpanderVector = cmCMakePresetsGraphInternal::MacroExpanderVector; +using BaseMacroExpander = cmCMakePresetsGraphInternal::BaseMacroExpander; +template <typename T> +using PresetMacroExpander = + cmCMakePresetsGraphInternal::PresetMacroExpander<T>; using cmCMakePresetsGraphInternal::ExpandMacros; void InheritString(std::string& child, const std::string& parent) @@ -203,13 +209,61 @@ } ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status, - const std::vector<MacroExpander>& macroExpanders, + MacroExpanderVector const& macroExpanders, int version); +template <class T> +class EnvironmentMacroExpander : public MacroExpander +{ + std::map<std::string, CycleStatus>& EnvCycles; + cm::optional<T>& Out; + MacroExpanderVector& MacroExpanders; + +public: + EnvironmentMacroExpander(MacroExpanderVector& macroExpanders, + cm::optional<T>& out, + std::map<std::string, CycleStatus>& envCycles) + : EnvCycles(envCycles) + , Out(out) + , MacroExpanders(macroExpanders) + { + } + ExpandMacroResult operator()(const std::string& macroNamespace, + const std::string& macroName, + std::string& macroOut, + int version) const override + { + if (macroNamespace == "env" && !macroName.empty() && Out) { + auto v = Out->Environment.find(macroName); + if (v != Out->Environment.end() && v->second) { + auto e = + VisitEnv(*v->second, EnvCycles[macroName], MacroExpanders, version); + if (e != ExpandMacroResult::Ok) { + return e; + } + macroOut += *v->second; + return ExpandMacroResult::Ok; + } + } + + if (macroNamespace == "env" || macroNamespace == "penv") { + if (macroName.empty()) { + return ExpandMacroResult::Error; + } + if (cm::optional<std::string> value = + cmSystemTools::GetEnvVar(macroName)) { + macroOut += *value; + } + return ExpandMacroResult::Ok; + } + + return ExpandMacroResult::Ignore; + } +}; bool ExpandMacros(const cmCMakePresetsGraph& graph, const ConfigurePreset& preset, cm::optional<ConfigurePreset>& out, - const std::vector<MacroExpander>& macroExpanders) + MacroExpanderVector const& macroExpanders) { std::string binaryDir = preset.BinaryDir; CHECK_EXPAND(out, binaryDir, macroExpanders, graph.GetVersion(preset)); @@ -251,7 +305,7 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const BuildPreset& preset, cm::optional<BuildPreset>& out, - const std::vector<MacroExpander>& macroExpanders) + MacroExpanderVector const& macroExpanders) { for (auto& target : out->Targets) { CHECK_EXPAND(out, target, macroExpanders, graph.GetVersion(preset)); @@ -267,7 +321,7 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset, cm::optional<TestPreset>& out, - const std::vector<MacroExpander>& macroExpanders) + MacroExpanderVector const& macroExpanders) { for (auto& overwrite : out->OverwriteConfigurationFile) { CHECK_EXPAND(out, overwrite, macroExpanders, graph.GetVersion(preset)); @@ -321,7 +375,7 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const PackagePreset& preset, cm::optional<PackagePreset>& out, - const std::vector<MacroExpander>& macroExpanders) + MacroExpanderVector const& macroExpanders) { for (auto& variable : out->Variables) { CHECK_EXPAND(out, variable.second, macroExpanders, @@ -343,7 +397,7 @@ bool ExpandMacros(const cmCMakePresetsGraph& /*graph*/, const WorkflowPreset& /*preset*/, cm::optional<WorkflowPreset>& /*out*/, - const std::vector<MacroExpander>& /*macroExpanders*/) + MacroExpanderVector const& /*macroExpanders*/) { return true; } @@ -359,100 +413,13 @@ envCycles[v.first] = CycleStatus::Unvisited; } - std::vector<MacroExpander> macroExpanders; + MacroExpanderVector macroExpanders{}; - MacroExpander defaultMacroExpander = - [&graph, &preset](const std::string& macroNamespace, - const std::string& macroName, std::string& macroOut, - int version) -> ExpandMacroResult { - if (macroNamespace.empty()) { - if (macroName == "sourceDir") { - macroOut += graph.SourceDir; - return ExpandMacroResult::Ok; - } - if (macroName == "sourceParentDir") { - macroOut += cmSystemTools::GetParentDirectory(graph.SourceDir); - return ExpandMacroResult::Ok; - } - if (macroName == "sourceDirName") { - macroOut += cmSystemTools::GetFilenameName(graph.SourceDir); - return ExpandMacroResult::Ok; - } - if (macroName == "presetName") { - macroOut += preset.Name; - return ExpandMacroResult::Ok; - } - if (macroName == "generator") { - // Generator only makes sense if preset is not hidden. - if (!preset.Hidden) { - macroOut += graph.GetGeneratorForPreset(preset.Name); - } - return ExpandMacroResult::Ok; - } - if (macroName == "dollar") { - macroOut += '$'; - return ExpandMacroResult::Ok; - } - if (macroName == "hostSystemName") { - if (version < 3) { - return ExpandMacroResult::Error; - } - macroOut += cmSystemTools::GetSystemName(); - return ExpandMacroResult::Ok; - } - if (macroName == "fileDir") { - if (version < 4) { - return ExpandMacroResult::Error; - } - macroOut += - cmSystemTools::GetParentDirectory(preset.OriginFile->Filename); - return ExpandMacroResult::Ok; - } - if (macroName == "pathListSep") { - if (version < 5) { - return ExpandMacroResult::Error; - } - macroOut += cmSystemTools::GetSystemPathlistSeparator(); - return ExpandMacroResult::Ok; - } - } - - return ExpandMacroResult::Ignore; - }; - - MacroExpander environmentMacroExpander = - [¯oExpanders, &out, &envCycles]( - const std::string& macroNamespace, const std::string& macroName, - std::string& result, int version) -> ExpandMacroResult { - if (macroNamespace == "env" && !macroName.empty() && out) { - auto v = out->Environment.find(macroName); - if (v != out->Environment.end() && v->second) { - auto e = - VisitEnv(*v->second, envCycles[macroName], macroExpanders, version); - if (e != ExpandMacroResult::Ok) { - return e; - } - result += *v->second; - return ExpandMacroResult::Ok; - } - } - - if (macroNamespace == "env" || macroNamespace == "penv") { - if (macroName.empty()) { - return ExpandMacroResult::Error; - } - if (cm::optional<std::string> value = - cmSystemTools::GetEnvVar(macroName)) { - result += *value; - } - return ExpandMacroResult::Ok; - } - - return ExpandMacroResult::Ignore; - }; - - macroExpanders.push_back(defaultMacroExpander); - macroExpanders.push_back(environmentMacroExpander); + macroExpanders.push_back(cm::make_unique<BaseMacroExpander>(graph)); + macroExpanders.push_back( + 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) { @@ -490,7 +457,7 @@ } ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status, - const std::vector<MacroExpander>& macroExpanders, + MacroExpanderVector const& macroExpanders, int version) { if (status == CycleStatus::Verified) { @@ -511,8 +478,7 @@ } ExpandMacroResult cmCMakePresetsGraphInternal::ExpandMacros( - std::string& out, const std::vector<MacroExpander>& macroExpanders, - int version) + std::string& out, MacroExpanderVector const& macroExpanders, int version) { std::string result; std::string macroNamespace; @@ -591,11 +557,11 @@ ExpandMacroResult cmCMakePresetsGraphInternal::ExpandMacro( std::string& out, const std::string& macroNamespace, - const std::string& macroName, - const std::vector<MacroExpander>& macroExpanders, int version) + const std::string& macroName, MacroExpanderVector const& macroExpanders, + int version) { for (auto const& macroExpander : macroExpanders) { - auto result = macroExpander(macroNamespace, macroName, out, version); + auto result = (*macroExpander)(macroNamespace, macroName, out, version); if (result != ExpandMacroResult::Ignore) { return result; } @@ -651,8 +617,56 @@ } } +ExpandMacroResult BaseMacroExpander::operator()( + const std::string& macroNamespace, const std::string& macroName, + std::string& macroOut, int version) const +{ + if (macroNamespace.empty()) { + if (macroName == "sourceDir") { + macroOut += Graph.SourceDir; + return ExpandMacroResult::Ok; + } + if (macroName == "sourceParentDir") { + macroOut += cmSystemTools::GetParentDirectory(Graph.SourceDir); + return ExpandMacroResult::Ok; + } + if (macroName == "sourceDirName") { + macroOut += cmSystemTools::GetFilenameName(Graph.SourceDir); + return ExpandMacroResult::Ok; + } + if (macroName == "dollar") { + macroOut += '$'; + return ExpandMacroResult::Ok; + } + if (macroName == "hostSystemName") { + if (version < 3) { + return ExpandMacroResult::Error; + } + macroOut += cmSystemTools::GetSystemName(); + return ExpandMacroResult::Ok; + } + // Enable fileDir macro expansion for non-preset expanders + if (macroName == "fileDir" && File) { + if (version < 4) { + return ExpandMacroResult::Error; + } + macroOut += cmSystemTools::GetParentDirectory(File.value()); + return ExpandMacroResult::Ok; + } + if (macroName == "pathListSep") { + if (version < 5) { + return ExpandMacroResult::Error; + } + macroOut += cmSystemTools::GetSystemPathlistSeparator(); + return ExpandMacroResult::Ok; + } + } + + return ExpandMacroResult::Ignore; +} + bool cmCMakePresetsGraphInternal::EqualsCondition::Evaluate( - const std::vector<MacroExpander>& expanders, int version, + MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const { std::string lhs = this->Lhs; @@ -666,7 +680,7 @@ } bool cmCMakePresetsGraphInternal::InListCondition::Evaluate( - const std::vector<MacroExpander>& expanders, int version, + MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const { std::string str = this->String; @@ -685,7 +699,7 @@ } bool cmCMakePresetsGraphInternal::MatchesCondition::Evaluate( - const std::vector<MacroExpander>& expanders, int version, + MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const { std::string str = this->String; @@ -703,7 +717,7 @@ } bool cmCMakePresetsGraphInternal::AnyAllOfCondition::Evaluate( - const std::vector<MacroExpander>& expanders, int version, + MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const { for (auto const& condition : this->Conditions) { @@ -729,7 +743,7 @@ } bool cmCMakePresetsGraphInternal::NotCondition::Evaluate( - const std::vector<MacroExpander>& expanders, int version, + MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const { out.reset();
diff --git a/Source/cmCMakePresetsGraphInternal.h b/Source/cmCMakePresetsGraphInternal.h index 5c76e0e..a67be21 100644 --- a/Source/cmCMakePresetsGraphInternal.h +++ b/Source/cmCMakePresetsGraphInternal.h
@@ -11,6 +11,7 @@ #include "cmCMakePresetsGraph.h" #include "cmJSONHelpers.h" +#include "cmSystemTools.h" #define CHECK_OK(expr) \ do { \ @@ -27,17 +28,25 @@ Error, }; -using MacroExpander = std::function<ExpandMacroResult( - const std::string&, const std::string&, std::string&, int version)>; +class MacroExpander +{ +public: + virtual ExpandMacroResult operator()(const std::string& macroNamespace, + const std::string& macroName, + std::string& macroOut, + int version) const = 0; + virtual ~MacroExpander() = default; +}; +using MacroExpanderVector = std::vector<std::unique_ptr<MacroExpander>>; -ExpandMacroResult ExpandMacros( - std::string& out, const std::vector<MacroExpander>& macroExpanders, - int version); +ExpandMacroResult ExpandMacros(std::string& out, + MacroExpanderVector const& macroExpanders, + int version); ExpandMacroResult ExpandMacro(std::string& out, const std::string& macroNamespace, const std::string& macroName, - const std::vector<MacroExpander>& macroExpanders, + MacroExpanderVector const& macroExpanders, int version); } @@ -47,17 +56,79 @@ virtual ~Condition() = default; virtual bool Evaluate( - const std::vector<cmCMakePresetsGraphInternal::MacroExpander>& expanders, + const cmCMakePresetsGraphInternal::MacroExpanderVector& expanders, int version, cm::optional<bool>& out) const = 0; virtual bool IsNull() const { return false; } }; namespace cmCMakePresetsGraphInternal { +class BaseMacroExpander : public MacroExpander +{ + cmCMakePresetsGraph const& Graph; + cm::optional<std::string> File; + +public: + BaseMacroExpander(const cmCMakePresetsGraph& graph) + : Graph(graph) + { + } + BaseMacroExpander(const cmCMakePresetsGraph& graph, const std::string& file) + : Graph(graph) + , File(file) + { + } + ExpandMacroResult operator()(const std::string& macroNamespace, + const std::string& macroName, + std::string& macroOut, + int version) const override; +}; + +template <class T> +class PresetMacroExpander : public MacroExpander +{ + cmCMakePresetsGraph const& Graph; + T const& Preset; + +public: + PresetMacroExpander(const cmCMakePresetsGraph& graph, const T& preset) + : Graph(graph) + , Preset(preset) + { + } + ExpandMacroResult operator()(const std::string& macroNamespace, + const std::string& macroName, + std::string& macroOut, + int version) const override + { + if (macroNamespace.empty()) { + if (macroName == "presetName") { + macroOut += Preset.Name; + return ExpandMacroResult::Ok; + } + if (macroName == "generator") { + // Generator only makes sense if preset is not hidden. + if (!Preset.Hidden) { + macroOut += Graph.GetGeneratorForPreset(Preset.Name); + } + return ExpandMacroResult::Ok; + } + if (macroName == "fileDir") { + if (version < 4) { + return ExpandMacroResult::Error; + } + macroOut += + cmSystemTools::GetParentDirectory(Preset.OriginFile->Filename); + return ExpandMacroResult::Ok; + } + } + return ExpandMacroResult::Ignore; + } +}; class NullCondition : public cmCMakePresetsGraph::Condition { - bool Evaluate(const std::vector<MacroExpander>& /*expanders*/, - int /*version*/, cm::optional<bool>& out) const override + bool Evaluate(MacroExpanderVector const& /*expanders*/, int /*version*/, + cm::optional<bool>& out) const override { out = true; return true; @@ -69,8 +140,8 @@ class ConstCondition : public cmCMakePresetsGraph::Condition { public: - bool Evaluate(const std::vector<MacroExpander>& /*expanders*/, - int /*version*/, cm::optional<bool>& out) const override + bool Evaluate(MacroExpanderVector const& /*expanders*/, int /*version*/, + cm::optional<bool>& out) const override { out = this->Value; return true; @@ -82,7 +153,7 @@ class EqualsCondition : public cmCMakePresetsGraph::Condition { public: - bool Evaluate(const std::vector<MacroExpander>& expanders, int version, + bool Evaluate(MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const override; std::string Lhs; @@ -92,7 +163,7 @@ class InListCondition : public cmCMakePresetsGraph::Condition { public: - bool Evaluate(const std::vector<MacroExpander>& expanders, int version, + bool Evaluate(MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const override; std::string String; @@ -102,7 +173,7 @@ class MatchesCondition : public cmCMakePresetsGraph::Condition { public: - bool Evaluate(const std::vector<MacroExpander>& expanders, int version, + bool Evaluate(MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const override; std::string String; @@ -112,7 +183,7 @@ class AnyAllOfCondition : public cmCMakePresetsGraph::Condition { public: - bool Evaluate(const std::vector<MacroExpander>& expanders, int version, + bool Evaluate(MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const override; std::vector<std::unique_ptr<Condition>> Conditions; @@ -122,7 +193,7 @@ class NotCondition : public cmCMakePresetsGraph::Condition { public: - bool Evaluate(const std::vector<MacroExpander>& expanders, int version, + bool Evaluate(MacroExpanderVector const& expanders, int version, cm::optional<bool>& out) const override; std::unique_ptr<Condition> SubCondition;
diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index 0d8ec63..1cf9500 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx
@@ -35,10 +35,12 @@ using JSONHelperBuilder = cmJSONHelperBuilder; using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult; using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander; +using MacroExpanderVector = cmCMakePresetsGraphInternal::MacroExpanderVector; +using cmCMakePresetsGraphInternal::BaseMacroExpander; using cmCMakePresetsGraphInternal::ExpandMacros; constexpr int MIN_VERSION = 1; -constexpr int MAX_VERSION = 8; +constexpr int MAX_VERSION = 9; struct CMakeVersion { @@ -297,6 +299,29 @@ false) .Bind<std::nullptr_t>("$schema"_s, nullptr, cmCMakePresetsGraphInternal::SchemaHelper(), false); + +class EnvironmentMacroExpander : public MacroExpander +{ +public: + ExpandMacroResult operator()(const std::string& macroNamespace, + const std::string& macroName, + std::string& macroOut, + int /*version*/) const override + { + if (macroNamespace == "penv") { + if (macroName.empty()) { + return ExpandMacroResult::Error; + } + if (cm::optional<std::string> value = + cmSystemTools::GetEnvVar(macroName)) { + macroOut += *value; + } + return ExpandMacroResult::Ok; + } + + return ExpandMacroResult::Ignore; + } +}; } namespace cmCMakePresetsGraphInternal { @@ -706,26 +731,13 @@ return true; }; - std::vector<MacroExpander> macroExpanders; + MacroExpanderVector macroExpanders{}; - MacroExpander environmentMacroExpander = - [](const std::string& macroNamespace, const std::string& macroName, - std::string& expanded, int /*version*/) -> ExpandMacroResult { - if (macroNamespace == "penv") { - if (macroName.empty()) { - return ExpandMacroResult::Error; - } - if (cm::optional<std::string> value = - cmSystemTools::GetEnvVar(macroName)) { - expanded += *value; - } - return ExpandMacroResult::Ok; - } - - return ExpandMacroResult::Ignore; - }; - - macroExpanders.push_back(environmentMacroExpander); + if (v >= 9) { + macroExpanders.push_back( + cm::make_unique<BaseMacroExpander>(*this, filename)); + } + macroExpanders.push_back(cm::make_unique<EnvironmentMacroExpander>()); for (Json::ArrayIndex i = 0; i < presets.Include.size(); ++i) { auto include = presets.Include[i];
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 1ae3cb5..e1aefd9 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx
@@ -760,7 +760,9 @@ } if (!this->GetCTestConfiguration("BuildDirectory").empty()) { this->Impl->BinaryDir = this->GetCTestConfiguration("BuildDirectory"); - cmSystemTools::ChangeDirectory(this->Impl->BinaryDir); + if (this->Impl->TestDir.empty()) { + cmSystemTools::ChangeDirectory(this->Impl->BinaryDir); + } } this->Impl->TimeOut = std::chrono::seconds(atoi(this->GetCTestConfiguration("TimeOut").c_str())); @@ -1890,6 +1892,7 @@ std::string& errormsg) { std::string arg = args[i]; + cm::string_view noTestsPrefix = "--no-tests="; if (this->CheckArgument(arg, "-F"_s)) { this->Impl->Failover = true; } else if (this->CheckArgument(arg, "-j"_s, "--parallel")) { @@ -2151,8 +2154,7 @@ this->SetOutputJUnitFileName(std::string(args[i])); } - cm::string_view noTestsPrefix = "--no-tests="; - if (cmHasPrefix(arg, noTestsPrefix)) { + else if (cmHasPrefix(arg, noTestsPrefix)) { cm::string_view noTestsMode = cm::string_view(arg).substr(noTestsPrefix.length()); if (noTestsMode == "error") { @@ -2260,6 +2262,8 @@ else if (this->CheckArgument(arg, "--rerun-failed"_s)) { this->GetTestHandler()->SetPersistentOption("RerunFailed", "true"); this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true"); + } else { + return false; } return true; } @@ -2309,7 +2313,7 @@ } // handle the -S -SR and -SP arguments -void cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args, +bool cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args, bool& SRArgumentSpecified) { std::string arg = args[i]; @@ -2324,8 +2328,8 @@ } } - if (this->CheckArgument(arg, "-SR"_s, "--script-run") && - i < args.size() - 1) { + else if (this->CheckArgument(arg, "-SR"_s, "--script-run") && + i < args.size() - 1) { SRArgumentSpecified = true; this->Impl->RunConfigurationScript = true; i++; @@ -2333,7 +2337,8 @@ ch->AddConfigurationScript(args[i], true); } - if (this->CheckArgument(arg, "-S"_s, "--script") && i < args.size() - 1) { + else if (this->CheckArgument(arg, "-S"_s, "--script") && + i < args.size() - 1) { this->Impl->RunConfigurationScript = true; i++; cmCTestScriptHandler* ch = this->GetScriptHandler(); @@ -2341,7 +2346,10 @@ if (!SRArgumentSpecified) { ch->AddConfigurationScript(args[i], true); } + } else { + return false; } + return true; } bool cmCTest::AddVariableDefinition(const std::string& arg) @@ -2734,16 +2742,18 @@ for (size_t i = 1; i < args.size(); ++i) { // handle the simple commandline arguments std::string errormsg; - if (!this->HandleCommandLineArguments(i, args, errormsg)) { + bool validArg = this->HandleCommandLineArguments(i, args, errormsg); + if (!validArg && !errormsg.empty()) { cmSystemTools::Error(errormsg); return 1; } + std::string arg = args[i]; // handle the script arguments -S -SR -SP - this->HandleScriptArguments(i, args, SRArgumentSpecified); + validArg = + validArg || this->HandleScriptArguments(i, args, SRArgumentSpecified); // --dashboard: handle a request for a dashboard - std::string arg = args[i]; if (this->CheckArgument(arg, "-D"_s, "--dashboard") && i < args.size() - 1) { this->Impl->ProduceXML = true; @@ -2757,6 +2767,7 @@ executeTests = false; } } + validArg = true; } // If it's not exactly -D, but it starts with -D, then try to parse out @@ -2767,16 +2778,17 @@ if (arg != "-D" && cmHasLiteralPrefix(arg, "-D")) { std::string input = arg.substr(2); this->AddVariableDefinition(input); + validArg = true; } // --test-action: calls SetTest(<stage>, /*report=*/ false) to enable // the corresponding stage - if (!this->HandleTestActionArgument(ctestExec, i, args)) { + if (!this->HandleTestActionArgument(ctestExec, i, args, validArg)) { executeTests = false; } // --test-model: what type of test model - if (!this->HandleTestModelArgument(ctestExec, i, args)) { + if (!this->HandleTestModelArgument(ctestExec, i, args, validArg)) { executeTests = false; } @@ -2788,30 +2800,40 @@ if (!this->SubmitExtraFiles(args[i])) { return 0; } + validArg = true; } // --build-and-test options if (this->CheckArgument(arg, "--build-and-test"_s) && i < args.size() - 1) { cmakeAndTest = true; + validArg = true; } // --schedule-random if (this->CheckArgument(arg, "--schedule-random"_s)) { this->Impl->ScheduleType = "Random"; + validArg = true; } // pass the argument to all the handlers as well, but it may no longer be // set to what it was originally so I'm not sure this is working as // intended for (auto& handler : this->Impl->GetTestingHandlers()) { - if (!handler->ProcessCommandLineArguments(arg, i, args)) { + if (!handler->ProcessCommandLineArguments(arg, i, args, validArg)) { cmCTestLog( this, ERROR_MESSAGE, "Problem parsing command line arguments within a handler\n"); return 0; } } + + if (!validArg && cmHasLiteralPrefix(arg, "-") && + !cmHasLiteralPrefix(arg, "--preset")) { + cmSystemTools::Error(cmStrCat("Unknown argument: ", arg)); + cmSystemTools::Error("Run 'ctest --help' for all supported options."); + return 1; + } } // the close of the for argument loop // handle CTEST_PARALLEL_LEVEL environment variable @@ -2877,12 +2899,14 @@ } bool cmCTest::HandleTestActionArgument(const char* ctestExec, size_t& i, - const std::vector<std::string>& args) + const std::vector<std::string>& args, + bool& validArg) { bool success = true; std::string const& arg = args[i]; if (this->CheckArgument(arg, "-T"_s, "--test-action") && (i < args.size() - 1)) { + validArg = true; this->Impl->ProduceXML = true; i++; if (!this->SetTest(args[i], false)) { @@ -2909,12 +2933,14 @@ } bool cmCTest::HandleTestModelArgument(const char* ctestExec, size_t& i, - const std::vector<std::string>& args) + const std::vector<std::string>& args, + bool& validArg) { bool success = true; std::string const& arg = args[i]; if (this->CheckArgument(arg, "-M"_s, "--test-model") && (i < args.size() - 1)) { + validArg = true; i++; std::string const& str = args[i]; if (cmSystemTools::LowerCase(str) == "nightly"_s) {
diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 2ab810c..ae2431c 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h
@@ -512,7 +512,7 @@ static bool ColoredOutputSupportedByConsole(); /** handle the -S -SP and -SR arguments */ - void HandleScriptArguments(size_t& i, std::vector<std::string>& args, + bool HandleScriptArguments(size_t& i, std::vector<std::string>& args, bool& SRArgumentSpecified); /** Reread the configuration file */ @@ -531,11 +531,13 @@ /** Handle the --test-action command line argument */ bool HandleTestActionArgument(const char* ctestExec, size_t& i, - const std::vector<std::string>& args); + const std::vector<std::string>& args, + bool& validArg); /** Handle the --test-model command line argument */ bool HandleTestModelArgument(const char* ctestExec, size_t& i, - const std::vector<std::string>& args); + const std::vector<std::string>& args, + bool& validArg); int RunCMakeAndTest(std::string* output); int ExecuteTests();
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 5c25cb9..51b2300 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx
@@ -577,7 +577,7 @@ bool cmCacheManager::CacheEntry::GetPropertyAsBool( const std::string& prop) const { - return cmIsOn(this->GetProperty(prop)); + return this->GetProperty(prop).IsOn(); } void cmCacheManager::CacheEntry::SetProperty(const std::string& prop,
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 90f0d55..9f25904 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx
@@ -295,7 +295,7 @@ if (this->GeneratorTarget->IsAIX()) { if (cmValue exportAll = this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) { - if (cmIsOff(*exportAll)) { + if (exportAll.IsOff()) { aixExports = "-n"; } } @@ -500,8 +500,9 @@ std::string propName = lang + "_LINKER_LAUNCHER"; cmValue launcherProp = this->GeneratorTarget->GetProperty(propName); if (cmNonempty(launcherProp)) { - cmGeneratorExpressionDAGChecker dagChecker(this->GeneratorTarget, propName, - nullptr, nullptr); + cmGeneratorExpressionDAGChecker dagChecker( + this->GeneratorTarget, propName, nullptr, nullptr, + this->LocalCommonGenerator, config); std::string evaluatedLinklauncher = cmGeneratorExpression::Evaluate( *launcherProp, this->LocalCommonGenerator, config, this->GeneratorTarget, &dagChecker, this->GeneratorTarget, lang);
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index f4b26f3..1937674 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx
@@ -30,6 +30,7 @@ #include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" +#include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" @@ -201,6 +202,117 @@ return makefile->GetDefinition(featureSupported).IsOn(); } +// LINK_LIBRARY feature attributes management +struct LinkLibraryFeatureAttributeSet +{ + std::set<cmStateEnums::TargetType> LibraryTypes = { + cmStateEnums::EXECUTABLE, cmStateEnums::STATIC_LIBRARY, + cmStateEnums::SHARED_LIBRARY, cmStateEnums::MODULE_LIBRARY, + cmStateEnums::UNKNOWN_LIBRARY + }; + std::set<std::string> Override; + + enum DeduplicationKind + { + Default, + Yes, + No + }; + DeduplicationKind Deduplication = Default; +}; +std::map<std::string, LinkLibraryFeatureAttributeSet> + LinkLibraryFeatureAttributes; +const LinkLibraryFeatureAttributeSet& GetLinkLibraryFeatureAttributes( + cmMakefile* makefile, std::string const& linkLanguage, + const std::string& feature) +{ + auto it = LinkLibraryFeatureAttributes.find(feature); + if (it != LinkLibraryFeatureAttributes.end()) { + return it->second; + } + + auto featureAttributesVariable = + cmStrCat("CMAKE_", linkLanguage, "_LINK_LIBRARY_", feature, "_ATTRIBUTES"); + auto featureAttributesValues = + makefile->GetDefinition(featureAttributesVariable); + if (featureAttributesValues.IsEmpty()) { + // try language agnostic definition + featureAttributesVariable = + cmStrCat("CMAKE_LINK_LIBRARY_", feature, "_ATTRIBUTES"); + featureAttributesValues = + makefile->GetDefinition(featureAttributesVariable); + } + if (!featureAttributesValues.IsEmpty()) { + LinkLibraryFeatureAttributeSet featureAttributes; + cmsys::RegularExpression processingOption{ + "^(LIBRARY_TYPE|DEDUPLICATION|OVERRIDE)=((STATIC|SHARED|MODULE|" + "EXECUTABLE)(,(" + "STATIC|" + "SHARED|MODULE|EXECUTABLE)" + ")*|YES|NO|DEFAULT|[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*)$" + }; + std::string errorMessage; + for (auto const& option : cmList{ featureAttributesValues }) { + if (processingOption.find(option)) { + if (processingOption.match(1) == "LIBRARY_TYPE") { + featureAttributes.LibraryTypes.clear(); + for (auto const& value : + cmTokenize(processingOption.match(2), ","_s)) { + if (value == "STATIC") { + featureAttributes.LibraryTypes.emplace( + cmStateEnums::STATIC_LIBRARY); + } else if (value == "SHARED") { + featureAttributes.LibraryTypes.emplace( + cmStateEnums::SHARED_LIBRARY); + } else if (value == "MODULE") { + featureAttributes.LibraryTypes.emplace( + cmStateEnums::MODULE_LIBRARY); + } else if (value == "EXECUTABLE") { + featureAttributes.LibraryTypes.emplace(cmStateEnums::EXECUTABLE); + } else { + errorMessage += cmStrCat(" ", option, '\n'); + break; + } + } + // Always add UNKNOWN type + featureAttributes.LibraryTypes.emplace( + cmStateEnums::UNKNOWN_LIBRARY); + } else if (processingOption.match(1) == "DEDUPLICATION") { + if (processingOption.match(2) == "YES") { + featureAttributes.Deduplication = + LinkLibraryFeatureAttributeSet::Yes; + } else if (processingOption.match(2) == "NO") { + featureAttributes.Deduplication = + LinkLibraryFeatureAttributeSet::No; + } else if (processingOption.match(2) == "DEFAULT") { + featureAttributes.Deduplication = + LinkLibraryFeatureAttributeSet::Default; + } else { + errorMessage += cmStrCat(" ", option, '\n'); + } + } else if (processingOption.match(1) == "OVERRIDE") { + featureAttributes.Override.clear(); + auto values = cmTokenize(processingOption.match(2), ","_s); + featureAttributes.Override.insert(values.begin(), values.end()); + } + } else { + errorMessage += cmStrCat(" ", option, '\n'); + } + } + if (!errorMessage.empty()) { + makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Erroneous option(s) for '", featureAttributesVariable, + "':\n", errorMessage)); + } + return LinkLibraryFeatureAttributes.emplace(feature, featureAttributes) + .first->second; + } + return LinkLibraryFeatureAttributes + .emplace(feature, LinkLibraryFeatureAttributeSet{}) + .first->second; +} + // LINK_GROUP helpers const auto LG_BEGIN = "<LINK_GROUP:"_s; const auto LG_END = "</LINK_GROUP:"_s; @@ -235,7 +347,9 @@ EntriesProcessing(const cmGeneratorTarget* target, const std::string& linkLanguage, EntryVector& entries, EntryVector& finalEntries) - : Entries(entries) + : Target(target) + , LinkLanguage(linkLanguage) + , Entries(entries) , FinalEntries(finalEntries) { const auto* makefile = target->Makefile; @@ -266,8 +380,10 @@ case cmPolicies::NEW: { if (auto libProcessing = makefile->GetDefinition(cmStrCat( "CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) { + // UNICITY keyword is just for compatibility with previous + // implementation cmsys::RegularExpression processingOption{ - "^(ORDER|UNICITY)=(FORWARD|REVERSE|ALL|NONE|SHARED)$" + "^(ORDER|UNICITY|DEDUPLICATION)=(FORWARD|REVERSE|ALL|NONE|SHARED)$" }; std::string errorMessage; for (auto const& option : cmList{ libProcessing }) { @@ -280,13 +396,14 @@ } else { errorMessage += cmStrCat(" ", option, '\n'); } - } else if (processingOption.match(1) == "UNICITY") { + } else if (processingOption.match(1) == "UNICITY" || + processingOption.match(1) == "DEDUPLICATION") { if (processingOption.match(2) == "ALL") { - this->Unicity = All; + this->Deduplication = All; } else if (processingOption.match(2) == "NONE") { - this->Unicity = None; + this->Deduplication = None; } else if (processingOption.match(2) == "SHARED") { - this->Unicity = Shared; + this->Deduplication = Shared; } else { errorMessage += cmStrCat(" ", option, '\n'); } @@ -389,7 +506,7 @@ Reverse }; - enum UnicityKind + enum DeduplicationKind { None, Shared, @@ -398,11 +515,24 @@ bool IncludeEntry(LinkEntry const& entry) const { - return this->Unicity == None || - (this->Unicity == Shared && + if (entry.Feature != cmComputeLinkDepends::LinkEntry::DEFAULT) { + auto const& featureAttributes = GetLinkLibraryFeatureAttributes( + this->Target->Makefile, this->LinkLanguage, entry.Feature); + if ((entry.Target == nullptr || + featureAttributes.LibraryTypes.find(entry.Target->GetType()) != + featureAttributes.LibraryTypes.end()) && + featureAttributes.Deduplication != + LinkLibraryFeatureAttributeSet::Default) { + return featureAttributes.Deduplication == + LinkLibraryFeatureAttributeSet::No; + } + } + + return this->Deduplication == None || + (this->Deduplication == Shared && (entry.Target == nullptr || entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) || - (this->Unicity == All && entry.Kind != LinkEntry::Library); + (this->Deduplication == All && entry.Kind != LinkEntry::Library); } template <typename Range> @@ -417,7 +547,9 @@ } OrderKind Order = Reverse; - UnicityKind Unicity = Shared; + DeduplicationKind Deduplication = Shared; + const cmGeneratorTarget* Target; + const std::string& LinkLanguage; EntryVector& Entries; EntryVector& FinalEntries; std::set<size_t> Emitted; @@ -451,10 +583,15 @@ if (cmValue feature = this->Target->GetProperty(key)) { if (!feature->empty() && key.length() > lloPrefix.length()) { auto item = key.substr(lloPrefix.length()); - cmGeneratorExpressionDAGChecker dag{ this->Target->GetBacktrace(), - this->Target, - "LINK_LIBRARY_OVERRIDE", - nullptr, nullptr }; + cmGeneratorExpressionDAGChecker dag{ + this->Target->GetBacktrace(), + this->Target, + "LINK_LIBRARY_OVERRIDE", + nullptr, + nullptr, + this->Target->GetLocalGenerator(), + config + }; auto overrideFeature = cmGeneratorExpression::Evaluate( *feature, this->Target->GetLocalGenerator(), config, this->Target, &dag, this->Target, linkLanguage); @@ -466,9 +603,13 @@ // global override property if (cmValue linkLibraryOverride = this->Target->GetProperty("LINK_LIBRARY_OVERRIDE")) { - cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, - "LINK_LIBRARY_OVERRIDE", nullptr, - nullptr }; + cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), + target, + "LINK_LIBRARY_OVERRIDE", + nullptr, + nullptr, + target->GetLocalGenerator(), + config }; auto overrideValue = cmGeneratorExpression::Evaluate( *linkLibraryOverride, target->GetLocalGenerator(), config, target, &dag, target, linkLanguage); @@ -847,7 +988,7 @@ { // Add direct link dependencies in this configuration. cmLinkImplementation const* impl = this->Target->GetLinkImplementation( - this->Config, cmGeneratorTarget::LinkInterfaceFor::Link); + this->Config, cmGeneratorTarget::UseTo::Link); this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT, impl->Libraries); this->AddLinkObjects(impl->Objects); @@ -966,12 +1107,14 @@ auto ale = this->AddLinkEntry(item, groupIndex.first); dependee_index = ale.first; LinkEntry& entry = this->EntryList[dependee_index]; + bool supportedItem = true; auto const& itemFeature = this->GetCurrentFeature(entry.Item.Value, item.Feature); if (inGroup && ale.second && entry.Target != nullptr && (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY || entry.Target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY)) { + supportedItem = false; const auto& groupFeature = this->EntryList[groupIndex.first].Feature; this->CMakeInstance->IssueMessage( MessageType::AUTHOR_WARNING, @@ -986,32 +1129,31 @@ " library '", entry.Item.Value, "'."), this->Target->GetBacktrace()); } - if (ale.second) { - // current item not yet defined - if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr && - (entry.Target->GetType() == - cmStateEnums::TargetType::OBJECT_LIBRARY || - entry.Target->GetType() == - cmStateEnums::TargetType::INTERFACE_LIBRARY)) { + // check if feature is applicable to this item + if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr) { + auto const& featureAttributes = GetLinkLibraryFeatureAttributes( + this->Makefile, this->LinkLanguage, itemFeature); + if (featureAttributes.LibraryTypes.find(entry.Target->GetType()) == + featureAttributes.LibraryTypes.end()) { + supportedItem = false; this->CMakeInstance->IssueMessage( MessageType::AUTHOR_WARNING, cmStrCat("The feature '", itemFeature, "', specified as part of a generator-expression " "'$<LINK_LIBRARY:", itemFeature, ">', will not be applied to the ", - (entry.Target->GetType() == - cmStateEnums::TargetType::OBJECT_LIBRARY - ? "OBJECT" - : "INTERFACE"), - " library '", entry.Item.Value, "'."), + cmState::GetTargetTypeName(entry.Target->GetType()), " '", + entry.Item.Value, "'."), this->Target->GetBacktrace()); } - entry.Feature = itemFeature; } - - bool supportedItem = entry.Target == nullptr || - (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY && - entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY); + if (ale.second) { + // current item not yet defined + entry.Feature = itemFeature; + if (!supportedItem) { + entry.Feature = LinkEntry::DEFAULT; + } + } if (supportedItem) { if (inGroup) { @@ -1036,21 +1178,53 @@ } } if (entry.Feature != itemFeature) { - // incompatibles features occurred - this->CMakeInstance->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Impossible to link target '", this->Target->GetName(), - "' because the link item '", entry.Item.Value, - "', specified ", - (itemFeature == LinkEntry::DEFAULT - ? "without any feature or 'DEFAULT' feature" - : cmStrCat("with the feature '", itemFeature, '\'')), - ", has already occurred ", - (entry.Feature == LinkEntry::DEFAULT - ? "without any feature or 'DEFAULT' feature" - : cmStrCat("with the feature '", entry.Feature, '\'')), - ", which is not allowed."), - this->Target->GetBacktrace()); + bool incompatibleFeatures = true; + // check if an override is possible + auto const& entryFeatureAttributes = GetLinkLibraryFeatureAttributes( + this->Makefile, this->LinkLanguage, entry.Feature); + auto const& itemFeatureAttributes = GetLinkLibraryFeatureAttributes( + this->Makefile, this->LinkLanguage, itemFeature); + if (itemFeatureAttributes.Override.find(entry.Feature) != + itemFeatureAttributes.Override.end() && + entryFeatureAttributes.Override.find(itemFeature) != + entryFeatureAttributes.Override.end()) { + // features override each other + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Impossible to link target '", this->Target->GetName(), + "' because the link item '", entry.Item.Value, + "' is specified with the features '", itemFeature, + "' and '", entry.Feature, "'", + ", and both have an 'OVERRIDE' attribute that overrides " + "the other. Such cycles are not allowed."), + this->Target->GetBacktrace()); + } else { + if (itemFeatureAttributes.Override.find(entry.Feature) != + itemFeatureAttributes.Override.end()) { + entry.Feature = itemFeature; + incompatibleFeatures = false; + } else if (entryFeatureAttributes.Override.find(itemFeature) != + entryFeatureAttributes.Override.end()) { + incompatibleFeatures = false; + } + if (incompatibleFeatures) { + // incompatibles features occurred + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Impossible to link target '", this->Target->GetName(), + "' because the link item '", entry.Item.Value, "', specified ", + (itemFeature == LinkEntry::DEFAULT + ? "without any feature or 'DEFAULT' feature" + : cmStrCat("with the feature '", itemFeature, '\'')), + ", has already occurred ", + (entry.Feature == LinkEntry::DEFAULT + ? "without any feature or 'DEFAULT' feature" + : cmStrCat("with the feature '", entry.Feature, '\'')), + ", which is not allowed."), + this->Target->GetBacktrace()); + } + } } }
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index c985767..b631610 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx
@@ -646,7 +646,7 @@ // Restore the target link type so the correct system runtime // libraries are found. cmValue lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC"); - if (cmIsOn(lss)) { + if (lss.IsOn()) { this->SetCurrentLinkType(LinkStatic); } else { this->SetCurrentLinkType(this->StartLinkType); @@ -1451,7 +1451,7 @@ // Lookup the starting link type from the target (linked statically?). cmValue lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC"); - this->StartLinkType = cmIsOn(lss) ? LinkStatic : LinkShared; + this->StartLinkType = lss.IsOn() ? LinkStatic : LinkShared; this->CurrentLinkType = this->StartLinkType; }
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index 6763225..e2faf94 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx
@@ -220,7 +220,7 @@ emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); if (cmLinkImplementation const* impl = depender->GetLinkImplementation( - it, cmGeneratorTarget::LinkInterfaceFor::Link)) { + it, cmGeneratorTarget::UseTo::Link)) { for (cmLinkImplItem const& lib : impl->Libraries) { // Don't emit the same library twice for this target. if (emitted.insert(lib).second) { @@ -473,7 +473,7 @@ } else { if (cmValue optimizeDependencies = gt->GetProperty("OPTIMIZE_DEPENDENCIES")) { - if (cmIsOn(optimizeDependencies)) { + if (optimizeDependencies.IsOn()) { this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges); } else { intermediateEdges = initialEdges;
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx index eba4c57..5a2b33a 100644 --- a/Source/cmConditionEvaluator.cxx +++ b/Source/cmConditionEvaluator.cxx
@@ -399,7 +399,7 @@ // Check definition. cmValue def = this->GetDefinitionIfUnquoted(arg); - return !cmIsOff(def); + return !def.IsOff(); } //========================================================================= @@ -416,14 +416,14 @@ return true; } cmValue def = this->GetDefinitionIfUnquoted(arg); - return !cmIsOff(def); + return !def.IsOff(); } // Old GetVariableOrNumber behavior. cmValue def = this->GetDefinitionIfUnquoted(arg); if (!def && std::atoi(arg.GetValue().c_str())) { def = cmValue(arg.GetValue()); } - return !cmIsOff(def); + return !def.IsOff(); } //=========================================================================
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 324c5ad..4d739ff 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx
@@ -1151,6 +1151,7 @@ vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s); vars.emplace("CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS"_s); + vars.emplace("CMAKE_VS_USE_DEBUG_LIBRARIES"_s); if (cmValue varListStr = this->Makefile->GetDefinition( kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) { @@ -1239,6 +1240,17 @@ } } + if (!this->SrcFileSignature && + this->Makefile->GetState()->GetGlobalPropertyAsBool( + "PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE")) { + const std::string var = "CMAKE_PROJECT_TOP_LEVEL_INCLUDES"; + if (cmValue val = this->Makefile->GetDefinition(var)) { + std::string flag = cmStrCat("-D", var, "=\'", *val, '\''); + arguments.CMakeFlags.emplace_back(std::move(flag)); + cmakeVariables.emplace(var, *val); + } + } + if (this->Makefile->GetState()->UseGhsMultiIDE()) { // Forward the GHS variables to the inner project cache. for (std::string const& var : ghs_platform_vars) {
diff --git a/Source/cmCryptoHash.cxx b/Source/cmCryptoHash.cxx index ff9ab0f..55e3a5b 100644 --- a/Source/cmCryptoHash.cxx +++ b/Source/cmCryptoHash.cxx
@@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCryptoHash.h" +#include <cassert> + #include <cm/memory> #include <cm3p/kwiml/int.h> @@ -80,6 +82,37 @@ return std::unique_ptr<cmCryptoHash>(nullptr); } +std::string cmCryptoHash::GetHashAlgoName() const +{ +#ifndef CMAKE_USE_SYSTEM_LIBRHASH + static_assert(RHASH_HASH_COUNT == 10, "Update switch statement!"); +#endif + switch (this->Id) { + case RHASH_MD5: + return "MD5"; + case RHASH_SHA1: + return "SHA1"; + case RHASH_SHA224: + return "SHA224"; + case RHASH_SHA256: + return "SHA256"; + case RHASH_SHA384: + return "SHA384"; + case RHASH_SHA512: + return "SHA512"; + case RHASH_SHA3_224: + return "SHA3_224"; + case RHASH_SHA3_256: + return "SHA3_256"; + case RHASH_SHA3_384: + return "SHA3_384"; + case RHASH_SHA3_512: + return "SHA3_512"; + } + assert(false); + return "UNKNOWN"; +} + bool cmCryptoHash::IntFromHexDigit(char input, char& output) { if (input >= '0' && input <= '9') {
diff --git a/Source/cmCryptoHash.h b/Source/cmCryptoHash.h index a2d45e7..bb026a2 100644 --- a/Source/cmCryptoHash.h +++ b/Source/cmCryptoHash.h
@@ -74,6 +74,10 @@ /// An empty string otherwise. std::string HashFile(const std::string& file); + /// @brief Returns the name of the hash type. + /// @return The name of the hash type associated with this hash generator. + std::string GetHashAlgoName() const; + void Initialize(); void Append(void const*, size_t); void Append(cm::string_view input);
diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx index 24ba368..51b95c3 100644 --- a/Source/cmCurl.cxx +++ b/Source/cmCurl.cxx
@@ -2,6 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCurl.h" +#include <cm/string_view> +#include <cmext/string_view> + #if !defined(CMAKE_USE_SYSTEM_CURL) && !defined(_WIN32) && \ !defined(__APPLE__) && !defined(CURL_CA_BUNDLE) && !defined(CURL_CA_PATH) # define CMAKE_FIND_CAFILE @@ -31,6 +34,83 @@ } \ } while (false) +// curl versions before 7.52.0 did not provide TLS 1.3 support +#if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x073400 +# define CURL_SSLVERSION_TLSv1_3 CURL_SSLVERSION_LAST +#endif + +// curl versions before 7.64.1 referred to Secure Transport as DarwinSSL +#if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x074001 +# define CURLSSLBACKEND_SECURETRANSPORT CURLSSLBACKEND_DARWINSSL +#endif + +// Make sure we keep up with new TLS versions supported by curl. +// Do this only for our vendored curl to avoid breaking builds +// against external future versions of curl. +#if !defined(CMAKE_USE_SYSTEM_CURL) +static_assert(CURL_SSLVERSION_LAST == 8, + "A new CURL_SSLVERSION_ may be available!"); +#endif + +void cmCurlInitOnce() +{ + // curl 7.56.0 introduced curl_global_sslset. +#if defined(__APPLE__) && defined(CMAKE_USE_SYSTEM_CURL) && \ + defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x073800 + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + cm::optional<std::string> curl_ssl_backend = + cmSystemTools::GetEnvVar("CURL_SSL_BACKEND"); + if (!curl_ssl_backend || curl_ssl_backend->empty()) { + curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST); + // curl 8.3.0 through 8.5.x did not re-initialize LibreSSL correctly, + // so prefer the Secure Transport backend by default in those versions. + if (cv->version_num >= 0x080300 && cv->version_num < 0x080600) { + curl_global_sslset(CURLSSLBACKEND_SECURETRANSPORT, NULL, NULL); + } + } +#endif +} + +cm::optional<int> cmCurlParseTLSVersion(cm::string_view tls_version) +{ + cm::optional<int> v; + if (tls_version == "1.0"_s) { + v = CURL_SSLVERSION_TLSv1_0; + } else if (tls_version == "1.1"_s) { + v = CURL_SSLVERSION_TLSv1_1; + } else if (tls_version == "1.2"_s) { + v = CURL_SSLVERSION_TLSv1_2; + } else if (tls_version == "1.3"_s) { + v = CURL_SSLVERSION_TLSv1_3; + } + return v; +} + +cm::optional<std::string> cmCurlPrintTLSVersion(int curl_tls_version) +{ + cm::optional<std::string> s; + switch (curl_tls_version) { + case CURL_SSLVERSION_TLSv1_0: + s = "CURL_SSLVERSION_TLSv1_0"_s; + break; + case CURL_SSLVERSION_TLSv1_1: + s = "CURL_SSLVERSION_TLSv1_1"_s; + break; + case CURL_SSLVERSION_TLSv1_2: + s = "CURL_SSLVERSION_TLSv1_2"_s; + break; + case CURL_SSLVERSION_TLSv1_3: + s = "CURL_SSLVERSION_TLSv1_3"_s; + break; + } + return s; +} + std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile) { std::string e; @@ -146,3 +226,15 @@ return url; } + +::CURL* cm_curl_easy_init() +{ + ::CURL* curl = curl_easy_init(); + if (curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST)) { + // curl 8.7.x returns incorrect HTTP/2 error codes. + if (cv->version_num >= 0x080700 && cv->version_num < 0x080800) { + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + } + } + return curl; +}
diff --git a/Source/cmCurl.h b/Source/cmCurl.h index b5134f4..2088e3d 100644 --- a/Source/cmCurl.h +++ b/Source/cmCurl.h
@@ -6,9 +6,17 @@ #include <string> +#include <cm/optional> +#include <cm/string_view> + #include <cm3p/curl/curl.h> +void cmCurlInitOnce(); +cm::optional<int> cmCurlParseTLSVersion(cm::string_view tls_version); +cm::optional<std::string> cmCurlPrintTLSVersion(int curl_tls_version); std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile = {}); std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level, const std::string& netrc_file); std::string cmCurlFixFileURL(std::string url); + +::CURL* cm_curl_easy_init();
diff --git a/Source/cmDebugTools.h b/Source/cmDebugTools.h index 99c0c6b..c3e05a6 100644 --- a/Source/cmDebugTools.h +++ b/Source/cmDebugTools.h
@@ -4,6 +4,8 @@ #include <iostream> +#include "cmSystemTools.h" + #define CM_DBG(expr) cm::dbg_impl(__FILE__, __LINE__, #expr, expr) namespace cm { @@ -13,8 +15,10 @@ template <typename T> T dbg_impl(const char* fname, int line, const char* expr, T value) { - std::cerr << fname << ':' << line << ": " << expr << " = " << value - << std::endl; + if (!cmSystemTools::GetEnvVar("CMAKE_NO_DBG")) { + std::cerr << fname << ':' << line << ": " << expr << " = " << value + << std::endl; + } return value; }
diff --git a/Source/cmELF.cxx b/Source/cmELF.cxx index 8aea753..a71e5f1 100644 --- a/Source/cmELF.cxx +++ b/Source/cmELF.cxx
@@ -212,7 +212,8 @@ // Return the number of sections as specified by the ELF header. unsigned int GetNumberOfSections() const override { - return static_cast<unsigned int>(this->ELFHeader.e_shnum); + return static_cast<unsigned int>(this->ELFHeader.e_shnum + + this->SectionHeaders[0].sh_size); } // Get the file position of a dynamic section entry. @@ -367,7 +368,7 @@ return !this->Stream->fail(); } - bool LoadSectionHeader(ELF_Half i) + bool LoadSectionHeader(unsigned int i) { // Read the section header from the file. this->Stream->seekg(this->ELFHeader.e_shoff + @@ -378,7 +379,7 @@ // Identify some important sections. if (this->SectionHeaders[i].sh_type == SHT_DYNAMIC) { - this->DynamicSectionIndex = i; + this->DynamicSectionIndex = static_cast<int>(i); } return true; } @@ -444,8 +445,11 @@ this->Machine = this->ELFHeader.e_machine; // Load the section headers. - this->SectionHeaders.resize(this->ELFHeader.e_shnum); - for (ELF_Half i = 0; i < this->ELFHeader.e_shnum; ++i) { + this->SectionHeaders.resize( + this->ELFHeader.e_shnum == 0 ? 1 : this->ELFHeader.e_shnum); + this->LoadSectionHeader(0); + this->SectionHeaders.resize(this->GetNumberOfSections()); + for (unsigned int i = 1; i < this->GetNumberOfSections(); ++i) { if (!this->LoadSectionHeader(i)) { this->SetErrorMessage("Failed to load section headers."); return; @@ -617,7 +621,19 @@ // Make sure the whole value was read. if (!(*this->Stream)) { - this->SetErrorMessage("Dynamic section specifies unreadable RPATH."); + if (tag == cmELF::TagRPath) { + this->SetErrorMessage( + "Dynamic section specifies unreadable DT_RPATH"); + } else if (tag == cmELF::TagRunPath) { + this->SetErrorMessage( + "Dynamic section specifies unreadable DT_RUNPATH"); + } else if (tag == cmELF::TagMipsRldMapRel) { + this->SetErrorMessage( + "Dynamic section specifies unreadable DT_MIPS_RLD_MAP_REL"); + } else { + this->SetErrorMessage("Dynamic section specifies unreadable value" + " for unexpected attribute"); + } se.Value = ""; return nullptr; }
diff --git a/Source/cmEnableLanguageCommand.cxx b/Source/cmEnableLanguageCommand.cxx index 59522c0..87fb077 100644 --- a/Source/cmEnableLanguageCommand.cxx +++ b/Source/cmEnableLanguageCommand.cxx
@@ -4,6 +4,9 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmSystemTools.h" bool cmEnableLanguageCommand(std::vector<std::string> const& args, cmExecutionStatus& status) @@ -13,6 +16,27 @@ return false; } + cmMakefile& mf = status.GetMakefile(); + if (!mf.IsNormalDefinitionSet("PROJECT_NAME")) { + switch (mf.GetPolicyStatus(cmPolicies::CMP0165)) { + case cmPolicies::WARN: + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + "project() should be called prior to this enable_language() call."); + break; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + mf.IssueMessage( + MessageType::FATAL_ERROR, + "project() must be called prior to this enable_language() call."); + cmSystemTools::SetFatalErrorOccurred(); + return false; + default: + break; + } + } + bool optional = false; std::vector<std::string> languages; for (std::string const& it : args) { @@ -23,6 +47,6 @@ } } - status.GetMakefile().EnableLanguage(languages, optional); + mf.EnableLanguage(languages, optional); return true; }
diff --git a/Source/cmEvaluatedTargetProperty.cxx b/Source/cmEvaluatedTargetProperty.cxx index b82c29b..56c31a3 100644 --- a/Source/cmEvaluatedTargetProperty.cxx +++ b/Source/cmEvaluatedTargetProperty.cxx
@@ -55,7 +55,7 @@ std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker, EvaluatedTargetPropertyEntries& entries, - cmGeneratorTarget::LinkInterfaceFor interfaceFor, + cmGeneratorTarget::UseTo usage, std::vector<cmLinkImplItem> const& libraries) { for (cmLinkImplItem const& lib : libraries) { @@ -67,8 +67,8 @@ cmGeneratorExpressionContext context( headTarget->GetLocalGenerator(), config, false, headTarget, headTarget, true, lib.Backtrace, lang); - cmExpandList(lib.Target->EvaluateInterfaceProperty( - prop, &context, dagChecker, interfaceFor), + cmExpandList(lib.Target->EvaluateInterfaceProperty(prop, &context, + dagChecker, usage), ee.Values); ee.ContextDependent = context.HadContextSensitiveCondition; entries.Entries.emplace_back(std::move(ee)); @@ -83,29 +83,29 @@ cmGeneratorExpressionDAGChecker* dagChecker, EvaluatedTargetPropertyEntries& entries, IncludeRuntimeInterface searchRuntime, - cmGeneratorTarget::LinkInterfaceFor interfaceFor) + cmGeneratorTarget::UseTo usage) { if (searchRuntime == IncludeRuntimeInterface::Yes) { if (cmLinkImplementation const* impl = - headTarget->GetLinkImplementation(config, interfaceFor)) { + headTarget->GetLinkImplementation(config, usage)) { entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; auto runtimeLibIt = impl->LanguageRuntimeLibraries.find(lang); if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) { addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries, - interfaceFor, runtimeLibIt->second); + usage, runtimeLibIt->second); } addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries, - interfaceFor, impl->Libraries); + usage, impl->Libraries); } } else { if (cmLinkImplementationLibraries const* impl = - headTarget->GetLinkImplementationLibraries(config, interfaceFor)) { + headTarget->GetLinkImplementationLibraries(config, usage)) { entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries, - interfaceFor, impl->Libraries); + usage, impl->Libraries); } } }
diff --git a/Source/cmEvaluatedTargetProperty.h b/Source/cmEvaluatedTargetProperty.h index e413ab0..e7b910d 100644 --- a/Source/cmEvaluatedTargetProperty.h +++ b/Source/cmEvaluatedTargetProperty.h
@@ -70,11 +70,10 @@ No }; -void AddInterfaceEntries(cmGeneratorTarget const* headTarget, - std::string const& config, std::string const& prop, - std::string const& lang, - cmGeneratorExpressionDAGChecker* dagChecker, - EvaluatedTargetPropertyEntries& entries, - IncludeRuntimeInterface searchRuntime, - cmGeneratorTarget::LinkInterfaceFor interfaceFor = - cmGeneratorTarget::LinkInterfaceFor::Usage); +void AddInterfaceEntries( + cmGeneratorTarget const* headTarget, std::string const& config, + std::string const& prop, std::string const& lang, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries, + IncludeRuntimeInterface searchRuntime, + cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile);
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index fb21f53..a2e6e70 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx
@@ -37,6 +37,15 @@ {}, cmExperimental::TryCompileCondition::Always, false }, + // CxxImportStd + { "CxxImportStd", + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508", + "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.", + {}, + cmExperimental::TryCompileCondition::Always, + false }, }; static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == static_cast<size_t>(cmExperimental::Feature::Sentinel), @@ -54,6 +63,20 @@ return ::DataForFeature(f); } +cm::optional<cmExperimental::Feature> cmExperimental::FeatureByName( + std::string const& name) +{ + size_t idx = 0; + for (auto const& feature : LookupTable) { + if (feature.Name == name) { + return static_cast<Feature>(idx); + } + ++idx; + } + + return {}; +} + bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f) { bool enabled = false;
diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h index 5593c85..05764f9 100644 --- a/Source/cmExperimental.h +++ b/Source/cmExperimental.h
@@ -8,6 +8,8 @@ #include <string> #include <vector> +#include <cm/optional> + class cmMakefile; class cmExperimental @@ -17,6 +19,7 @@ { ExportPackageDependencies, WindowsKernelModeDriver, + CxxImportStd, Sentinel, }; @@ -40,5 +43,6 @@ }; static const FeatureData& DataForFeature(Feature f); + static cm::optional<Feature> FeatureByName(std::string const& name); static bool HasSupportEnabled(cmMakefile const& mf, Feature f); };
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx index 34bda1b..cbc05dd 100644 --- a/Source/cmExportBuildAndroidMKGenerator.cxx +++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -109,8 +109,8 @@ std::string sharedLibs; std::string ldlibs; cmLinkInterfaceLibraries const* linkIFace = - target->GetLinkInterfaceLibraries( - config, target, cmGeneratorTarget::LinkInterfaceFor::Link); + target->GetLinkInterfaceLibraries(config, target, + cmGeneratorTarget::UseTo::Link); for (cmLinkItem const& item : linkIFace->Libraries) { cmGeneratorTarget const* gt = item.Target; std::string const& lib = item.AsStr(); @@ -170,8 +170,8 @@ // Tell the NDK build system if prebuilt static libraries use C++. if (target->GetType() == cmStateEnums::STATIC_LIBRARY) { - cmLinkImplementation const* li = target->GetLinkImplementation( - config, cmGeneratorTarget::LinkInterfaceFor::Link); + cmLinkImplementation const* li = + target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link); if (cm::contains(li->Languages, "CXX")) { os << "LOCAL_HAS_CPP := true\n"; }
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 2345d64..d877d76 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx
@@ -152,6 +152,8 @@ gte, cmGeneratorExpression::BuildInterface, properties); } this->PopulateCompatibleInterfaceProperties(gte, properties); + this->PopulateCustomTransitiveInterfaceProperties( + gte, cmGeneratorExpression::BuildInterface, properties); this->GenerateInterfaceProperties(gte, os, properties);
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index f60a17e..28c91da 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx
@@ -604,6 +604,28 @@ } } +void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + this->PopulateInterfaceProperty("TRANSITIVE_COMPILE_PROPERTIES", target, + properties); + this->PopulateInterfaceProperty("TRANSITIVE_LINK_PROPERTIES", target, + properties); + cmGeneratorTarget::CheckLinkLibrariesSuppressionRAII cllSuppressRAII; + std::set<std::string> ifaceProperties; + for (std::string const& config : this->Configurations) { + for (auto const& i : target->GetCustomTransitiveProperties( + config, cmGeneratorTarget::PropertyFor::Interface)) { + ifaceProperties.emplace(i.second.InterfaceName); + } + } + for (std::string const& ip : ifaceProperties) { + this->PopulateInterfaceProperty(ip, target, preprocessRule, properties); + } +} + void cmExportFileGenerator::GenerateInterfaceProperties( const cmGeneratorTarget* target, std::ostream& os, const ImportPropertyMap& properties) @@ -986,8 +1008,9 @@ /* clang-format on */ // Isolate the file policy level. - // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.27 (this upper limit may be reviewed + // Support CMake versions as far back as the + // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW + // policy settings for up to CMake 3.28 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. @@ -996,7 +1019,7 @@ << "cmake_policy(VERSION " << this->RequiredCMakeVersionMajor << '.' << this->RequiredCMakeVersionMinor << '.' - << this->RequiredCMakeVersionPatch << "...3.27)\n"; + << this->RequiredCMakeVersionPatch << "...3.28)\n"; /* clang-format on */ } @@ -1387,6 +1410,12 @@ os << ")\n\n"; } +enum class ExportWhen +{ + Defined, + Always, +}; + enum class PropertyType { Strings, @@ -1408,6 +1437,12 @@ } } +struct ModuleTargetPropertyTable +{ + cm::static_string_view Name; + ExportWhen Cond; +}; + struct ModulePropertyTable { cm::static_string_view Name; @@ -1423,18 +1458,29 @@ return true; } - const cm::static_string_view exportedDirectModuleProperties[] = { - "CXX_EXTENSIONS"_s, + const ModuleTargetPropertyTable exportedDirectModuleProperties[] = { + { "CXX_EXTENSIONS"_s, ExportWhen::Defined }, + // Always define this property as it is an intrinsic property of the target + // and should not be inherited from the in-scope `CMAKE_CXX_MODULE_STD` + // variable. + // + // TODO(cxxmodules): A future policy may make this "ON" based on the target + // policies if unset. Add a new `ExportWhen` condition to handle it when + // this happens. + { "CXX_MODULE_STD"_s, ExportWhen::Always }, }; - for (auto const& propName : exportedDirectModuleProperties) { - auto const propNameStr = std::string(propName); - cmValue prop = gte->Target->GetComputedProperty( + for (auto const& prop : exportedDirectModuleProperties) { + auto const propNameStr = std::string(prop.Name); + cmValue propValue = gte->Target->GetComputedProperty( propNameStr, *gte->Target->GetMakefile()); - if (!prop) { - prop = gte->Target->GetProperty(propNameStr); + if (!propValue) { + propValue = gte->Target->GetProperty(propNameStr); } - if (prop) { - properties[propNameStr] = cmGeneratorExpression::Preprocess(*prop, ctx); + if (propValue) { + properties[propNameStr] = + cmGeneratorExpression::Preprocess(*propValue, ctx); + } else if (prop.Cond == ExportWhen::Always) { + properties[propNameStr] = ""; } }
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f619576..f275a12 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h
@@ -145,6 +145,10 @@ ImportPropertyMap& properties); void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target, ImportPropertyMap& properties); + void PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); virtual void GenerateInterfaceProperties( cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties);
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 5c95ecd..f5f22ef 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx
@@ -160,6 +160,8 @@ properties); this->PopulateCompatibleInterfaceProperties(gt, properties); + this->PopulateCustomTransitiveInterfaceProperties( + gt, cmGeneratorExpression::InstallInterface, properties); this->GenerateInterfaceProperties(gt, os, properties);
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 00c9173..7ce5cd9 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -2,9 +2,11 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportTryCompileFileGenerator.h" +#include <map> #include <utility> #include <cm/memory> +#include <cm/string_view> #include "cmFileSet.h" #include "cmGeneratorExpression.h" @@ -44,12 +46,10 @@ ImportPropertyMap properties; for (std::string const& lang : this->Languages) { -#define FIND_TARGETS(PROPERTY) \ - this->FindTargets("INTERFACE_" #PROPERTY, te, lang, emittedDeps); - - CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(FIND_TARGETS) - -#undef FIND_TARGETS + for (auto i : cmGeneratorTarget::BuiltinTransitiveProperties) { + this->FindTargets(std::string(i.second.InterfaceName), te, lang, + emittedDeps); + } } this->PopulateProperties(te, properties, emittedDeps); @@ -76,10 +76,12 @@ // To please constraint checks of DAGChecker, this property must have // LINK_OPTIONS property as parent parentDagChecker = cm::make_unique<cmGeneratorExpressionDAGChecker>( - tgt, "LINK_OPTIONS", nullptr, nullptr); + tgt, "LINK_OPTIONS", nullptr, nullptr, tgt->GetLocalGenerator(), + this->Config); } - cmGeneratorExpressionDAGChecker dagChecker(tgt, propName, nullptr, - parentDagChecker.get()); + cmGeneratorExpressionDAGChecker dagChecker( + tgt, propName, nullptr, parentDagChecker.get(), tgt->GetLocalGenerator(), + this->Config); std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*prop);
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index 4524ba6..d4a7175 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx
@@ -830,7 +830,7 @@ // The "cmakeFiles" object kind. -static unsigned int const CMakeFilesV1Minor = 0; +static unsigned int const CMakeFilesV1Minor = 1; void cmFileAPI::BuildClientRequestCMakeFiles( ClientRequest& r, std::vector<RequestVersion> const& versions)
diff --git a/Source/cmFileAPICMakeFiles.cxx b/Source/cmFileAPICMakeFiles.cxx index e208ca8..bc80319 100644 --- a/Source/cmFileAPICMakeFiles.cxx +++ b/Source/cmFileAPICMakeFiles.cxx
@@ -4,11 +4,13 @@ #include <memory> #include <string> +#include <utility> #include <vector> #include <cm3p/json/value.h> #include "cmFileAPI.h" +#include "cmGlobCacheEntry.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -29,6 +31,8 @@ Json::Value DumpPaths(); Json::Value DumpInputs(); Json::Value DumpInput(std::string const& file); + Json::Value DumpGlobsDependent(); + Json::Value DumpGlobDependent(cmGlobCacheEntry const& entry); public: CMakeFiles(cmFileAPI& fileAPI, unsigned long version); @@ -51,6 +55,10 @@ Json::Value cmakeFiles = Json::objectValue; cmakeFiles["paths"] = this->DumpPaths(); cmakeFiles["inputs"] = this->DumpInputs(); + Json::Value globsDependent = this->DumpGlobsDependent(); + if (!globsDependent.empty()) { + cmakeFiles["globsDependent"] = std::move(globsDependent); + } return cmakeFiles; } @@ -106,6 +114,40 @@ return input; } + +Json::Value CMakeFiles::DumpGlobsDependent() +{ + Json::Value globsDependent = Json::arrayValue; + for (cmGlobCacheEntry const& entry : + this->FileAPI.GetCMakeInstance()->GetGlobCacheEntries()) { + globsDependent.append(this->DumpGlobDependent(entry)); + } + return globsDependent; +} + +Json::Value CMakeFiles::DumpGlobDependent(cmGlobCacheEntry const& entry) +{ + Json::Value globDependent = Json::objectValue; + globDependent["expression"] = entry.Expression; + if (entry.Recurse) { + globDependent["recurse"] = true; + } + if (entry.ListDirectories) { + globDependent["listDirectories"] = true; + } + if (entry.FollowSymlinks) { + globDependent["followSymlinks"] = true; + } + if (!entry.Relative.empty()) { + globDependent["relative"] = entry.Relative; + } + Json::Value paths = Json::arrayValue; + for (std::string const& file : entry.Files) { + paths.append(file); + } + globDependent["paths"] = std::move(paths); + return globDependent; +} } Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, unsigned long version)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index e9302da..b9daffb 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx
@@ -685,6 +685,11 @@ continue; } + // Ignore targets starting with `__cmake_` as they are internal. + if (cmHasLiteralPrefix(gt->GetName(), "__cmake_")) { + continue; + } + targets.append(this->DumpTarget(gt, targets.size())); } @@ -1379,14 +1384,11 @@ } // Add precompile headers compile options. - std::vector<std::string> architectures = - this->GT->GetAppleArchs(this->Config, fd.Language); - if (architectures.empty()) { - architectures.emplace_back(); - } + std::vector<std::string> pchArchs = + this->GT->GetPchArchs(this->Config, fd.Language); std::unordered_map<std::string, std::string> pchSources; - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { const std::string pchSource = this->GT->GetPchSource(this->Config, fd.Language, arch); if (!pchSource.empty()) {
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index e3f5b96..08baca0 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx
@@ -35,12 +35,14 @@ #include "cmELF.h" #include "cmExecutionStatus.h" #include "cmFSPermissions.h" +#include "cmFileCommand_ReadMacho.h" #include "cmFileCopier.h" #include "cmFileInstaller.h" #include "cmFileLockPool.h" #include "cmFileTimes.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGlobCacheEntry.h" #include "cmGlobalGenerator.h" #include "cmHexFileConverter.h" #include "cmList.h" @@ -72,6 +74,11 @@ bool HandleWriteImpl(std::vector<std::string> const& args, bool append, cmExecutionStatus& status) { + if (args.size() < 2) { + status.SetError(cmStrCat( + args[0], " must be called with at least one additional argument.")); + return false; + } auto i = args.begin(); i++; // Get rid of subcommand @@ -657,8 +664,11 @@ bool HandleGlobImpl(std::vector<std::string> const& args, bool recurse, cmExecutionStatus& status) { - // File commands has at least one argument - assert(args.size() > 1); + if (args.size() < 2) { + status.SetError(cmStrCat( + args[0], " must be called with at least one additional argument.")); + return false; + } auto i = args.begin(); @@ -810,11 +820,16 @@ std::sort(foundFiles.begin(), foundFiles.end()); foundFiles.erase(std::unique(foundFiles.begin(), foundFiles.end()), foundFiles.end()); - cm->AddGlobCacheEntry( - recurse, (recurse ? g.GetRecurseListDirs() : g.GetListDirs()), + auto entry = cmGlobCacheEntry{ + recurse, + (recurse ? g.GetRecurseListDirs() : g.GetListDirs()), (recurse ? g.GetRecurseThroughSymlinks() : false), - (g.GetRelative() ? g.GetRelative() : ""), expr, foundFiles, variable, - status.GetMakefile().GetBacktrace()); + (g.GetRelative() ? g.GetRelative() : ""), + expr, + foundFiles + }; + cm->AddGlobCacheEntry(entry, variable, + status.GetMakefile().GetBacktrace()); } else { warnConfigureLate = true; } @@ -863,8 +878,8 @@ bool HandleMakeDirectoryCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - // File command has at least one argument - assert(args.size() > 1); + // Projects might pass a dynamically generated list of directories, and it + // could be an empty list. We should not assume there is at least one. std::string expr; for (std::string const& arg : @@ -897,8 +912,8 @@ bool HandleTouchImpl(std::vector<std::string> const& args, bool create, cmExecutionStatus& status) { - // File command has at least one argument - assert(args.size() > 1); + // Projects might pass a dynamically generated list of files, and it + // could be an empty list. We should not assume there is at least one. for (std::string const& arg : cmMakeRange(args).advance(1)) // Get rid of subcommand @@ -1859,7 +1874,8 @@ long inactivity_timeout = 0; std::string logVar; std::string statusVar; - bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY"); + cm::optional<std::string> tls_version; + cm::optional<bool> tls_verify; cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); std::string netrc_level = status.GetMakefile().GetSafeDefinition("CMAKE_NETRC"); @@ -1905,6 +1921,14 @@ return false; } statusVar = *i; + } else if (*i == "TLS_VERSION") { + ++i; + if (i != args.end()) { + tls_version = *i; + } else { + status.SetError("DOWNLOAD missing value for TLS_VERSION."); + return false; + } } else if (*i == "TLS_VERIFY") { ++i; if (i != args.end()) { @@ -2016,6 +2040,30 @@ ++i; } + if (!tls_verify) { + if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) { + tls_verify = v.IsOn(); + } + } + if (!tls_verify) { + if (cm::optional<std::string> v = + cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) { + tls_verify = cmIsOn(*v); + } + } + + if (!tls_version) { + if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) { + tls_version = *v; + } + } + if (!tls_version) { + if (cm::optional<std::string> v = + cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) { + tls_version = std::move(v); + } + } + // Can't calculate hash if we don't save the file. // TODO Incrementally calculate hash in the write callback as the file is // being downloaded so this check can be relaxed. @@ -2067,8 +2115,9 @@ url = cmCurlFixFileURL(url); ::CURL* curl; + cmCurlInitOnce(); ::curl_global_init(CURL_GLOBAL_DEFAULT); - curl = ::curl_easy_init(); + curl = cm_curl_easy_init(); if (!curl) { status.SetError("DOWNLOAD error initializing curl."); return false; @@ -2082,7 +2131,10 @@ res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); check_curl_result(res, "DOWNLOAD cannot set http failure option: "); - res = ::curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/" LIBCURL_VERSION); + curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST); + res = ::curl_easy_setopt( + curl, CURLOPT_USERAGENT, + cmStrCat("curl/", cv ? cv->version : LIBCURL_VERSION).c_str()); check_curl_result(res, "DOWNLOAD cannot set user agent option: "); res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cmWriteToFileCallback); @@ -2092,8 +2144,21 @@ cmFileCommandCurlDebugCallback); check_curl_result(res, "DOWNLOAD cannot set debug function: "); + if (tls_version) { + if (cm::optional<int> v = cmCurlParseTLSVersion(*tls_version)) { + res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v); + check_curl_result( + res, + cmStrCat("DOWNLOAD cannot set TLS/SSL version ", *tls_version, ": ")); + } else { + status.SetError( + cmStrCat("DOWNLOAD given unknown TLS/SSL version ", *tls_version)); + return false; + } + } + // check to see if TLS verification is requested - if (tls_verify) { + if (tls_verify && *tls_verify) { res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify on: "); } else { @@ -2281,7 +2346,8 @@ std::string logVar; std::string statusVar; bool showProgress = false; - bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY"); + cm::optional<std::string> tls_version; + cm::optional<bool> tls_verify; cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); std::string userpwd; std::string netrc_level = @@ -2324,6 +2390,14 @@ statusVar = *i; } else if (*i == "SHOW_PROGRESS") { showProgress = true; + } else if (*i == "TLS_VERSION") { + ++i; + if (i != args.end()) { + tls_version = *i; + } else { + status.SetError("UPLOAD missing value for TLS_VERSION."); + return false; + } } else if (*i == "TLS_VERIFY") { ++i; if (i != args.end()) { @@ -2379,6 +2453,30 @@ ++i; } + if (!tls_verify) { + if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) { + tls_verify = v.IsOn(); + } + } + if (!tls_verify) { + if (cm::optional<std::string> v = + cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) { + tls_verify = cmIsOn(*v); + } + } + + if (!tls_version) { + if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) { + tls_version = *v; + } + } + if (!tls_version) { + if (cm::optional<std::string> v = + cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) { + tls_version = std::move(v); + } + } + // Open file for reading: // FILE* fin = cmsys::SystemTools::Fopen(filename, "rb"); @@ -2394,8 +2492,9 @@ url = cmCurlFixFileURL(url); ::CURL* curl; + cmCurlInitOnce(); ::curl_global_init(CURL_GLOBAL_DEFAULT); - curl = ::curl_easy_init(); + curl = cm_curl_easy_init(); if (!curl) { status.SetError("UPLOAD error initializing curl."); fclose(fin); @@ -2423,8 +2522,21 @@ cmFileCommandCurlDebugCallback); check_curl_result(res, "UPLOAD cannot set debug function: "); + if (tls_version) { + if (cm::optional<int> v = cmCurlParseTLSVersion(*tls_version)) { + res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v); + check_curl_result( + res, + cmStrCat("UPLOAD cannot set TLS/SSL version ", *tls_version, ": ")); + } else { + status.SetError( + cmStrCat("UPLOAD given unknown TLS/SSL version ", *tls_version)); + return false; + } + } + // check to see if TLS verification is requested - if (tls_verify) { + if (tls_verify && *tls_verify) { res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); check_curl_result(res, "UPLOAD cannot set TLS/SSL Verify on: "); } else { @@ -3820,8 +3932,9 @@ bool cmFileCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - if (args.size() < 2) { - status.SetError("must be called with at least two arguments."); + if (args.empty()) { + status.SetError( + "given no arguments, but it requires at least a sub-command."); return false; } @@ -3858,6 +3971,7 @@ { "RPATH_CHECK"_s, HandleRPathCheckCommand }, { "RPATH_REMOVE"_s, HandleRPathRemoveCommand }, { "READ_ELF"_s, HandleReadElfCommand }, + { "READ_MACHO"_s, HandleReadMachoCommand }, { "REAL_PATH"_s, HandleRealPathCommand }, { "RELATIVE_PATH"_s, HandleRelativePathCommand }, { "TO_CMAKE_PATH"_s, HandleCMakePathCommand },
diff --git a/Source/cmFileCommand_ReadMacho.cxx b/Source/cmFileCommand_ReadMacho.cxx new file mode 100644 index 0000000..870b3ca --- /dev/null +++ b/Source/cmFileCommand_ReadMacho.cxx
@@ -0,0 +1,99 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileCommand_ReadMacho.h" + +#include "cmArgumentParser.h" +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmRange.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#if defined(CMake_USE_MACH_PARSER) +# include "cmMachO.h" +#endif + +#include <cmext/string_view> + +bool HandleReadMachoCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 4) { + status.SetError("READ_MACHO must be called with at least three additional " + "arguments."); + return false; + } + + std::string const& fileNameArg = args[1]; + + struct Arguments + { + std::string Architectures; + std::string Error; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("ARCHITECTURES"_s, &Arguments::Architectures) + .Bind("CAPTURE_ERROR"_s, &Arguments::Error); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2), + /*unparsedArguments=*/nullptr); + + if (!arguments.Architectures.empty()) { + // always return something sensible for ARCHITECTURES + status.GetMakefile().AddDefinition(arguments.Architectures, "unknown"_s); + } + if (!cmSystemTools::FileExists(fileNameArg, true)) { + if (arguments.Error.empty()) { + status.SetError(cmStrCat("READ_MACHO given FILE \"", fileNameArg, + "\" that does not exist.")); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, cmStrCat(fileNameArg, " does not exist")); + return true; + } + +#if defined(CMake_USE_MACH_PARSER) + cmMachO macho(fileNameArg.c_str()); + if (!macho) { + if (arguments.Error.empty()) { + status.SetError(cmStrCat("READ_MACHO given FILE:\n ", fileNameArg, + "\nthat is not a valid Macho-O file.")); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, cmStrCat(fileNameArg, " is not a valid Macho-O file")); + return true; + } else if (!macho.GetErrorMessage().empty()) { + if (arguments.Error.empty()) { + status.SetError(cmStrCat( + "READ_MACHO given FILE:\n ", fileNameArg, + "\nthat is not a supported Macho-O file: ", macho.GetErrorMessage())); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, + cmStrCat(fileNameArg, + " is not a supported Macho-O file: ", macho.GetErrorMessage())); + return true; + } + + std::string output; + + if (!arguments.Architectures.empty()) { + auto archs = macho.GetArchitectures(); + output = cmJoin(archs, ";"); + + // Save the output in a makefile variable. + status.GetMakefile().AddDefinition(arguments.Architectures, output); + } +#else + if (arguments.Error.empty()) { + status.SetError("READ_MACHO support not available on this platform."); + return false; + } + status.GetMakefile().AddDefinition( + arguments.Error, "READ_MACHO support not available on this platform."); +#endif // CMake_USE_MACH_PARSER + return true; +}
diff --git a/Source/cmFileCommand_ReadMacho.h b/Source/cmFileCommand_ReadMacho.h new file mode 100644 index 0000000..b79e15f --- /dev/null +++ b/Source/cmFileCommand_ReadMacho.h
@@ -0,0 +1,11 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> +#include <vector> + +class cmExecutionStatus; + +bool HandleReadMachoCommand(std::vector<std::string> const& args, + cmExecutionStatus& status);
diff --git a/Source/cmFileSet.cxx b/Source/cmFileSet.cxx index b74855f..a00c10e 100644 --- a/Source/cmFileSet.cxx +++ b/Source/cmFileSet.cxx
@@ -4,9 +4,11 @@ #include <sstream> #include <string> +#include <unordered_map> #include <utility> #include <vector> +#include <cm/optional> #include <cmext/algorithm> #include <cmext/string_view> @@ -158,6 +160,13 @@ const cmGeneratorTarget* 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(lg, config, target, dagChecker); @@ -166,12 +175,29 @@ if (!cmSystemTools::FileIsFullPath(dir)) { dir = cmStrCat(lg->GetCurrentSourceDirectory(), '/', dir); } - auto collapsedDir = cmSystemTools::CollapseFullPath(dir); + + auto dirCacheResult = dirCache.emplace(dir, DirCacheEntry()); + auto& dirCacheEntry = dirCacheResult.first->second; + const auto 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 collapsedPriorDir = cmSystemTools::CollapseFullPath(priorDir); - if (!cmSystemTools::SameFile(collapsedDir, collapsedPriorDir) && - (cmSystemTools::IsSubDirectory(collapsedDir, collapsedPriorDir) || - cmSystemTools::IsSubDirectory(collapsedPriorDir, collapsedDir))) { + 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))) { lg->GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, cmStrCat(
diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index bec6369..31cc14b 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx
@@ -26,7 +26,7 @@ "CMAKE_ENVIRONMENT"); cmFindCommon::PathLabel cmFindCommon::PathLabel::Hints("HINTS"); cmFindCommon::PathLabel cmFindCommon::PathLabel::SystemEnvironment( - "SYSTM_ENVIRONMENT"); + "SYSTEM_ENVIRONMENT"); cmFindCommon::PathLabel cmFindCommon::PathLabel::CMakeSystem("CMAKE_SYSTEM"); cmFindCommon::PathLabel cmFindCommon::PathLabel::Guess("GUESS"); @@ -207,7 +207,7 @@ for (auto const& path : search_paths) { cmValue def = this->Makefile->GetDefinition(path.second); if (def) { - path.first = !cmIsOn(*def); + path.first = !def.IsOn(); } } }
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 9b51b1a..344c250 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx
@@ -547,6 +547,7 @@ this->DebugMode = false; this->AppendSearchPathGroups(); + this->DeprecatedFindModules["Boost"] = cmPolicies::CMP0167; this->DeprecatedFindModules["CUDA"] = cmPolicies::CMP0146; this->DeprecatedFindModules["Dart"] = cmPolicies::CMP0145; this->DeprecatedFindModules["PythonInterp"] = cmPolicies::CMP0148; @@ -628,7 +629,7 @@ // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY if (cmValue const def = this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) { - this->NoUserRegistry = !cmIsOn(*def); + this->NoUserRegistry = !def.IsOn(); } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) { this->NoUserRegistry = true; } @@ -638,7 +639,7 @@ // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY if (cmValue const def = this->Makefile->GetDefinition( "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) { - this->NoSystemRegistry = !cmIsOn(*def); + this->NoSystemRegistry = !def.IsOn(); } else if (this->Makefile->IsOn( "CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY")) { this->NoSystemRegistry = true; @@ -982,6 +983,36 @@ return true; } + // Restore PACKAGE_PREFIX_DIR to its pre-call value when we return. If our + // caller is a file generated by configure_package_config_file(), and if + // the package we are about to load also has a config file created by that + // command, it will overwrite PACKAGE_PREFIX_DIR. We need to restore it in + // case something still refers to it in our caller's scope after we return. + class RestoreVariableOnLeavingScope + { + cmMakefile* makefile_; + cm::optional<std::string> value_; + + public: + RestoreVariableOnLeavingScope(cmMakefile* makefile) + : makefile_(makefile) + { + cmValue v = makefile->GetDefinition("PACKAGE_PREFIX_DIR"); + if (v) { + value_ = *v; + } + } + ~RestoreVariableOnLeavingScope() + { + if (this->value_) { + makefile_->AddDefinition("PACKAGE_PREFIX_DIR", *value_); + } else { + makefile_->RemoveDefinition("PACKAGE_PREFIX_DIR"); + } + } + }; + RestoreVariableOnLeavingScope restorePackagePrefixDir(this->Makefile); + // Now choose what method(s) we will use to satisfy the request. Note that // we still want all the above checking of arguments, etc. regardless of the // method used. This will ensure ill-formed arguments are caught earlier, @@ -1420,7 +1451,7 @@ // Try to load the config file if the directory is known bool fileFound = false; if (this->UseConfigFiles) { - if (!cmIsOff(def)) { + if (!def.IsOff()) { // Get the directory from the variable value. std::string dir = *def; cmSystemTools::ConvertToUnixSlashes(dir); @@ -1440,7 +1471,7 @@ } // Search for the config file if it is not already found. - if (cmIsOff(def) || !fileFound) { + if (def.IsOff() || !fileFound) { fileFound = this->FindConfig(); }
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 04decd2..8e590fa 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx
@@ -70,13 +70,6 @@ currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem, this->Backtrace, language); - return this->EvaluateWithContext(context, dagChecker); -} - -const std::string& cmCompiledGeneratorExpression::EvaluateWithContext( - cmGeneratorExpressionContext& context, - cmGeneratorExpressionDAGChecker* dagChecker) const -{ if (!this->NeedsEvaluation) { return this->Input; } @@ -432,7 +425,7 @@ cmGeneratorExpressionDAGChecker dagChecker( this->HeadTarget, property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property, nullptr, - nullptr); + nullptr, this->LocalGenerator, this->Config); return this->CompiledGeneratorExpression->Evaluate( this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index e22b8ab..71855c9 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h
@@ -17,7 +17,6 @@ class cmake; class cmCompiledGeneratorExpression; class cmGeneratorTarget; -struct cmGeneratorExpressionContext; struct cmGeneratorExpressionDAGChecker; struct cmGeneratorExpressionEvaluator; @@ -151,10 +150,6 @@ std::map<std::string, std::string>& mapping); private: - const std::string& EvaluateWithContext( - cmGeneratorExpressionContext& context, - cmGeneratorExpressionDAGChecker* dagChecker) const; - cmCompiledGeneratorExpression(cmake& cmakeInstance, cmListFileBacktrace backtrace, std::string input);
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index 4e46df7..aad25f0 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -2,10 +2,10 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGeneratorExpressionDAGChecker.h" -#include <cstring> #include <sstream> #include <utility> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> @@ -18,47 +18,42 @@ #include "cmake.h" cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( + cmGeneratorTarget const* target, std::string property, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG, + std::string const& contextConfig) + : cmGeneratorExpressionDAGChecker(cmListFileBacktrace(), target, + std::move(property), content, parent, + contextLG, contextConfig) +{ +} + +cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( cmListFileBacktrace backtrace, cmGeneratorTarget const* target, std::string property, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* parent) + cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG, + std::string const& contextConfig) : Parent(parent) + , Top(parent ? parent->Top : this) , Target(target) , Property(std::move(property)) , Content(content) , Backtrace(std::move(backtrace)) - , TransitivePropertiesOnly(false) - , CMP0131(false) { - this->Initialize(); -} + if (parent) { + this->TopIsTransitiveProperty = parent->TopIsTransitiveProperty; + } else { + this->TopIsTransitiveProperty = + this->Target + ->IsTransitiveProperty(this->Property, contextLG, contextConfig, + this->EvaluatingLinkLibraries()) + .has_value(); + } -cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( - cmGeneratorTarget const* target, std::string property, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* parent) - : Parent(parent) - , Target(target) - , Property(std::move(property)) - , Content(content) - , Backtrace() - , TransitivePropertiesOnly(false) - , CMP0131(false) -{ - this->Initialize(); -} - -void cmGeneratorExpressionDAGChecker::Initialize() -{ - const auto* top = this->Top(); this->CheckResult = this->CheckGraph(); -#define TEST_TRANSITIVE_PROPERTY_METHOD(METHOD) top->METHOD() || - - if (this->CheckResult == DAG && - (CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD( - TEST_TRANSITIVE_PROPERTY_METHOD) false)) // NOLINT(*) -#undef TEST_TRANSITIVE_PROPERTY_METHOD - { + if (this->CheckResult == DAG && this->EvaluatingTransitiveProperty()) { + const auto* top = this->Top; auto it = top->Seen.find(this->Target); if (it != top->Seen.end()) { const std::set<std::string>& propSet = it->second; @@ -142,37 +137,51 @@ bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnly() const { - return this->Top()->TransitivePropertiesOnly; + return this->Top->TransitivePropertiesOnly; } bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnlyCMP0131() const { - return this->Top()->CMP0131; + return this->Top->CMP0131; +} + +bool cmGeneratorExpressionDAGChecker::EvaluatingTransitiveProperty() const +{ + return this->TopIsTransitiveProperty; } bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression() const { + // Corresponds to GenexEvaluator::EvaluateExpression. return cmHasLiteralPrefix(this->Property, "TARGET_GENEX_EVAL:") || cmHasLiteralPrefix(this->Property, "GENEX_EVAL:"); } bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression() const { - return this->Top()->Property == "INTERFACE_POSITION_INDEPENDENT_CODE"; + // Corresponds to checkInterfacePropertyCompatibility's special case + // that evaluates the value of POSITION_INDEPENDENT_CODE as a genex. + return this->Top->Property == "INTERFACE_POSITION_INDEPENDENT_CODE"; } bool cmGeneratorExpressionDAGChecker::EvaluatingCompileExpression() const { - cm::string_view property(this->Top()->Property); + cm::string_view property(this->Top->Property); return property == "INCLUDE_DIRECTORIES"_s || property == "COMPILE_DEFINITIONS"_s || property == "COMPILE_OPTIONS"_s; } +bool cmGeneratorExpressionDAGChecker::EvaluatingSources() const +{ + return this->Property == "SOURCES"_s || + this->Property == "INTERFACE_SOURCES"_s; +} + bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const { - cm::string_view property(this->Top()->Property); + cm::string_view property(this->Top->Property); return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s || property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s || @@ -181,14 +190,14 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const { - cm::string_view property(this->Top()->Property); + cm::string_view property(this->Top->Property); return property == "LINK_OPTIONS"_s || property == "LINKER_TYPE"_s; } bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const { - cm::string_view property(this->Top()->Property); + cm::string_view property(this->Top->Property); return property.length() > cmStrLen("_LINKER_LAUNCHER") && property.substr(property.length() - cmStrLen("_LINKER_LAUNCHER")) == @@ -198,7 +207,7 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries( cmGeneratorTarget const* tgt, ForGenex genex) const { - const auto* top = this->Top(); + const auto* top = this->Top; cm::string_view prop(top->Property); @@ -219,54 +228,7 @@ : (result || prop == "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s); } -cmGeneratorExpressionDAGChecker const* cmGeneratorExpressionDAGChecker::Top() - const -{ - const cmGeneratorExpressionDAGChecker* top = this; - const cmGeneratorExpressionDAGChecker* parent = this->Parent; - while (parent) { - top = parent; - parent = parent->Parent; - } - return top; -} - cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const { - return this->Top()->Target; + return this->Top->Target; } - -enum TransitiveProperty -{ -#define DEFINE_ENUM_ENTRY(NAME) NAME, - CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY) -#undef DEFINE_ENUM_ENTRY - TransitivePropertyTerminal -}; - -template <TransitiveProperty> -bool additionalTest(const char* const /*unused*/) -{ - return false; -} - -template <> -bool additionalTest<COMPILE_DEFINITIONS>(const char* const prop) -{ - return cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_"); -} - -#define DEFINE_TRANSITIVE_PROPERTY_METHOD(METHOD, PROPERTY) \ - bool cmGeneratorExpressionDAGChecker::METHOD() const \ - { \ - const char* const prop = this->Property.c_str(); \ - if (strcmp(prop, #PROPERTY) == 0 || \ - strcmp(prop, "INTERFACE_" #PROPERTY) == 0) { \ - return true; \ - } \ - return additionalTest<PROPERTY>(prop); \ - } - -CM_FOR_EACH_TRANSITIVE_PROPERTY(DEFINE_TRANSITIVE_PROPERTY_METHOD) - -#undef DEFINE_TRANSITIVE_PROPERTY_METHOD
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 782d6c8..8b0eea7 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -13,33 +13,7 @@ struct GeneratorExpressionContent; struct cmGeneratorExpressionContext; class cmGeneratorTarget; - -#define CM_SELECT_BOTH(F, A1, A2) F(A1, A2) -#define CM_SELECT_FIRST(F, A1, A2) F(A1) -#define CM_SELECT_SECOND(F, A1, A2) F(A2) - -#define CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, SELECT) \ - SELECT(F, EvaluatingIncludeDirectories, INCLUDE_DIRECTORIES) \ - SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \ - SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \ - SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \ - SELECT(F, EvaluatingAutoMocMacroNames, AUTOMOC_MACRO_NAMES) \ - SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \ - SELECT(F, EvaluatingSources, SOURCES) \ - SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES) \ - SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS) \ - SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES) \ - SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS) \ - SELECT(F, EvaluatingPrecompileHeaders, PRECOMPILE_HEADERS) - -#define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \ - CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH) - -#define CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(F) \ - CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_FIRST) - -#define CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(F) \ - CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_SECOND) +class cmLocalGenerator; struct cmGeneratorExpressionDAGChecker { @@ -47,11 +21,15 @@ cmGeneratorTarget const* target, std::string property, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* parent); + cmGeneratorExpressionDAGChecker* parent, + cmLocalGenerator const* contextLG, + std::string const& contextConfig); cmGeneratorExpressionDAGChecker(cmGeneratorTarget const* target, std::string property, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* parent); + cmGeneratorExpressionDAGChecker* parent, + cmLocalGenerator const* contextLG, + std::string const& contextConfig); enum Result { @@ -66,6 +44,7 @@ void ReportError(cmGeneratorExpressionContext* context, const std::string& expr); + bool EvaluatingTransitiveProperty() const; bool EvaluatingGenexExpression() const; bool EvaluatingPICExpression() const; bool EvaluatingCompileExpression() const; @@ -82,11 +61,7 @@ bool EvaluatingLinkLibraries(cmGeneratorTarget const* tgt = nullptr, ForGenex genex = ForGenex::ANY) const; -#define DECLARE_TRANSITIVE_PROPERTY_METHOD(METHOD) bool METHOD() const; - - CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(DECLARE_TRANSITIVE_PROPERTY_METHOD) - -#undef DECLARE_TRANSITIVE_PROPERTY_METHOD + bool EvaluatingSources() const; bool GetTransitivePropertiesOnly() const; void SetTransitivePropertiesOnly() { this->TransitivePropertiesOnly = true; } @@ -94,20 +69,20 @@ bool GetTransitivePropertiesOnlyCMP0131() const; void SetTransitivePropertiesOnlyCMP0131() { this->CMP0131 = true; } - cmGeneratorExpressionDAGChecker const* Top() const; cmGeneratorTarget const* TopTarget() const; private: Result CheckGraph() const; - void Initialize(); const cmGeneratorExpressionDAGChecker* const Parent; + const cmGeneratorExpressionDAGChecker* const Top; cmGeneratorTarget const* Target; const std::string Property; mutable std::map<cmGeneratorTarget const*, std::set<std::string>> Seen; const GeneratorExpressionContent* const Content; const cmListFileBacktrace Backtrace; Result CheckResult; - bool TransitivePropertiesOnly; - bool CMP0131; + bool TransitivePropertiesOnly = false; + bool CMP0131 = false; + bool TopIsTransitiveProperty = false; };
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index c8147b2..84521ca 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx
@@ -487,7 +487,8 @@ if (context->HeadTarget) { cmGeneratorExpressionDAGChecker dagChecker( context->Backtrace, context->HeadTarget, - genexOperator + ":" + expression, content, dagCheckerParent); + genexOperator + ":" + expression, content, dagCheckerParent, + context->LG, context->Config); switch (dagChecker.Check()) { case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: { @@ -547,6 +548,7 @@ return expression; } + // Replace the surrounding context with the named target. cmGeneratorExpressionContext targetContext( context->LG, context->Config, context->Quiet, target, target, context->EvaluateForBuildsystem, context->Backtrace, context->Language); @@ -1859,6 +1861,7 @@ static const CharacterNode<'>'> angle_rNode; static const CharacterNode<','> commaNode; static const CharacterNode<';'> semicolonNode; +static const CharacterNode<'"'> quoteNode; struct CompilerIdNode : public cmGeneratorExpressionNode { @@ -2010,6 +2013,76 @@ fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"), hipCompilerVersionNode("HIP"); +struct CompilerFrontendVariantNode : public cmGeneratorExpressionNode +{ + CompilerFrontendVariantNode(const char* compilerLang) + : CompilerLanguage(compilerLang) + { + } + + int NumExpectedParameters() const override { return ZeroOrMoreParameters; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + if (!context->HeadTarget) { + std::ostringstream e; + e << "$<" << this->CompilerLanguage + << "_COMPILER_FRONTEND_VARIANT> may only be used with binary targets. " + " It may not be used with add_custom_command or add_custom_target."; + reportError(context, content->GetOriginalExpression(), e.str()); + return {}; + } + return this->EvaluateWithLanguage(parameters, context, content, dagChecker, + this->CompilerLanguage); + } + + std::string EvaluateWithLanguage(const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*unused*/, + const std::string& lang) const + { + std::string const& compilerFrontendVariant = + context->LG->GetMakefile()->GetSafeDefinition( + "CMAKE_" + lang + "_COMPILER_FRONTEND_VARIANT"); + if (parameters.empty()) { + return compilerFrontendVariant; + } + if (compilerFrontendVariant.empty()) { + return parameters.front().empty() ? "1" : "0"; + } + static cmsys::RegularExpression compilerFrontendVariantValidator( + "^[A-Za-z0-9_]*$"); + + for (auto const& param : parameters) { + if (!compilerFrontendVariantValidator.find(param)) { + reportError(context, content->GetOriginalExpression(), + "Expression syntax not recognized."); + return {}; + } + if (strcmp(param.c_str(), compilerFrontendVariant.c_str()) == 0) { + return "1"; + } + } + return "0"; + } + + const char* const CompilerLanguage; +}; + +static const CompilerFrontendVariantNode cCompilerFrontendVariantNode("C"), + cxxCompilerFrontendVariantNode("CXX"), + cudaCompilerFrontendVariantNode("CUDA"), + objcCompilerFrontendVariantNode("OBJC"), + objcxxCompilerFrontendVariantNode("OBJCXX"), + fortranCompilerFrontendVariantNode("Fortran"), + hipCompilerFrontendVariantNode("HIP"), + ispcCompilerFrontendVariantNode("ISPC"); + struct PlatformIdNode : public cmGeneratorExpressionNode { PlatformIdNode() {} // NOLINT(modernize-use-equals-default) @@ -2698,12 +2771,12 @@ static std::string getLinkedTargetsContent( cmGeneratorTarget const* target, std::string const& prop, cmGeneratorExpressionContext* context, - cmGeneratorExpressionDAGChecker* dagChecker) + cmGeneratorExpressionDAGChecker* dagChecker, cmGeneratorTarget::UseTo usage) { std::string result; if (cmLinkImplementationLibraries const* impl = target->GetLinkImplementationLibraries( - context->Config, cmGeneratorTarget::LinkInterfaceFor::Usage)) { + context->Config, cmGeneratorTarget::UseTo::Compile)) { for (cmLinkImplItem const& lib : impl->Libraries) { if (lib.Target) { // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our @@ -2713,8 +2786,8 @@ target->GetLocalGenerator(), context->Config, context->Quiet, target, target, context->EvaluateForBuildsystem, lib.Backtrace, context->Language); - std::string libResult = - lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker); + std::string libResult = lib.Target->EvaluateInterfaceProperty( + prop, &libContext, dagChecker, usage); if (!libResult.empty()) { if (result.empty()) { result = std::move(libResult); @@ -2870,37 +2943,31 @@ return target->GetLinkerLanguage(context->Config); } + bool const evaluatingLinkLibraries = + dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries(); + std::string interfacePropertyName; bool isInterfaceProperty = false; + cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile; -#define POPULATE_INTERFACE_PROPERTY_NAME(prop) \ - if (propertyName == #prop) { \ - interfacePropertyName = "INTERFACE_" #prop; \ - } else if (propertyName == "INTERFACE_" #prop) { \ - interfacePropertyName = "INTERFACE_" #prop; \ - isInterfaceProperty = true; \ - } else - - CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME) - // Note that the above macro terminates with an else - /* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) { - cmPolicies::PolicyStatus polSt = - context->LG->GetPolicyStatus(cmPolicies::CMP0043); - if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { - interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS"; - } + if (cm::optional<cmGeneratorTarget::TransitiveProperty> transitiveProp = + target->IsTransitiveProperty(propertyName, context->LG, + context->Config, + evaluatingLinkLibraries)) { + interfacePropertyName = std::string(transitiveProp->InterfaceName); + isInterfaceProperty = transitiveProp->InterfaceName == propertyName; + usage = transitiveProp->Usage; } -#undef POPULATE_INTERFACE_PROPERTY_NAME - - bool evaluatingLinkLibraries = false; if (dagCheckerParent) { + // This $<TARGET_PROPERTY:...> node has been reached while evaluating + // another target property value. Check that the outermost evaluation + // expects such nested evaluations. if (dagCheckerParent->EvaluatingGenexExpression() || dagCheckerParent->EvaluatingPICExpression() || dagCheckerParent->EvaluatingLinkerLauncher()) { // No check required. - } else if (dagCheckerParent->EvaluatingLinkLibraries()) { - evaluatingLinkLibraries = true; + } else if (evaluatingLinkLibraries) { if (!interfacePropertyName.empty()) { reportError( context, content->GetOriginalExpression(), @@ -2910,21 +2977,19 @@ return std::string(); } } else { -#define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() || - assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD( - ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy) -#undef ASSERT_TRANSITIVE_PROPERTY_METHOD + assert(dagCheckerParent->EvaluatingTransitiveProperty()); } } if (isInterfaceProperty) { return cmGeneratorExpression::StripEmptyListElements( target->EvaluateInterfaceProperty(propertyName, context, - dagCheckerParent)); + dagCheckerParent, usage)); } cmGeneratorExpressionDAGChecker dagChecker( - context->Backtrace, target, propertyName, content, dagCheckerParent); + context->Backtrace, target, propertyName, content, dagCheckerParent, + context->LG, context->Config); switch (dagChecker.Check()) { case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: @@ -2949,8 +3014,20 @@ return std::string(); } - if (!haveProp && !target->IsImported() && - target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + // Properties named by COMPATIBLE_INTERFACE_ properties combine over + // the transitive link closure as a single order-independent value. + // Imported targets do not themselves have a defined value for these + // properties, but they can contribute to the value of a non-imported + // dependent. + // + // For COMPATIBLE_INTERFACE_{BOOL,STRING}: + // * If set on this target, use the value directly. It is checked + // elsewhere for consistency over the transitive link closure. + // * If not set on this target, compute the value from the closure. + // + // For COMPATIBLE_INTERFACE_NUMBER_{MAX,MIN} we always compute the value + // from this target and the transitive link closure to get the max or min. + if (!haveProp && !target->IsImported()) { if (target->IsLinkInterfaceDependentBoolProperty(propertyName, context->Config)) { context->HadContextSensitiveCondition = true; @@ -2967,6 +3044,8 @@ context->Config); return propContent ? propContent : ""; } + } + if (!evaluatingLinkLibraries && !target->IsImported()) { if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, context->Config)) { context->HadContextSensitiveCondition = true; @@ -2985,32 +3064,14 @@ } } - if (!target->IsImported() && dagCheckerParent && - !dagCheckerParent->EvaluatingLinkLibraries()) { - if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, - context->Config)) { - context->HadContextSensitiveCondition = true; - const char* propContent = - target->GetLinkInterfaceDependentNumberMinProperty(propertyName, - context->Config); - return propContent ? propContent : ""; - } - if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, - context->Config)) { - context->HadContextSensitiveCondition = true; - const char* propContent = - target->GetLinkInterfaceDependentNumberMaxProperty(propertyName, - context->Config); - return propContent ? propContent : ""; - } - } - + // Some properties, such as usage requirements, combine over the + // transitive link closure as an ordered list. if (!interfacePropertyName.empty()) { result = cmGeneratorExpression::StripEmptyListElements( this->EvaluateDependentExpression(result, context->LG, context, target, &dagChecker, target)); std::string linkedTargetsContent = getLinkedTargetsContent( - target, interfacePropertyName, context, &dagChecker); + target, interfacePropertyName, context, &dagChecker, usage); if (!linkedTargetsContent.empty()) { result += (result.empty() ? "" : ";") + linkedTargetsContent; } @@ -4458,6 +4519,14 @@ { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode }, { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode }, { "HIP_COMPILER_VERSION", &hipCompilerVersionNode }, + { "C_COMPILER_FRONTEND_VARIANT", &cCompilerFrontendVariantNode }, + { "CXX_COMPILER_FRONTEND_VARIANT", &cxxCompilerFrontendVariantNode }, + { "CUDA_COMPILER_FRONTEND_VARIANT", &cudaCompilerFrontendVariantNode }, + { "OBJC_COMPILER_FRONTEND_VARIANT", &objcCompilerFrontendVariantNode }, + { "OBJCXX_COMPILER_FRONTEND_VARIANT", &objcxxCompilerFrontendVariantNode }, + { "Fortran_COMPILER_FRONTEND_VARIANT", + &fortranCompilerFrontendVariantNode }, + { "HIP_COMPILER_FRONTEND_VARIANT", &hipCompilerFrontendVariantNode }, { "PLATFORM_ID", &platformIdNode }, { "COMPILE_FEATURES", &compileFeaturesNode }, { "CONFIGURATION", &configurationNode }, @@ -4529,6 +4598,7 @@ { "ANGLE-R", &angle_rNode }, { "COMMA", &commaNode }, { "SEMICOLON", &semicolonNode }, + { "QUOTE", "eNode }, { "TARGET_PROPERTY", &targetPropertyNode }, { "TARGET_NAME", &targetNameNode }, { "TARGET_OBJECTS", &targetObjectsNode },
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 0fe19de..727d452 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx
@@ -6,13 +6,9 @@ #include <array> #include <cassert> #include <cctype> -#include <cerrno> #include <cstddef> #include <cstdio> -#include <cstdlib> #include <cstring> -#include <iterator> -#include <queue> #include <sstream> #include <type_traits> #include <unordered_set> @@ -24,21 +20,16 @@ #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/RegularExpression.hxx" - #include "cmAlgorithms.h" -#include "cmComputeLinkInformation.h" +#include "cmComputeLinkInformation.h" // IWYU pragma: keep #include "cmCryptoHash.h" -#include "cmCustomCommandGenerator.h" #include "cmCxxModuleUsageEffects.h" -#include "cmEvaluatedTargetProperty.h" +#include "cmExperimental.h" #include "cmFileSet.h" #include "cmFileTimes.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" -#include "cmGeneratorExpressionContext.h" #include "cmGeneratorExpressionDAGChecker.h" -#include "cmGeneratorExpressionNode.h" #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmLocalGenerator.h" @@ -46,11 +37,9 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPropertyMap.h" -#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" -#include "cmSourceGroup.h" #include "cmStandardLevel.h" #include "cmStandardLevelResolver.h" #include "cmState.h" @@ -60,16 +49,11 @@ #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" #include "cmTargetPropertyComputer.h" +#include "cmTargetTraceDependencies.h" #include "cmake.h" namespace { -using LinkInterfaceFor = cmGeneratorTarget::LinkInterfaceFor; - -const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES"; -const std::string kINTERFACE_LINK_LIBRARIES_DIRECT = - "INTERFACE_LINK_LIBRARIES_DIRECT"; -const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE = - "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"; +using UseTo = cmGeneratorTarget::UseTo; } template <> @@ -95,176 +79,14 @@ return tgt->GetLocation(config); } -cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem; - -class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryString(BT<std::string> propertyValue, - cmLinkImplItem const& item = NoLinkImplItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , PropertyValue(std::move(propertyValue)) - { - } - - const std::string& Evaluate(cmLocalGenerator*, const std::string&, - cmGeneratorTarget const*, - cmGeneratorExpressionDAGChecker*, - std::string const&) const override - { - return this->PropertyValue.Value; - } - - cmListFileBacktrace GetBacktrace() const override - { - return this->PropertyValue.Backtrace; - } - std::string const& GetInput() const override - { - return this->PropertyValue.Value; - } - -private: - BT<std::string> PropertyValue; -}; - -class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, - cmLinkImplItem const& item = NoLinkImplItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , ge(std::move(cge)) - { - } - - const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, - cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& language) const override - { - return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr, - language); - } - - 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: - const std::unique_ptr<cmCompiledGeneratorExpression> ge; -}; - -class TargetPropertyEntryFileSet - : public cmGeneratorTarget::TargetPropertyEntry -{ -public: - TargetPropertyEntryFileSet( - std::vector<std::string> dirs, bool contextSensitiveDirs, - std::unique_ptr<cmCompiledGeneratorExpression> entryCge, - const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem) - : cmGeneratorTarget::TargetPropertyEntry(item) - , BaseDirs(std::move(dirs)) - , ContextSensitiveDirs(contextSensitiveDirs) - , EntryCge(std::move(entryCge)) - , FileSet(fileSet) - { - } - - const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, - cmGeneratorTarget const* headTarget, - cmGeneratorExpressionDAGChecker* dagChecker, - std::string const& /*lang*/) const override - { - std::map<std::string, std::vector<std::string>> filesPerDir; - this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, - this->EntryCge, lg, config, 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: - const std::vector<std::string> BaseDirs; - const bool ContextSensitiveDirs; - const std::unique_ptr<cmCompiledGeneratorExpression> EntryCge; - const cmFileSet* FileSet; -}; - -std::unique_ptr< - cmGeneratorTarget:: - TargetPropertyEntry> static CreateTargetPropertyEntry(cmake& cmakeInstance, - const BT< - std::string>& - propertyValue, - bool - evaluateForBuildsystem = - false) -{ - 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)); -} - -cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry( - cmLinkImplItem const& item) - : LinkImplItem(item) -{ -} - -bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition() - const -{ - return false; -} - static void CreatePropertyGeneratorExpressions( cmake& cmakeInstance, cmBTStringRange entries, std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items, bool evaluateForBuildsystem = false) { for (auto const& entry : entries) { - items.push_back( - CreateTargetPropertyEntry(cmakeInstance, entry, evaluateForBuildsystem)); + items.push_back(cmGeneratorTarget::TargetPropertyEntry::Create( + cmakeInstance, entry, evaluateForBuildsystem)); } } @@ -689,11 +511,15 @@ this->SourcesAreContextDependent = Tribool::Indeterminate; this->Objects.clear(); this->VisitedConfigsForObjects.clear(); + this->LinkImplClosureForLinkMap.clear(); + this->LinkImplClosureForUsageMap.clear(); this->LinkImplMap.clear(); this->LinkImplUsageRequirementsOnlyMap.clear(); this->IncludeDirectoriesCache.clear(); this->CompileOptionsCache.clear(); this->CompileDefinitionsCache.clear(); + this->CustomTransitiveBuildPropertiesMap.clear(); + this->CustomTransitiveInterfacePropertiesMap.clear(); this->PrecompileHeadersCache.clear(); this->LinkOptionsCache.clear(); this->LinkDirectoriesCache.clear(); @@ -711,7 +537,7 @@ { this->SourceEntries.insert( before ? this->SourceEntries.begin() : this->SourceEntries.end(), - CreateTargetPropertyEntry( + TargetPropertyEntry::Create( *this->LocalGenerator->GetCMakeInstance(), BT<std::string>(src, this->Makefile->GetBacktrace()), true)); this->ClearSourcesCache(); @@ -739,7 +565,7 @@ this->IncludeDirectoriesEntries.insert( before ? this->IncludeDirectoriesEntries.begin() : this->IncludeDirectoriesEntries.end(), - CreateTargetPropertyEntry( + TargetPropertyEntry::Create( *this->Makefile->GetCMakeInstance(), BT<std::string>(src, this->Makefile->GetBacktrace()), true)); } @@ -885,8 +711,8 @@ std::string propName{ "LINKER_TYPE" }; auto linkerType = this->GetProperty(propName); if (!linkerType.IsEmpty()) { - cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr, - nullptr); + cmGeneratorExpressionDAGChecker dagChecker( + this, propName, nullptr, nullptr, this->LocalGenerator, config); auto ltype = cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(), config, this, &dagChecker, this, lang); @@ -928,7 +754,7 @@ { cmValue feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config); - if (!cmIsOn(feature)) { + if (!feature.IsOn()) { // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies return false; } @@ -1094,8 +920,8 @@ bool cmGeneratorTarget::GetLanguageStandardRequired( std::string const& lang) const { - return cmIsOn( - this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED")); + return this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED") + .IsOn(); } void cmGeneratorTarget::GetModuleDefinitionSources( @@ -1351,7 +1177,8 @@ if (iter == this->SystemIncludesCache.end()) { cmGeneratorExpressionDAGChecker dagChecker( - this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr); + this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr, + this->LocalGenerator, config); bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED"); @@ -1363,14 +1190,14 @@ } std::vector<cmGeneratorTarget const*> const& deps = - this->GetLinkImplementationClosure(config); + this->GetLinkImplementationClosure(config, UseTo::Compile); for (cmGeneratorTarget const* dep : deps) { handleSystemIncludesDep(this->LocalGenerator, dep, config, this, &dagChecker, result, excludeImported, language); } cmLinkImplementation const* impl = - this->GetLinkImplementation(config, LinkInterfaceFor::Usage); + this->GetLinkImplementation(config, UseTo::Compile); if (impl != nullptr) { auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language); if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) { @@ -1400,743 +1227,6 @@ return this->Target->GetPropertyAsBool(prop); } -bool cmGeneratorTarget::MaybeHaveInterfaceProperty( - std::string const& prop, cmGeneratorExpressionContext* context, - LinkInterfaceFor interfaceFor) const -{ - std::string const key = prop + '@' + 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; - - // If this target itself has a non-empty property value, we are done. - maybeInterfaceProp = cmNonempty(this->GetProperty(prop)); - - // Otherwise, recurse to interface dependencies. - if (!maybeInterfaceProp) { - cmGeneratorTarget const* headTarget = - context->HeadTarget ? context->HeadTarget : this; - if (cmLinkInterfaceLibraries const* iface = - this->GetLinkInterfaceLibraries(context->Config, headTarget, - interfaceFor)) { - 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->MaybeHaveInterfaceProperty(prop, context, - interfaceFor)) { - maybeInterfaceProp = true; - break; - } - } - } - } - } - } - return i->second; -} - -std::string cmGeneratorTarget::EvaluateInterfaceProperty( - std::string const& prop, cmGeneratorExpressionContext* context, - cmGeneratorExpressionDAGChecker* dagCheckerParent, - LinkInterfaceFor interfaceFor) const -{ - std::string result; - - // If the property does not appear transitively at all, we are done. - if (!this->MaybeHaveInterfaceProperty(prop, context, interfaceFor)) { - return result; - } - - // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is - // a subset of TargetPropertyNode::Evaluate without stringify/parse steps - // but sufficient for transitive interface properties. - cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace, this, prop, - nullptr, dagCheckerParent); - switch (dagChecker.Check()) { - case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: - dagChecker.ReportError( - context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">"); - return result; - 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 result; - case cmGeneratorExpressionDAGChecker::DAG: - break; - } - - cmGeneratorTarget const* headTarget = - context->HeadTarget ? context->HeadTarget : this; - - if (cmValue p = this->GetProperty(prop)) { - result = cmGeneratorExpressionNode::EvaluateDependentExpression( - *p, context->LG, context, headTarget, &dagChecker, this); - } - - if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries( - context->Config, headTarget, interfaceFor)) { - context->HadContextSensitiveCondition = - context->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) { - // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the - // above property and hand-evaluate it as if it were compiled. - // Create a context as cmCompiledGeneratorExpression::Evaluate does. - cmGeneratorExpressionContext libContext( - context->LG, context->Config, context->Quiet, headTarget, this, - context->EvaluateForBuildsystem, context->Backtrace, - context->Language); - std::string libResult = cmGeneratorExpression::StripEmptyListElements( - lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker, - interfaceFor)); - if (!libResult.empty()) { - if (result.empty()) { - result = std::move(libResult); - } else { - result.reserve(result.size() + 1 + libResult.size()); - result += ";"; - result += libResult; - } - } - context->HadContextSensitiveCondition = - context->HadContextSensitiveCondition || - libContext.HadContextSensitiveCondition; - context->HadHeadSensitiveCondition = - context->HadHeadSensitiveCondition || - libContext.HadHeadSensitiveCondition; - } - } - } - - return result; -} - -namespace { - -enum class IncludeDirectoryFallBack -{ - BINARY, - OBJECT -}; - -std::string AddLangSpecificInterfaceIncludeDirectories( - const cmGeneratorTarget* root, const cmGeneratorTarget* target, - const std::string& lang, const std::string& config, - const std::string& propertyName, IncludeDirectoryFallBack mode, - cmGeneratorExpressionDAGChecker* context) -{ - cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, - propertyName, nullptr, context }; - switch (dag.Check()) { - case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: - dag.ReportError( - nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName"); - CM_FALLTHROUGH; - 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 ""; - case cmGeneratorExpressionDAGChecker::DAG: - break; - } - - std::string directories; - if (const auto* link_interface = target->GetLinkInterfaceLibraries( - config, root, LinkInterfaceFor::Usage)) { - for (const cmLinkItem& library : link_interface->Libraries) { - if (const cmGeneratorTarget* dependency = library.Target) { - if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { - auto* lg = dependency->GetLocalGenerator(); - std::string value = dependency->GetSafeProperty(propertyName); - if (value.empty()) { - if (mode == IncludeDirectoryFallBack::BINARY) { - value = lg->GetCurrentBinaryDirectory(); - } else if (mode == IncludeDirectoryFallBack::OBJECT) { - value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', - lg->GetTargetDirectory(dependency)); - } - } - - if (!directories.empty()) { - directories += ";"; - } - directories += value; - } - } - } - } - return directories; -} - -void AddLangSpecificImplicitIncludeDirectories( - const cmGeneratorTarget* target, const std::string& lang, - const std::string& config, const std::string& propertyName, - IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries) -{ - if (const auto* libraries = target->GetLinkImplementationLibraries( - config, LinkInterfaceFor::Usage)) { - cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, - propertyName, nullptr, nullptr }; - - for (const cmLinkImplItem& library : libraries->Libraries) { - if (const cmGeneratorTarget* dependency = library.Target) { - if (!dependency->IsInBuildSystem()) { - continue; - } - if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { - auto* lg = dependency->GetLocalGenerator(); - EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; - - if (cmValue val = dependency->GetProperty(propertyName)) { - entry.Values.emplace_back(*val); - } else { - if (mode == IncludeDirectoryFallBack::BINARY) { - entry.Values.emplace_back(lg->GetCurrentBinaryDirectory()); - } else if (mode == IncludeDirectoryFallBack::OBJECT) { - entry.Values.emplace_back( - dependency->GetObjectDirectory(config)); - } - } - - cmExpandList( - AddLangSpecificInterfaceIncludeDirectories( - target, dependency, lang, config, propertyName, mode, &dag), - entry.Values); - entries.Entries.emplace_back(std::move(entry)); - } - } - } - } -} - -void AddObjectEntries(cmGeneratorTarget const* headTarget, - std::string const& config, - cmGeneratorExpressionDAGChecker* dagChecker, - EvaluatedTargetPropertyEntries& entries) -{ - if (cmLinkImplementationLibraries const* impl = - headTarget->GetLinkImplementationLibraries(config, - LinkInterfaceFor::Usage)) { - entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; - for (cmLinkImplItem const& lib : impl->Libraries) { - if (lib.Target && - lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::string uniqueName = - headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely( - lib.Target); - std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">"; - cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(), - lib.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); - cge->SetEvaluateForBuildsystem(true); - - EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); - cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config, - headTarget, dagChecker), - ee.Values); - if (cge->GetHadContextSensitiveCondition()) { - ee.ContextDependent = true; - } - entries.Entries.emplace_back(std::move(ee)); - } - } - } -} - -void addFileSetEntry(cmGeneratorTarget const* headTarget, - std::string const& config, - cmGeneratorExpressionDAGChecker* dagChecker, - cmFileSet const* fileSet, - EvaluatedTargetPropertyEntries& entries) -{ - auto dirCges = fileSet->CompileDirectoryEntries(); - auto dirs = fileSet->EvaluateDirectoryEntries( - dirCges, headTarget->GetLocalGenerator(), config, 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()) { - TargetPropertyEntryFileSet tpe(dirs, contextSensitiveDirs, - std::move(entryCge), fileSet); - entries.Entries.emplace_back( - EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, tpe)); - 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, - std::string const& config, - cmGeneratorExpressionDAGChecker* dagChecker, - 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, config, 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, config, dagChecker, cxxModuleSet, entries); - } - } -} - -bool processSources(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, - std::vector<BT<std::string>>& srcs, - std::unordered_set<std::string>& uniqueSrcs, - bool debugSources) -{ - cmMakefile* mf = tgt->Target->GetMakefile(); - - bool contextDependent = entries.HadContextSensitiveCondition; - - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { - if (entry.ContextDependent) { - contextDependent = true; - } - - cmLinkImplItem const& item = entry.LinkImplItem; - std::string const& targetName = item.AsStr(); - - for (std::string& src : entry.Values) { - cmSourceFile* sf = mf->GetOrCreateSource(src); - std::string e; - std::string w; - std::string fullPath = sf->ResolveFullPath(&e, &w); - cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); - if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); - } - if (fullPath.empty()) { - if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); - } - return contextDependent; - } - - if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) { - std::ostringstream err; - if (!targetName.empty()) { - err << "Target \"" << targetName - << "\" contains relative path in its INTERFACE_SOURCES:\n \"" - << src << "\""; - } else { - err << "Found relative path while evaluating sources of \"" - << tgt->GetName() << "\":\n \"" << src << "\"\n"; - } - tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, - err.str()); - return contextDependent; - } - src = fullPath; - } - std::string usedSources; - for (std::string const& src : entry.Values) { - if (uniqueSrcs.insert(src).second) { - srcs.emplace_back(src, entry.Backtrace); - if (debugSources) { - usedSources += " * " + src + "\n"; - } - } - } - if (!usedSources.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used sources for target ") + tgt->GetName() + ":\n" + - usedSources, - entry.Backtrace); - } - } - return contextDependent; -} -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( - std::string const& config) const -{ - std::vector<BT<std::string>> files; - - if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { - // At configure-time, this method can be called as part of getting the - // LOCATION property or to export() a file to be include()d. However - // there is no cmGeneratorTarget at configure-time, so search the SOURCES - // for TARGET_OBJECTS instead for backwards compatibility with OLD - // behavior of CMP0024 and CMP0026 only. - - cmBTStringRange sourceEntries = this->Target->GetSourceEntries(); - for (auto const& entry : sourceEntries) { - cmList items{ entry.Value }; - for (auto const& item : items) { - if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") && - item.back() == '>') { - continue; - } - files.emplace_back(item); - } - } - return files; - } - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugSources = - !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES"); - - if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { - this->DebugSourcesDone = true; - } - - cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, - nullptr); - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, std::string(), &dagChecker, this->SourceEntries); - - std::unordered_set<std::string> uniqueSrcs; - bool contextDependentDirectSources = - processSources(this, entries, files, uniqueSrcs, debugSources); - - // Collect INTERFACE_SOURCES of all direct link-dependencies. - EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries; - AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(), - &dagChecker, linkInterfaceSourcesEntries, - IncludeRuntimeInterface::No, LinkInterfaceFor::Usage); - bool contextDependentInterfaceSources = processSources( - this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); - - // Collect TARGET_OBJECTS of direct object link-dependencies. - bool contextDependentObjects = false; - if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { - EvaluatedTargetPropertyEntries linkObjectsEntries; - AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); - contextDependentObjects = processSources(this, 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 - // are specified without per-config generator expressions. - } - - // Collect this target's file sets. - EvaluatedTargetPropertyEntries fileSetEntries; - AddFileSetEntries(this, config, &dagChecker, fileSetEntries); - bool contextDependentFileSets = - processSources(this, fileSetEntries, files, uniqueSrcs, debugSources); - - // Determine if sources are context-dependent or not. - if (!contextDependentDirectSources && !contextDependentInterfaceSources && - !contextDependentObjects && !contextDependentFileSets) { - this->SourcesAreContextDependent = Tribool::False; - } else { - this->SourcesAreContextDependent = Tribool::True; - } - - return files; -} - -void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, - const std::string& config) const -{ - std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config); - files.reserve(tmp.size()); - for (BT<cmSourceFile*>& v : tmp) { - files.push_back(v.Value); - } -} - -std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles( - std::string const& config) const -{ - std::vector<BT<cmSourceFile*>> files; - if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) { - // Since we are still configuring not all sources may exist yet, - // so we need to avoid full source classification because that - // requires the absolute paths to all sources to be determined. - // Since this is only for compatibility with old policies that - // projects should not depend on anymore, just compute the files - // without memoizing them. - std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); - std::set<cmSourceFile*> emitted; - for (BT<std::string> const& s : srcs) { - cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); - if (emitted.insert(sf).second) { - files.emplace_back(sf, s.Backtrace); - } - } - return files; - } - - KindedSources const& kinded = this->GetKindedSources(config); - files.reserve(kinded.Sources.size()); - for (SourceAndKind const& si : kinded.Sources) { - files.push_back(si.Source); - } - return files; -} - -void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( - std::vector<cmSourceFile*>& files, const std::string& config) const -{ - std::vector<BT<cmSourceFile*>> tmp = - this->GetSourceFilesWithoutObjectLibraries(config); - files.reserve(tmp.size()); - for (BT<cmSourceFile*>& v : tmp) { - files.push_back(v.Value); - } -} - -std::vector<BT<cmSourceFile*>> -cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( - std::string const& config) const -{ - std::vector<BT<cmSourceFile*>> files; - KindedSources const& kinded = this->GetKindedSources(config); - files.reserve(kinded.Sources.size()); - for (SourceAndKind const& si : kinded.Sources) { - if (si.Source.Value->GetObjectLibrary().empty()) { - files.push_back(si.Source); - } - } - return files; -} - -cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( - std::string const& config) const -{ - // If we already processed one configuration and found no dependency - // on configuration then always use the one result. - if (this->SourcesAreContextDependent == Tribool::False) { - return this->KindedSourcesMap.begin()->second; - } - - // Lookup any existing link implementation for this configuration. - std::string const key = cmSystemTools::UpperCase(config); - auto it = this->KindedSourcesMap.find(key); - if (it != this->KindedSourcesMap.end()) { - if (!it->second.Initialized) { - std::ostringstream e; - e << "The SOURCES of \"" << this->GetName() - << "\" use a generator expression that depends on the " - "SOURCES themselves."; - this->GlobalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); - static KindedSources empty; - return empty; - } - return it->second; - } - - // Add an entry to the map for this configuration. - KindedSources& files = this->KindedSourcesMap[key]; - this->ComputeKindedSources(files, config); - files.Initialized = true; - return files; -} - -void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, - std::string const& config) const -{ - // Get the source file paths by string. - std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); - - cmsys::RegularExpression header_regex(CM_HEADER_REGEX); - std::vector<cmSourceFile*> badObjLib; - - std::set<cmSourceFile*> emitted; - for (BT<std::string> const& s : srcs) { - // Create each source at most once. - cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); - if (!emitted.insert(sf).second) { - continue; - } - - // Compute the kind (classification) of this source file. - SourceKind kind; - std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); - cmFileSet 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)) { - kind = SourceKindCxxModuleSource; - } else if (this->Target->GetType() == cmStateEnums::UTILITY || - this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY - // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 - // NOLINTNEXTLINE(bugprone-branch-clone) - ) { - kind = SourceKindExtra; - } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) { - kind = SourceKindUnityBatched; - // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) { - kind = SourceKindHeader; - } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - kind = SourceKindExternalObject; - } else if (!sf->GetOrDetermineLanguage().empty()) { - kind = SourceKindObjectSource; - } else if (ext == "def") { - kind = SourceKindModuleDefinition; - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { - badObjLib.push_back(sf); - } - } else if (ext == "idl") { - kind = SourceKindIDL; - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { - badObjLib.push_back(sf); - } - } else if (ext == "resx") { - kind = SourceKindResx; - } else if (ext == "appxmanifest") { - kind = SourceKindAppManifest; - } else if (ext == "manifest") { - if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) { - kind = SourceKindExtra; - } else { - kind = SourceKindManifest; - } - } else if (ext == "pfx") { - kind = SourceKindCertificate; - } else if (ext == "xaml") { - kind = SourceKindXaml; - } else if (header_regex.find(sf->ResolveFullPath())) { - kind = SourceKindHeader; - } else { - kind = SourceKindExtra; - } - - // Save this classified source file in the result vector. - files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind }); - } - - if (!badObjLib.empty()) { - std::ostringstream e; - e << "OBJECT library \"" << this->GetName() << "\" contains:\n"; - for (cmSourceFile* i : badObjLib) { - e << " " << i->GetLocation().GetName() << "\n"; - } - e << "but may contain only sources that compile, header files, and " - "other files that would not affect linking of a normal library."; - this->GlobalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); - } -} - -std::vector<cmGeneratorTarget::AllConfigSource> const& -cmGeneratorTarget::GetAllConfigSources() const -{ - if (this->AllConfigSources.empty()) { - this->ComputeAllConfigSources(); - } - return this->AllConfigSources; -} - -void cmGeneratorTarget::ComputeAllConfigSources() const -{ - std::vector<std::string> configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - - std::map<cmSourceFile const*, size_t> index; - - for (size_t ci = 0; ci < configs.size(); ++ci) { - KindedSources const& sources = this->GetKindedSources(configs[ci]); - for (SourceAndKind const& src : sources.Sources) { - auto mi = index.find(src.Source.Value); - if (mi == index.end()) { - AllConfigSource acs; - acs.Source = src.Source.Value; - acs.Kind = src.Kind; - this->AllConfigSources.push_back(std::move(acs)); - std::map<cmSourceFile const*, size_t>::value_type entry( - src.Source.Value, this->AllConfigSources.size() - 1); - mi = index.insert(entry).first; - } - this->AllConfigSources[mi->second].Configs.push_back(ci); - } - } -} - -std::vector<cmGeneratorTarget::AllConfigSource> -cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const -{ - std::vector<AllConfigSource> result; - for (AllConfigSource const& source : this->GetAllConfigSources()) { - if (source.Kind == kind) { - result.push_back(source); - } - } - return result; -} - -std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const -{ - std::set<std::string> languages; - std::vector<AllConfigSource> const& sources = this->GetAllConfigSources(); - for (AllConfigSource const& si : sources) { - std::string const& lang = si.Source->GetOrDetermineLanguage(); - if (!lang.empty()) { - languages.emplace(lang); - } - } - return languages; -} - std::string cmGeneratorTarget::GetCompilePDBName( const std::string& config) const { @@ -2432,7 +1522,7 @@ cmValue build_with_install_name = this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR"); if (build_with_install_name) { - return cmIsOn(*build_with_install_name); + return build_with_install_name.IsOn(); } cmPolicies::PolicyStatus cmp0068 = this->GetPolicyStatusCMP0068(); @@ -2691,214 +1781,6 @@ return nullptr; } -class cmTargetCollectLinkLanguages -{ -public: - cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, - std::string config, - std::unordered_set<std::string>& languages, - cmGeneratorTarget const* head, bool secondPass) - : Config(std::move(config)) - , Languages(languages) - , HeadTarget(head) - , SecondPass(secondPass) - { - this->Visited.insert(target); - } - - void Visit(cmLinkItem const& item) - { - if (!item.Target) { - return; - } - if (!this->Visited.insert(item.Target).second) { - return; - } - cmLinkInterface const* iface = item.Target->GetLinkInterface( - this->Config, this->HeadTarget, this->SecondPass); - if (!iface) { - return; - } - if (iface->HadLinkLanguageSensitiveCondition) { - this->HadLinkLanguageSensitiveCondition = true; - } - - for (std::string const& language : iface->Languages) { - this->Languages.insert(language); - } - - for (cmLinkItem const& lib : iface->Libraries) { - this->Visit(lib); - } - } - - bool GetHadLinkLanguageSensitiveCondition() const - { - return this->HadLinkLanguageSensitiveCondition; - } - -private: - std::string Config; - std::unordered_set<std::string>& Languages; - cmGeneratorTarget const* HeadTarget; - std::set<cmGeneratorTarget const*> Visited; - bool SecondPass; - bool HadLinkLanguageSensitiveCondition = false; -}; - -cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( - const std::string& config) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - static LinkClosure const empty = { {}, {} }; - return ∅ - } - - std::string key(cmSystemTools::UpperCase(config)); - auto i = this->LinkClosureMap.find(key); - if (i == this->LinkClosureMap.end()) { - LinkClosure lc; - this->ComputeLinkClosure(config, lc); - LinkClosureMapType::value_type entry(key, lc); - i = this->LinkClosureMap.insert(entry).first; - } - return &i->second; -} - -class cmTargetSelectLinker -{ - int Preference = 0; - cmGeneratorTarget const* Target; - cmGlobalGenerator* GG; - std::set<std::string> Preferred; - -public: - cmTargetSelectLinker(cmGeneratorTarget const* target) - : Target(target) - { - this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); - } - void Consider(const std::string& lang) - { - int preference = this->GG->GetLinkerPreference(lang); - if (preference > this->Preference) { - this->Preference = preference; - this->Preferred.clear(); - } - if (preference == this->Preference) { - this->Preferred.insert(lang); - } - } - std::string Choose() - { - if (this->Preferred.empty()) { - return ""; - } - if (this->Preferred.size() > 1) { - std::ostringstream e; - e << "Target " << this->Target->GetName() - << " contains multiple languages with the highest linker preference" - << " (" << this->Preference << "):\n"; - for (std::string const& li : this->Preferred) { - e << " " << li << "\n"; - } - e << "Set the LINKER_LANGUAGE property for this target."; - cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); - cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Target->GetBacktrace()); - } - return *this->Preferred.begin(); - } -}; - -bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config, - LinkClosure& lc, - bool secondPass) const -{ - // Get languages built in this target. - std::unordered_set<std::string> languages; - cmLinkImplementation const* impl = - this->GetLinkImplementation(config, LinkInterfaceFor::Link, secondPass); - assert(impl); - languages.insert(impl->Languages.cbegin(), impl->Languages.cend()); - - // Add interface languages from linked targets. - // cmTargetCollectLinkLanguages cll(this, config, languages, this, - // secondPass); - cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass); - for (cmLinkImplItem const& lib : impl->Libraries) { - cll.Visit(lib); - } - - // Store the transitive closure of languages. - cm::append(lc.Languages, languages); - - // Choose the language whose linker should be used. - if (secondPass || lc.LinkerLanguage.empty()) { - // Find the language with the highest preference value. - cmTargetSelectLinker tsl(this); - - // First select from the languages compiled directly in this target. - for (std::string const& l : impl->Languages) { - tsl.Consider(l); - } - - // Now consider languages that propagate from linked targets. - for (std::string const& lang : languages) { - std::string propagates = - "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; - if (this->Makefile->IsOn(propagates)) { - tsl.Consider(lang); - } - } - - lc.LinkerLanguage = tsl.Choose(); - } - - return impl->HadLinkLanguageSensitiveCondition || - cll.GetHadLinkLanguageSensitiveCondition(); -} - -void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, - LinkClosure& lc) const -{ - bool secondPass = false; - - { - LinkClosure linkClosure; - linkClosure.LinkerLanguage = this->LinkerLanguage; - - bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") || - !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty(); - - // Get languages built in this target. - secondPass = this->ComputeLinkClosure(config, linkClosure, false) && - !hasHardCodedLinkerLanguage; - this->LinkerLanguage = linkClosure.LinkerLanguage; - if (!secondPass) { - lc = std::move(linkClosure); - } - } - - if (secondPass) { - LinkClosure linkClosure; - - this->ComputeLinkClosure(config, linkClosure, secondPass); - lc = std::move(linkClosure); - - // linker language must not be changed between the two passes - if (this->LinkerLanguage != lc.LinkerLanguage) { - std::ostringstream e; - e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> " - "changes\nthe linker language for target \"" - << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '" - << lc.LinkerLanguage << "') which is invalid."; - cmSystemTools::Error(e.str()); - } - } -} - cmGeneratorTarget::NameComponents const& cmGeneratorTarget::GetFullNameComponents( std::string const& config, cmStateEnums::ArtifactType artifact) const @@ -3056,303 +1938,13 @@ return; } - cmGeneratorExpressionDAGChecker dagChecker(this, "AUTOUIC_OPTIONS", nullptr, - nullptr); + cmGeneratorExpressionDAGChecker dagChecker( + this, "AUTOUIC_OPTIONS", nullptr, nullptr, this->LocalGenerator, config); cmExpandList(cmGeneratorExpression::Evaluate(prop, this->LocalGenerator, config, this, &dagChecker), result); } -static void processILibs(const std::string& config, - cmGeneratorTarget const* headTarget, - cmLinkItem const& item, cmGlobalGenerator* gg, - std::vector<cmGeneratorTarget const*>& tgts, - std::set<cmGeneratorTarget const*>& emitted) -{ - if (item.Target && emitted.insert(item.Target).second) { - tgts.push_back(item.Target); - if (cmLinkInterfaceLibraries const* iface = - item.Target->GetLinkInterfaceLibraries(config, headTarget, - LinkInterfaceFor::Usage)) { - for (cmLinkItem const& lib : iface->Libraries) { - processILibs(config, headTarget, lib, gg, tgts, emitted); - } - } - } -} - -const std::vector<const cmGeneratorTarget*>& -cmGeneratorTarget::GetLinkImplementationClosure( - const std::string& config) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - static std::vector<const cmGeneratorTarget*> const empty; - return empty; - } - - LinkImplClosure& tgts = this->LinkImplClosureMap[config]; - if (!tgts.Done) { - tgts.Done = true; - std::set<cmGeneratorTarget const*> emitted; - - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Usage); - assert(impl); - - for (cmLinkImplItem const& lib : impl->Libraries) { - processILibs(config, this, lib, - this->LocalGenerator->GetGlobalGenerator(), tgts, emitted); - } - } - return tgts; -} - -class cmTargetTraceDependencies -{ -public: - cmTargetTraceDependencies(cmGeneratorTarget* target); - void Trace(); - -private: - cmGeneratorTarget* GeneratorTarget; - cmMakefile* Makefile; - cmLocalGenerator* LocalGenerator; - cmGlobalGenerator const* GlobalGenerator; - using SourceEntry = cmGeneratorTarget::SourceEntry; - SourceEntry* CurrentEntry; - std::queue<cmSourceFile*> SourceQueue; - std::set<cmSourceFile*> SourcesQueued; - using NameMapType = std::map<std::string, cmSourcesWithOutput>; - NameMapType NameMap; - std::vector<std::string> NewSources; - - void QueueSource(cmSourceFile* sf); - void FollowName(std::string const& name); - void FollowNames(std::vector<std::string> const& names); - bool IsUtility(std::string const& dep); - void CheckCustomCommand(cmCustomCommand const& cc); - void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); -}; - -cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) - : GeneratorTarget(target) -{ - // Convenience. - this->Makefile = target->Target->GetMakefile(); - this->LocalGenerator = target->GetLocalGenerator(); - this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); - this->CurrentEntry = nullptr; - - // Queue all the source files already specified for the target. - std::set<cmSourceFile*> emitted; - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& c : configs) { - std::vector<cmSourceFile*> sources; - this->GeneratorTarget->GetSourceFiles(sources, c); - for (cmSourceFile* sf : sources) { - const std::set<cmGeneratorTarget const*> tgts = - this->GlobalGenerator->GetFilenameTargetDepends(sf); - if (cm::contains(tgts, this->GeneratorTarget)) { - std::ostringstream e; - e << "Evaluation output file\n \"" << sf->ResolveFullPath() - << "\"\ndepends on the sources of a target it is used in. This " - "is a dependency loop and is not allowed."; - this->GeneratorTarget->LocalGenerator->IssueMessage( - MessageType::FATAL_ERROR, e.str()); - return; - } - if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - } - } - } - - // Queue pre-build, pre-link, and post-build rule dependencies. - this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); - this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); - this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); -} - -void cmTargetTraceDependencies::Trace() -{ - // Process one dependency at a time until the queue is empty. - while (!this->SourceQueue.empty()) { - // Get the next source from the queue. - cmSourceFile* sf = this->SourceQueue.front(); - this->SourceQueue.pop(); - this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; - - // Queue dependencies added explicitly by the user. - if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { - cmList objDeps{ *additionalDeps }; - for (auto& objDep : objDeps) { - if (cmSystemTools::FileIsFullPath(objDep)) { - objDep = cmSystemTools::CollapseFullPath(objDep); - } - } - this->FollowNames(objDeps); - } - - // Queue the source needed to generate this file, if any. - this->FollowName(sf->ResolveFullPath()); - - // Queue dependencies added programmatically by commands. - this->FollowNames(sf->GetDepends()); - - // Queue custom command dependencies. - if (cmCustomCommand const* cc = sf->GetCustomCommand()) { - this->CheckCustomCommand(*cc); - } - } - this->CurrentEntry = nullptr; - - this->GeneratorTarget->AddTracedSources(this->NewSources); -} - -void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) -{ - if (this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - - // Make sure this file is in the target at the end. - this->NewSources.push_back(sf->ResolveFullPath()); - } -} - -void cmTargetTraceDependencies::FollowName(std::string const& name) -{ - // Use lower bound with key comparison to not repeat the search for the - // insert position if the name could not be found (which is the common case). - auto i = this->NameMap.lower_bound(name); - if (i == this->NameMap.end() || i->first != name) { - // Check if we know how to generate this file. - cmSourcesWithOutput sources = - this->LocalGenerator->GetSourcesWithOutput(name); - // If we failed to find a target or source and we have a relative path, it - // might be a valid source if made relative to the current binary - // directory. - if (!sources.Target && !sources.Source && - !cmSystemTools::FileIsFullPath(name)) { - auto fullname = - cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); - fullname = cmSystemTools::CollapseFullPath( - fullname, this->Makefile->GetHomeOutputDirectory()); - sources = this->LocalGenerator->GetSourcesWithOutput(fullname); - } - i = this->NameMap.emplace_hint(i, name, sources); - } - if (cmTarget* t = i->second.Target) { - // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or - // POST_BUILD command. - this->GeneratorTarget->Target->AddUtility(t->GetName(), false); - } - if (cmSourceFile* sf = i->second.Source) { - // For now only follow the dependency if the source file is not a - // byproduct. Semantics of byproducts in a non-Ninja context will have to - // be defined first. - if (!i->second.SourceIsByproduct) { - // Record the dependency we just followed. - if (this->CurrentEntry) { - this->CurrentEntry->Depends.push_back(sf); - } - this->QueueSource(sf); - } - } -} - -void cmTargetTraceDependencies::FollowNames( - std::vector<std::string> const& names) -{ - for (std::string const& name : names) { - this->FollowName(name); - } -} - -bool cmTargetTraceDependencies::IsUtility(std::string const& dep) -{ - // Dependencies on targets (utilities) are supposed to be named by - // just the target name. However for compatibility we support - // naming the output file generated by the target (assuming there is - // no output-name property which old code would not have set). In - // that case the target name will be the file basename of the - // dependency. - std::string util = cmSystemTools::GetFilenameName(dep); - if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { - util = cmSystemTools::GetFilenameWithoutLastExtension(util); - } - - // Check for a target with this name. - if (cmGeneratorTarget* t = - this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( - util)) { - // If we find the target and the dep was given as a full path, - // then make sure it was not a full path to something else, and - // the fact that the name matched a target was just a coincidence. - if (cmSystemTools::FileIsFullPath(dep)) { - if (t->GetType() >= cmStateEnums::EXECUTABLE && - t->GetType() <= cmStateEnums::MODULE_LIBRARY) { - // This is really only for compatibility so we do not need to - // worry about configuration names and output names. - std::string tLocation = t->GetLocationForBuild(); - tLocation = cmSystemTools::GetFilenamePath(tLocation); - std::string depLocation = cmSystemTools::GetFilenamePath(dep); - depLocation = cmSystemTools::CollapseFullPath(depLocation); - tLocation = cmSystemTools::CollapseFullPath(tLocation); - if (depLocation == tLocation) { - this->GeneratorTarget->Target->AddUtility(util, false); - return true; - } - } - } else { - // The original name of the dependency was not a full path. It - // must name a target, so add the target-level dependency. - this->GeneratorTarget->Target->AddUtility(util, true); - return true; - } - } - - // The dependency does not name a target built in this project. - return false; -} - -void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) -{ - // Collect dependencies referenced by all configurations. - std::set<std::string> depends; - for (std::string const& config : - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { - for (cmCustomCommandGenerator const& ccg : - this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { - // Collect target-level dependencies referenced in command lines. - for (auto const& util : ccg.GetUtilities()) { - this->GeneratorTarget->Target->AddUtility(util); - } - - // Collect file-level dependencies referenced in DEPENDS. - depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); - } - } - - // Queue file-level dependencies. - for (std::string const& dep : depends) { - if (!this->IsUtility(dep)) { - // The dependency does not name a target and may be a file we - // know how to generate. Queue it. - this->FollowName(dep); - } - } -} - -void cmTargetTraceDependencies::CheckCustomCommands( - const std::vector<cmCustomCommand>& commands) -{ - for (cmCustomCommand const& command : commands) { - this->CheckCustomCommand(command); - } -} - void cmGeneratorTarget::TraceDependencies() { // CMake-generated targets have no dependencies to trace. Normally tracing @@ -3738,569 +2330,18 @@ return cmSystemTools::CollapseFullPath(path); } -namespace { -void processIncludeDirectories(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, - std::vector<BT<std::string>>& includes, - std::unordered_set<std::string>& uniqueIncludes, - bool debugIncludes) +std::vector<std::string> cmGeneratorTarget::GetPchArchs( + std::string const& config, std::string const& lang) const { - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { - cmLinkImplItem const& item = entry.LinkImplItem; - std::string const& targetName = item.AsStr(); - bool const fromImported = item.Target && item.Target->IsImported(); - bool const checkCMP0027 = item.CheckCMP0027; - - std::string usedIncludes; - for (std::string& entryInclude : entry.Values) { - if (fromImported && !cmSystemTools::FileExists(entryInclude)) { - std::ostringstream e; - MessageType messageType = MessageType::FATAL_ERROR; - if (checkCMP0027) { - switch (tgt->GetPolicyStatusCMP0027()) { - case cmPolicies::WARN: - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n"; - CM_FALLTHROUGH; - case cmPolicies::OLD: - messageType = MessageType::AUTHOR_WARNING; - break; - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::NEW: - break; - } - } - /* clang-format off */ - e << "Imported target \"" << targetName << "\" includes " - "non-existent path\n \"" << entryInclude << "\"\nin its " - "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n" - "* The path was deleted, renamed, or moved to another " - "location.\n" - "* An install or uninstall procedure did not complete " - "successfully.\n" - "* The installation package was faulty and references files it " - "does not provide.\n"; - /* clang-format on */ - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - return; - } - - if (!cmSystemTools::FileIsFullPath(entryInclude)) { - 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_INCLUDE_DIRECTORIES:\n" - " \"" << entryInclude << "\""; - /* clang-format on */ - } else { - switch (tgt->GetPolicyStatusCMP0021()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - e << "Found relative path while evaluating include directories of " - "\"" - << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n"; - } - if (!noMessage) { - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - - if (!cmIsOff(entryInclude)) { - cmSystemTools::ConvertToUnixSlashes(entryInclude); - } - - if (uniqueIncludes.insert(entryInclude).second) { - includes.emplace_back(entryInclude, entry.Backtrace); - if (debugIncludes) { - usedIncludes += " * " + entryInclude + "\n"; - } - } - } - if (!usedIncludes.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used includes for target ") + tgt->GetName() + ":\n" + - usedIncludes, - entry.Backtrace); - } + std::vector<std::string> pchArchs; + if (!this->GetGlobalGenerator()->IsXcode()) { + pchArchs = this->GetAppleArchs(config, lang); } -} -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( - const std::string& config, const std::string& lang) const -{ - ConfigAndLanguage cacheKey(config, lang); - { - auto it = this->IncludeDirectoriesCache.find(cacheKey); - if (it != this->IncludeDirectoriesCache.end()) { - return it->second; - } + if (pchArchs.size() < 2) { + // We do not need per-arch PCH files when building for one architecture. + pchArchs = { {} }; } - std::vector<BT<std::string>> includes; - std::unordered_set<std::string> uniqueIncludes; - - cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES", - nullptr, nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugIncludes = !this->DebugIncludesDone && - cm::contains(debugProperties, "INCLUDE_DIRECTORIES"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugIncludesDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, lang, &dagChecker, this->IncludeDirectoriesEntries); - - if (lang == "Swift") { - AddLangSpecificImplicitIncludeDirectories( - this, lang, config, "Swift_MODULE_DIRECTORY", - IncludeDirectoryFallBack::BINARY, entries); - } - - if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) { - - const std::string propertyName = "ISPC_HEADER_DIRECTORY"; - - // If this target has ISPC sources make sure to add the header - // directory to other compilation units - if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) { - if (cmValue val = this->GetProperty(propertyName)) { - includes.emplace_back(*val); - } else { - includes.emplace_back(this->GetObjectDirectory(config)); - } - } - - AddLangSpecificImplicitIncludeDirectories( - this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT, - entries); - } - - AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - processIncludeDirectories(this, entries, includes, uniqueIncludes, - debugIncludes); - - if (this->IsApple()) { - if (cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config, - LinkInterfaceFor::Usage)) { - for (cmLinkImplItem const& lib : impl->Libraries) { - std::string libDir; - if (lib.Target == nullptr) { - libDir = cmSystemTools::CollapseFullPath( - lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); - } else if (lib.Target->Target->IsFrameworkOnApple() || - this->IsImportedFrameworkFolderOnApple(config)) { - libDir = lib.Target->GetLocation(config); - } else { - continue; - } - - auto fwDescriptor = - this->GetGlobalGenerator()->SplitFrameworkPath(libDir); - if (!fwDescriptor) { - continue; - } - - auto fwInclude = fwDescriptor->GetFrameworkPath(); - if (uniqueIncludes.insert(fwInclude).second) { - includes.emplace_back(fwInclude, cmListFileBacktrace()); - } - } - } - } - - this->IncludeDirectoriesCache.emplace(cacheKey, includes); - return includes; -} - -enum class OptionsParse -{ - None, - Shell -}; - -namespace { -const auto DL_BEGIN = "<DEVICE_LINK>"_s; -const auto DL_END = "</DEVICE_LINK>"_s; - -void processOptions(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries const& entries, - std::vector<BT<std::string>>& options, - std::unordered_set<std::string>& uniqueOptions, - bool debugOptions, const char* logName, OptionsParse parse, - bool processDeviceOptions = false) -{ - bool splitOption = !processDeviceOptions; - for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { - std::string usedOptions; - for (std::string const& opt : entry.Values) { - if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) { - options.emplace_back(opt, entry.Backtrace); - splitOption = opt == DL_BEGIN; - continue; - } - - if (uniqueOptions.insert(opt).second) { - if (parse == OptionsParse::Shell && - cmHasLiteralPrefix(opt, "SHELL:")) { - if (splitOption) { - 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(std::string(opt.c_str() + 6), - entry.Backtrace); - } - } else { - options.emplace_back(opt, entry.Backtrace); - } - if (debugOptions) { - usedOptions += " * " + opt + "\n"; - } - } - } - if (!usedOptions.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used ") + logName + std::string(" for target ") + - tgt->GetName() + ":\n" + usedOptions, - entry.Backtrace); - } - } -} - -std::vector<BT<std::string>> wrapOptions( - std::vector<std::string>& options, const cmListFileBacktrace& bt, - const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep, - bool concatFlagAndArgs) -{ - std::vector<BT<std::string>> result; - - if (options.empty()) { - return result; - } - - if (wrapperFlag.empty()) { - // nothing specified, insert elements as is - result.reserve(options.size()); - for (std::string& o : options) { - result.emplace_back(std::move(o), bt); - } - return result; - } - - for (std::vector<std::string>::size_type index = 0; index < options.size(); - index++) { - if (cmHasLiteralPrefix(options[index], "LINKER:")) { - // LINKER wrapper specified, insert elements as is - result.emplace_back(std::move(options[index]), bt); - continue; - } - if (cmHasLiteralPrefix(options[index], "-Wl,")) { - // replace option by LINKER wrapper - result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt); - continue; - } - if (cmHasLiteralPrefix(options[index], "-Xlinker=")) { - // replace option by LINKER wrapper - result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt); - continue; - } - if (options[index] == "-Xlinker") { - // replace option by LINKER wrapper - if (index + 1 < options.size()) { - result.emplace_back("LINKER:" + options[++index], bt); - } else { - result.emplace_back(std::move(options[index]), bt); - } - continue; - } - - // collect all options which must be transformed - std::vector<std::string> opts; - while (index < options.size()) { - if (!cmHasLiteralPrefix(options[index], "LINKER:") && - !cmHasLiteralPrefix(options[index], "-Wl,") && - !cmHasLiteralPrefix(options[index], "-Xlinker")) { - opts.emplace_back(std::move(options[index++])); - } else { - --index; - break; - } - } - if (opts.empty()) { - continue; - } - - if (!wrapperSep.empty()) { - if (concatFlagAndArgs) { - // insert flag elements except last one - for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { - result.emplace_back(*i, bt); - } - // concatenate last flag element and all list values - // in one option - result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); - } else { - for (std::string const& i : wrapperFlag) { - result.emplace_back(i, bt); - } - // concatenate all list values in one option - result.emplace_back(cmJoin(opts, wrapperSep), bt); - } - } else { - // prefix each element of list with wrapper - if (concatFlagAndArgs) { - std::transform(opts.begin(), opts.end(), opts.begin(), - [&wrapperFlag](std::string const& o) -> std::string { - return wrapperFlag.back() + o; - }); - } - for (std::string& o : opts) { - for (auto i = wrapperFlag.begin(), - e = concatFlagAndArgs ? wrapperFlag.end() - 1 - : wrapperFlag.end(); - i != e; ++i) { - result.emplace_back(*i, bt); - } - result.emplace_back(std::move(o), bt); - } - } - } - return result; -} -} - -void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::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; - } - } - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_OPTIONS", nullptr, - nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugOptions = !this->DebugCompileOptionsDone && - cm::contains(debugProperties, "COMPILE_OPTIONS"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugCompileOptionsDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->CompileOptionsEntries); - - AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - processOptions(this, entries, result, uniqueOptions, debugOptions, - "compile options", OptionsParse::Shell); - - CompileOptionsCache.emplace(cacheKey, result); - return result; -} - -void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, - const std::string& config) const -{ - std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures( - std::string const& config) const -{ - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueFeatures; - - cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_FEATURES", nullptr, - nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugFeatures = !this->DebugCompileFeaturesDone && - cm::contains(debugProperties, "COMPILE_FEATURES"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugCompileFeaturesDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, std::string(), &dagChecker, this->CompileFeaturesEntries); - - AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES", - std::string(), &dagChecker, entries, - IncludeRuntimeInterface::Yes); - - processOptions(this, entries, result, uniqueFeatures, debugFeatures, - "compile features", OptionsParse::None); - - return result; -} - -void cmGeneratorTarget::GetCompileDefinitions( - std::vector<std::string>& result, const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = - this->GetCompileDefinitions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::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; - } - } - std::vector<BT<std::string>> list; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS", - nullptr, nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugDefines = !this->DebugCompileDefinitionsDone && - cm::contains(debugProperties, "COMPILE_DEFINITIONS"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugCompileDefinitionsDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->CompileDefinitionsEntries); - - AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - if (!config.empty()) { - std::string configPropName = - "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); - cmValue configProp = this->GetProperty(configPropName); - if (configProp) { - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) { - case cmPolicies::WARN: { - this->LocalGenerator->IssueMessage( - MessageType::AUTHOR_WARNING, - cmPolicies::GetPolicyWarning(cmPolicies::CMP0043)); - CM_FALLTHROUGH; - } - case cmPolicies::OLD: { - std::unique_ptr<TargetPropertyEntry> entry = - CreateTargetPropertyEntry( - *this->LocalGenerator->GetCMakeInstance(), *configProp); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - this, config, language, &dagChecker, *entry)); - } break; - case cmPolicies::NEW: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - break; - } - } - } - - processOptions(this, entries, list, uniqueOptions, debugDefines, - "compile definitions", OptionsParse::None); - - this->CompileDefinitionsCache.emplace(cacheKey, list); - return list; -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders( - const std::string& config, const std::string& language) const -{ - ConfigAndLanguage cacheKey(config, language); - { - auto it = this->PrecompileHeadersCache.find(cacheKey); - if (it != this->PrecompileHeadersCache.end()) { - return it->second; - } - } - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS", - nullptr, nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugDefines = !this->DebugPrecompileHeadersDone && - std::find(debugProperties.begin(), debugProperties.end(), - "PRECOMPILE_HEADERS") != debugProperties.end(); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugPrecompileHeadersDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->PrecompileHeadersEntries); - - AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes); - - std::vector<BT<std::string>> list; - processOptions(this, entries, list, uniqueOptions, debugDefines, - "precompile headers", OptionsParse::None); - - this->PrecompileHeadersCache.emplace(cacheKey, list); - return list; + return pchArchs; } std::string cmGeneratorTarget::GetPchHeader(const std::string& config, @@ -4634,388 +2675,6 @@ this->UnityBatchedSourceFiles.end(); } -void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - if (this->IsDeviceLink() && - this->GetPolicyStatusCMP0105() != cmPolicies::NEW) { - // link options are not propagated to the device link step - return; - } - - std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( - std::string const& config, std::string const& language) const -{ - ConfigAndLanguage cacheKey( - config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); - { - auto it = this->LinkOptionsCache.find(cacheKey); - if (it != this->LinkOptionsCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_OPTIONS", nullptr, - nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugOptions = !this->DebugLinkOptionsDone && - cm::contains(debugProperties, "LINK_OPTIONS"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugLinkOptionsDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->LinkOptionsEntries); - - AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes, - this->GetPolicyStatusCMP0099() == cmPolicies::NEW - ? LinkInterfaceFor::Link - : LinkInterfaceFor::Usage); - - processOptions(this, entries, result, uniqueOptions, debugOptions, - "link options", OptionsParse::Shell, this->IsDeviceLink()); - - if (this->IsDeviceLink()) { - // wrap host link options - const std::string wrapper(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG")); - cmList wrapperFlag{ wrapper }; - const std::string wrapperSep(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP")); - bool concatFlagAndArgs = true; - if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { - concatFlagAndArgs = false; - wrapperFlag.pop_back(); - } - - auto it = result.begin(); - while (it != result.end()) { - if (it->Value == DL_BEGIN) { - // device link options, no treatment - it = result.erase(it); - it = std::find_if(it, result.end(), [](const BT<std::string>& item) { - return item.Value == DL_END; - }); - if (it != result.end()) { - it = result.erase(it); - } - } else { - // host link options must be wrapped - std::vector<std::string> options; - cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options); - auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag, - wrapperSep, concatFlagAndArgs); - it = result.erase(it); - // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect - // C++11 standard: 'std::vector::insert()' do not returns an iterator, - // so need to recompute the iterator after insertion. - if (it == result.end()) { - cm::append(result, hostOptions); - it = result.end(); - } else { - auto index = it - result.begin(); - result.insert(it, hostOptions.begin(), hostOptions.end()); - it = result.begin() + index + hostOptions.size(); - } - } - } - } - - // Last step: replace "LINKER:" prefixed elements by - // actual linker wrapper - result = this->ResolveLinkerWrapper(result, language); - - this->LinkOptionsCache.emplace(cacheKey, result); - return result; -} - -std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( - std::vector<BT<std::string>>& result, const std::string& language, - bool joinItems) const -{ - // replace "LINKER:" prefixed elements by actual linker wrapper - const std::string wrapper(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + - (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG" - : "_LINKER_WRAPPER_FLAG"))); - cmList wrapperFlag{ wrapper }; - const std::string wrapperSep(this->Makefile->GetSafeDefinition( - "CMAKE_" + language + - (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP" - : "_LINKER_WRAPPER_FLAG_SEP"))); - bool concatFlagAndArgs = true; - if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { - concatFlagAndArgs = false; - wrapperFlag.pop_back(); - } - - const std::string LINKER{ "LINKER:" }; - const std::string SHELL{ "SHELL:" }; - const std::string LINKER_SHELL = LINKER + SHELL; - - std::vector<BT<std::string>>::iterator entry; - while ((entry = std::find_if(result.begin(), result.end(), - [&LINKER](BT<std::string> const& item) -> bool { - return item.Value.compare(0, LINKER.length(), - LINKER) == 0; - })) != result.end()) { - std::string value = std::move(entry->Value); - cmListFileBacktrace bt = std::move(entry->Backtrace); - entry = result.erase(entry); - - std::vector<std::string> linkerOptions; - if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) { - cmSystemTools::ParseUnixCommandLine( - value.c_str() + LINKER_SHELL.length(), linkerOptions); - } else { - linkerOptions = cmTokenize(value.substr(LINKER.length()), ","); - } - - if (linkerOptions.empty() || - (linkerOptions.size() == 1 && linkerOptions.front().empty())) { - continue; - } - - // for now, raise an error if prefix SHELL: is part of arguments - if (std::find_if(linkerOptions.begin(), linkerOptions.end(), - [&SHELL](const std::string& item) -> bool { - return item.find(SHELL) != std::string::npos; - }) != linkerOptions.end()) { - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, - "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.", - this->GetBacktrace()); - return result; - } - - std::vector<BT<std::string>> options = wrapOptions( - linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs); - if (joinItems) { - result.insert(entry, - cmJoin(cmRange<decltype(options.cbegin())>( - options.cbegin(), options.cend()), - " "_s)); - } else { - result.insert(entry, options.begin(), options.end()); - } - } - return result; -} - -void cmGeneratorTarget::GetStaticLibraryLinkOptions( - std::vector<std::string>& result, const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = - this->GetStaticLibraryLinkOptions(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( - std::string const& config, std::string const& language) const -{ - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - - cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS", - nullptr, nullptr); - - EvaluatedTargetPropertyEntries entries; - if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { - std::unique_ptr<TargetPropertyEntry> entry = CreateTargetPropertyEntry( - *this->LocalGenerator->GetCMakeInstance(), *linkOptions); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - this, config, language, &dagChecker, *entry)); - } - processOptions(this, entries, result, uniqueOptions, false, - "static library link options", OptionsParse::Shell); - - return result; -} - -namespace { -void processLinkDirectories(cmGeneratorTarget const* tgt, - EvaluatedTargetPropertyEntries& entries, - std::vector<BT<std::string>>& directories, - std::unordered_set<std::string>& uniqueDirectories, - bool debugDirectories) -{ - for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { - cmLinkImplItem const& item = entry.LinkImplItem; - 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::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - e << "Found relative path while evaluating link directories of " - "\"" - << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; - } - if (!noMessage) { - tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - - // Sanitize the path the same way the link_directories command does - // in case projects set the LINK_DIRECTORIES property directly. - cmSystemTools::ConvertToUnixSlashes(entryDirectory); - if (uniqueDirectories.insert(entryDirectory).second) { - directories.emplace_back(entryDirectory, entry.Backtrace); - if (debugDirectories) { - usedDirectories += " * " + entryDirectory + "\n"; - } - } - } - if (!usedDirectories.empty()) { - tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::LOG, - std::string("Used link directories for target ") + tgt->GetName() + - ":\n" + usedDirectories, - entry.Backtrace); - } - } -} -} - -void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = - this->GetLinkDirectories(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories( - std::string const& config, std::string const& language) const -{ - ConfigAndLanguage cacheKey( - config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); - { - auto it = this->LinkDirectoriesCache.find(cacheKey); - if (it != this->LinkDirectoriesCache.end()) { - return it->second; - } - } - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueDirectories; - - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr, - nullptr); - - cmList debugProperties{ this->Makefile->GetDefinition( - "CMAKE_DEBUG_TARGET_PROPERTIES") }; - bool debugDirectories = !this->DebugLinkDirectoriesDone && - cm::contains(debugProperties, "LINK_DIRECTORIES"); - - if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { - this->DebugLinkDirectoriesDone = true; - } - - EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( - this, config, language, &dagChecker, this->LinkDirectoriesEntries); - - AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes, - this->GetPolicyStatusCMP0099() == cmPolicies::NEW - ? LinkInterfaceFor::Link - : LinkInterfaceFor::Usage); - - processLinkDirectories(this, entries, result, uniqueDirectories, - debugDirectories); - - this->LinkDirectoriesCache.emplace(cacheKey, result); - return result; -} - -void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result, - const std::string& config, - const std::string& language) const -{ - std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language); - result.reserve(tmp.size()); - for (BT<std::string>& v : tmp) { - result.emplace_back(std::move(v.Value)); - } -} - -std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( - std::string const& config, std::string const& language) const -{ - std::vector<BT<std::string>> result; - std::unordered_set<std::string> uniqueOptions; - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DEPENDS", nullptr, - nullptr); - - EvaluatedTargetPropertyEntries entries; - if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) { - cmList depends{ *linkDepends }; - for (const auto& depend : depends) { - std::unique_ptr<TargetPropertyEntry> entry = CreateTargetPropertyEntry( - *this->LocalGenerator->GetCMakeInstance(), depend); - entries.Entries.emplace_back(EvaluateTargetPropertyEntry( - this, config, language, &dagChecker, *entry)); - } - } - AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language, - &dagChecker, entries, IncludeRuntimeInterface::Yes, - this->GetPolicyStatusCMP0099() == cmPolicies::NEW - ? LinkInterfaceFor::Link - : LinkInterfaceFor::Usage); - - processOptions(this, entries, result, uniqueOptions, false, "link depends", - OptionsParse::None); - - return result; -} - void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const { if (this->IsImported()) { @@ -5822,675 +3481,6 @@ } } -const cmGeneratorTarget::CompatibleInterfacesBase& -cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const -{ - cmGeneratorTarget::CompatibleInterfaces& compat = - this->CompatibleInterfacesMap[config]; - if (!compat.Done) { - compat.Done = true; - compat.PropsBool.insert("POSITION_INDEPENDENT_CODE"); - compat.PropsString.insert("AUTOUIC_OPTIONS"); - std::vector<cmGeneratorTarget const*> const& deps = - this->GetLinkImplementationClosure(config); - for (cmGeneratorTarget const* li : deps) { -#define CM_READ_COMPATIBLE_INTERFACE(X, x) \ - if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \ - cmList props(*prop); \ - compat.Props##x.insert(props.begin(), props.end()); \ - } - CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool) - CM_READ_COMPATIBLE_INTERFACE(STRING, String) - CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin) - CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax) -#undef CM_READ_COMPATIBLE_INTERFACE - } - } - return compat; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0; -} - -bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty( - const std::string& p, const std::string& config) const -{ - if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return false; - } - return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0; -} - -enum CompatibleType -{ - BoolType, - StringType, - NumberMinType, - NumberMaxType -}; - -template <typename PropertyType> -PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, - const std::string& prop, - const std::string& config, - CompatibleType, PropertyType*); - -template <> -bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, - const std::string& prop, - const std::string& config, - CompatibleType /*unused*/, - bool* /*unused*/) -{ - return tgt->GetLinkInterfaceDependentBoolProperty(prop, config); -} - -template <> -const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, - const std::string& prop, - const std::string& config, - CompatibleType t, - const char** /*unused*/) -{ - switch (t) { - case BoolType: - assert(false && - "String compatibility check function called for boolean"); - return nullptr; - case StringType: - return tgt->GetLinkInterfaceDependentStringProperty(prop, config); - case NumberMinType: - return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config); - case NumberMaxType: - return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config); - } - assert(false && "Unreachable!"); - return nullptr; -} - -template <typename PropertyType> -void checkPropertyConsistency(cmGeneratorTarget const* depender, - cmGeneratorTarget const* dependee, - const std::string& propName, - std::set<std::string>& emitted, - const std::string& config, CompatibleType t, - PropertyType* /*unused*/) -{ - cmValue prop = dependee->GetProperty(propName); - if (!prop) { - return; - } - - cmList props{ *prop }; - std::string pdir = - cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/"); - - for (std::string const& p : props) { - std::string pname = cmSystemTools::HelpFileName(p); - std::string pfile = pdir + pname + ".rst"; - if (cmSystemTools::FileExists(pfile, true)) { - std::ostringstream e; - e << "Target \"" << dependee->GetName() << "\" has property \"" << p - << "\" listed in its " << propName - << " property. " - "This is not allowed. Only user-defined properties may appear " - "listed in the " - << propName << " property."; - depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, - e.str()); - return; - } - if (emitted.insert(p).second) { - getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t, - nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - } - } -} - -namespace { -std::string intersect(const std::set<std::string>& s1, - const std::set<std::string>& s2) -{ - std::set<std::string> intersect; - std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), - std::inserter(intersect, intersect.begin())); - if (!intersect.empty()) { - return *intersect.begin(); - } - return ""; -} - -std::string intersect(const std::set<std::string>& s1, - const std::set<std::string>& s2, - const std::set<std::string>& s3) -{ - std::string result; - result = intersect(s1, s2); - if (!result.empty()) { - return result; - } - result = intersect(s1, s3); - if (!result.empty()) { - return result; - } - return intersect(s2, s3); -} - -std::string intersect(const std::set<std::string>& s1, - const std::set<std::string>& s2, - const std::set<std::string>& s3, - const std::set<std::string>& s4) -{ - std::string result; - result = intersect(s1, s2); - if (!result.empty()) { - return result; - } - result = intersect(s1, s3); - if (!result.empty()) { - return result; - } - result = intersect(s1, s4); - if (!result.empty()) { - return result; - } - return intersect(s2, s3, s4); -} -} - -void cmGeneratorTarget::CheckPropertyCompatibility( - cmComputeLinkInformation& info, const std::string& config) const -{ - const cmComputeLinkInformation::ItemVector& deps = info.GetItems(); - - std::set<std::string> emittedBools; - static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL"; - std::set<std::string> emittedStrings; - static const std::string strString = "COMPATIBLE_INTERFACE_STRING"; - std::set<std::string> emittedMinNumbers; - static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN"; - std::set<std::string> emittedMaxNumbers; - static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX"; - - for (auto const& dep : deps) { - if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - continue; - } - - checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools, - config, BoolType, nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - checkPropertyConsistency<const char*>(this, dep.Target, strString, - emittedStrings, config, StringType, - nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - checkPropertyConsistency<const char*>(this, dep.Target, strNumMin, - emittedMinNumbers, config, - NumberMinType, nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - checkPropertyConsistency<const char*>(this, dep.Target, strNumMax, - emittedMaxNumbers, config, - NumberMaxType, nullptr); - if (cmSystemTools::GetErrorOccurredFlag()) { - return; - } - } - - std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers, - emittedMaxNumbers); - - if (!prop.empty()) { - // Use a sorted std::vector to keep the error message sorted. - std::vector<std::string> props; - auto i = emittedBools.find(prop); - if (i != emittedBools.end()) { - props.push_back(strBool); - } - i = emittedStrings.find(prop); - if (i != emittedStrings.end()) { - props.push_back(strString); - } - i = emittedMinNumbers.find(prop); - if (i != emittedMinNumbers.end()) { - props.push_back(strNumMin); - } - i = emittedMaxNumbers.find(prop); - if (i != emittedMaxNumbers.end()) { - props.push_back(strNumMax); - } - std::sort(props.begin(), props.end()); - - std::string propsString = cmStrCat( - cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back()); - - std::ostringstream e; - e << "Property \"" << prop << "\" appears in both the " << propsString - << " property in the dependencies of target \"" << this->GetName() - << "\". This is not allowed. A property may only require " - "compatibility " - "in a boolean interpretation, a numeric minimum, a numeric maximum " - "or a " - "string interpretation, but not a mixture."; - this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str()); - } -} - -template <typename PropertyType> -std::string valueAsString(PropertyType); -template <> -std::string valueAsString<bool>(bool value) -{ - return value ? "TRUE" : "FALSE"; -} -template <> -std::string valueAsString<const char*>(const char* value) -{ - return value ? value : "(unset)"; -} -template <> -std::string valueAsString<std::string>(std::string value) -{ - return value; -} -template <> -std::string valueAsString<cmValue>(cmValue value) -{ - return value ? *value : std::string("(unset)"); -} -template <> -std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/) -{ - return "(unset)"; -} - -static std::string compatibilityType(CompatibleType t) -{ - switch (t) { - case BoolType: - return "Boolean compatibility"; - case StringType: - return "String compatibility"; - case NumberMaxType: - return "Numeric maximum compatibility"; - case NumberMinType: - return "Numeric minimum compatibility"; - } - assert(false && "Unreachable!"); - return ""; -} - -static std::string compatibilityAgree(CompatibleType t, bool dominant) -{ - switch (t) { - case BoolType: - case StringType: - return dominant ? "(Disagree)\n" : "(Agree)\n"; - case NumberMaxType: - case NumberMinType: - return dominant ? "(Dominant)\n" : "(Ignored)\n"; - } - assert(false && "Unreachable!"); - return ""; -} - -template <typename PropertyType> -PropertyType getTypedProperty( - cmGeneratorTarget const* tgt, const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter = nullptr); - -template <> -bool getTypedProperty<bool>(cmGeneratorTarget const* tgt, - const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter) -{ - if (genexInterpreter == nullptr) { - return tgt->GetPropertyAsBool(prop); - } - - cmValue value = tgt->GetProperty(prop); - return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop)); -} - -template <> -const char* getTypedProperty<const char*>( - cmGeneratorTarget const* tgt, const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter) -{ - cmValue value = tgt->GetProperty(prop); - - if (genexInterpreter == nullptr) { - return value.GetCStr(); - } - - return genexInterpreter->Evaluate(value ? *value : "", prop).c_str(); -} - -template <> -std::string getTypedProperty<std::string>( - cmGeneratorTarget const* tgt, const std::string& prop, - cmGeneratorExpressionInterpreter* genexInterpreter) -{ - cmValue value = tgt->GetProperty(prop); - - if (genexInterpreter == nullptr) { - return valueAsString(value); - } - - return genexInterpreter->Evaluate(value ? *value : "", prop); -} - -template <typename PropertyType> -PropertyType impliedValue(PropertyType); -template <> -bool impliedValue<bool>(bool /*unused*/) -{ - return false; -} -template <> -const char* impliedValue<const char*>(const char* /*unused*/) -{ - return ""; -} -template <> -std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*) -{ - return std::string(); -} - -template <typename PropertyType> -std::pair<bool, PropertyType> consistentProperty(PropertyType lhs, - PropertyType rhs, - CompatibleType t); - -template <> -std::pair<bool, bool> consistentProperty(bool lhs, bool rhs, - CompatibleType /*unused*/) -{ - return { lhs == rhs, lhs }; -} - -static std::pair<bool, const char*> consistentStringProperty(const char* lhs, - const char* rhs) -{ - const bool b = strcmp(lhs, rhs) == 0; - return { b, b ? lhs : nullptr }; -} - -static std::pair<bool, std::string> consistentStringProperty( - const std::string& lhs, const std::string& rhs) -{ - const bool b = lhs == rhs; - return { b, b ? lhs : valueAsString(nullptr) }; -} - -static std::pair<bool, const char*> consistentNumberProperty(const char* lhs, - const char* rhs, - CompatibleType t) -{ - char* pEnd; - - long lnum = strtol(lhs, &pEnd, 0); - if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) { - return { false, nullptr }; - } - - long rnum = strtol(rhs, &pEnd, 0); - if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) { - return { false, nullptr }; - } - - if (t == NumberMaxType) { - return { true, std::max(lnum, rnum) == lnum ? lhs : rhs }; - } - - return { true, std::min(lnum, rnum) == lnum ? lhs : rhs }; -} - -template <> -std::pair<bool, const char*> consistentProperty(const char* lhs, - const char* rhs, - CompatibleType t) -{ - if (!lhs && !rhs) { - return { true, lhs }; - } - if (!lhs) { - return { true, rhs }; - } - if (!rhs) { - return { true, lhs }; - } - - switch (t) { - case BoolType: { - bool same = cmIsOn(lhs) == cmIsOn(rhs); - return { same, same ? lhs : nullptr }; - } - case StringType: - return consistentStringProperty(lhs, rhs); - case NumberMinType: - case NumberMaxType: - return consistentNumberProperty(lhs, rhs, t); - } - assert(false && "Unreachable!"); - return { false, nullptr }; -} - -static std::pair<bool, std::string> consistentProperty(const std::string& lhs, - const std::string& rhs, - CompatibleType t) -{ - const std::string null_ptr = valueAsString(nullptr); - - if (lhs == null_ptr && rhs == null_ptr) { - return { true, lhs }; - } - if (lhs == null_ptr) { - return { true, rhs }; - } - if (rhs == null_ptr) { - return { true, lhs }; - } - - switch (t) { - case BoolType: { - bool same = cmIsOn(lhs) == cmIsOn(rhs); - return { same, same ? lhs : null_ptr }; - } - case StringType: - return consistentStringProperty(lhs, rhs); - case NumberMinType: - case NumberMaxType: { - auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t); - return { value.first, - value.first ? std::string(value.second) : null_ptr }; - } - } - assert(false && "Unreachable!"); - return { false, null_ptr }; -} - -template <typename PropertyType> -PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, - const std::string& p, - const std::string& config, - const char* defaultValue, - CompatibleType t, - PropertyType* /*unused*/) -{ - PropertyType propContent = getTypedProperty<PropertyType>(tgt, p); - - std::vector<std::string> headPropKeys = tgt->GetPropertyKeys(); - const bool explicitlySet = cm::contains(headPropKeys, p); - - const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p); - assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet)); - - std::vector<cmGeneratorTarget const*> const& deps = - tgt->GetLinkImplementationClosure(config); - - if (deps.empty()) { - return propContent; - } - bool propInitialized = explicitlySet; - - std::string report = cmStrCat(" * Target \"", tgt->GetName()); - if (explicitlySet) { - report += "\" has property content \""; - report += valueAsString<PropertyType>(propContent); - report += "\"\n"; - } else if (impliedByUse) { - report += "\" property is implied by use.\n"; - } else { - report += "\" property not set.\n"; - } - - std::string interfaceProperty = "INTERFACE_" + p; - std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter; - if (p == "POSITION_INDEPENDENT_CODE") { - genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>( - tgt->GetLocalGenerator(), config, tgt); - } - - for (cmGeneratorTarget const* theTarget : deps) { - // An error should be reported if one dependency - // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other - // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the - // target itself has a POSITION_INDEPENDENT_CODE which disagrees - // with a dependency. - - std::vector<std::string> propKeys = theTarget->GetPropertyKeys(); - - const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty); - PropertyType ifacePropContent = getTypedProperty<PropertyType>( - theTarget, interfaceProperty, genexInterpreter.get()); - - std::string reportEntry; - if (ifaceIsSet) { - reportEntry += " * Target \""; - reportEntry += theTarget->GetName(); - reportEntry += "\" property value \""; - reportEntry += valueAsString<PropertyType>(ifacePropContent); - reportEntry += "\" "; - } - - if (explicitlySet) { - if (ifaceIsSet) { - std::pair<bool, PropertyType> consistent = - consistentProperty(propContent, ifacePropContent, t); - report += reportEntry; - report += compatibilityAgree(t, propContent != consistent.second); - if (!consistent.first) { - std::ostringstream e; - e << "Property " << p << " on target \"" << tgt->GetName() - << "\" does\nnot match the " - "INTERFACE_" - << p - << " property requirement\nof " - "dependency \"" - << theTarget->GetName() << "\".\n"; - cmSystemTools::Error(e.str()); - break; - } - propContent = consistent.second; - continue; - } - // Explicitly set on target and not set in iface. Can't disagree. - continue; - } - if (impliedByUse) { - propContent = impliedValue<PropertyType>(propContent); - - if (ifaceIsSet) { - std::pair<bool, PropertyType> consistent = - consistentProperty(propContent, ifacePropContent, t); - report += reportEntry; - report += compatibilityAgree(t, propContent != consistent.second); - if (!consistent.first) { - std::ostringstream e; - e << "Property " << p << " on target \"" << tgt->GetName() - << "\" is\nimplied to be " << defaultValue - << " because it was used to determine the link libraries\n" - "already. The INTERFACE_" - << p << " property on\ndependency \"" << theTarget->GetName() - << "\" is in conflict.\n"; - cmSystemTools::Error(e.str()); - break; - } - propContent = consistent.second; - continue; - } - // Implicitly set on target and not set in iface. Can't disagree. - continue; - } - if (ifaceIsSet) { - if (propInitialized) { - std::pair<bool, PropertyType> consistent = - consistentProperty(propContent, ifacePropContent, t); - report += reportEntry; - report += compatibilityAgree(t, propContent != consistent.second); - if (!consistent.first) { - std::ostringstream e; - e << "The INTERFACE_" << p << " property of \"" - << theTarget->GetName() << "\" does\nnot agree with the value of " - << p << " already determined\nfor \"" << tgt->GetName() << "\".\n"; - cmSystemTools::Error(e.str()); - break; - } - propContent = consistent.second; - continue; - } - report += reportEntry + "(Interface set)\n"; - propContent = ifacePropContent; - propInitialized = true; - } else { - // Not set. Nothing to agree on. - continue; - } - } - - tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent), - report, compatibilityType(t)); - return propContent; -} - bool cmGeneratorTarget::SetDeviceLink(bool deviceLink) { bool previous = this->DeviceLink; @@ -6498,200 +3488,6 @@ return previous; } -bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE", - BoolType, nullptr); -} - -std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<std::string>( - this, p, config, "FALSE", BoolType, nullptr); -} - -const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<const char*>( - this, p, config, "empty", StringType, nullptr); -} - -const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<const char*>( - this, p, config, "empty", NumberMinType, nullptr); -} - -const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty( - const std::string& p, const std::string& config) const -{ - return checkInterfacePropertyCompatibility<const char*>( - this, p, config, "empty", NumberMaxType, nullptr); -} - -cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( - const std::string& config) const -{ - // Lookup any existing information for this configuration. - std::string key(cmSystemTools::UpperCase(config)); - auto i = this->LinkInformation.find(key); - if (i == this->LinkInformation.end()) { - // Compute information for this configuration. - auto info = cm::make_unique<cmComputeLinkInformation>(this, config); - if (info && !info->Compute()) { - info.reset(); - } - - // Store the information for this configuration. - i = this->LinkInformation.emplace(key, std::move(info)).first; - - if (i->second) { - this->CheckPropertyCompatibility(*i->second, config); - } - } - return i->second.get(); -} - -void cmGeneratorTarget::CheckLinkLibraries() const -{ - bool linkLibrariesOnlyTargets = - this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS"); - - // Evaluate the link interface of this target if needed for extra checks. - if (linkLibrariesOnlyTargets) { - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - for (std::string const& config : configs) { - this->GetLinkInterfaceLibraries(config, this, LinkInterfaceFor::Link); - } - } - - // Check link the implementation for each generated configuration. - for (auto const& hmp : this->LinkImplMap) { - HeadToLinkImplementationMap const& hm = hmp.second; - // There could be several entries used when computing the pre-CMP0022 - // default link interface. Check only the entry for our own link impl. - auto const hmi = hm.find(this); - if (hmi == hm.end() || !hmi->second.LibrariesDone) { - continue; - } - for (cmLinkImplItem const& item : hmi->second.Libraries) { - if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) { - return; - } - if (linkLibrariesOnlyTargets && - !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) { - return; - } - } - } - - // Check link the interface for each generated combination of - // configuration and consuming head target. We should not need to - // consider LinkInterfaceUsageRequirementsOnlyMap because its entries - // should be a subset of LinkInterfaceMap (with LINK_ONLY left out). - for (auto const& hmp : this->LinkInterfaceMap) { - for (auto const& hmi : hmp.second) { - if (!hmi.second.LibrariesDone) { - continue; - } - for (cmLinkItem const& item : hmi.second.Libraries) { - if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) { - return; - } - if (linkLibrariesOnlyTargets && - !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) { - return; - } - } - } - } -} - -namespace { -cm::string_view missingTargetPossibleReasons = - "Possible reasons include:\n" - " * There is a typo in the target name.\n" - " * A find_package call is missing for an IMPORTED target.\n" - " * An ALIAS target is missing.\n"_s; -} - -bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, - cmLinkItem const& item) const -{ - if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || - item.AsStr().find("::") == std::string::npos) { - return true; - } - MessageType messageType = MessageType::FATAL_ERROR; - std::string e; - switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) { - case cmPolicies::WARN: { - e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n"); - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - return true; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - - if (role == LinkItemRole::Implementation) { - e = cmStrCat(e, "Target \"", this->GetName(), "\" links to"); - } else { - e = cmStrCat(e, "The link interface of target \"", this->GetName(), - "\" contains"); - } - e = - cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ", - missingTargetPossibleReasons); - cmListFileBacktrace backtrace = item.Backtrace; - if (backtrace.Empty()) { - backtrace = this->GetBacktrace(); - } - this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e, - backtrace); - return false; -} - -bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, - cmLinkItem const& item) const -{ - if (item.Target) { - return true; - } - std::string const& str = item.AsStr(); - if (!str.empty() && - (str[0] == '-' || str[0] == '$' || str[0] == '`' || - str.find_first_of("/\\") != std::string::npos || - cmHasPrefix(str, "<LINK_LIBRARY:"_s) || - cmHasPrefix(str, "<LINK_GROUP:"_s))) { - return true; - } - - std::string e = cmStrCat("Target \"", this->GetName(), - "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ", - role == LinkItemRole::Implementation - ? "it links to" - : "its link interface contains", - ":\n ", item.AsStr(), "\nwhich is not a target. ", - missingTargetPossibleReasons); - cmListFileBacktrace backtrace = item.Backtrace; - if (backtrace.Empty()) { - backtrace = this->GetBacktrace(); - } - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e, backtrace); - return false; -} - void cmGeneratorTarget::GetTargetVersion(int& major, int& minor) const { int patch; @@ -6778,7 +3574,7 @@ { if (cmValue prop = this->GetProperty("Fortran_BUILDING_INSTRINSIC_MODULES")) { - return cmIsOn(*prop); + return prop.IsOn(); } return false; } @@ -6930,291 +3726,6 @@ areport); } -bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, - cmLocalGenerator const*& lg) const -{ - if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) { - cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP)); - if (dirId.String.empty()) { - lg = this->LocalGenerator; - return true; - } - if (cmLocalGenerator const* otherLG = - this->GlobalGenerator->FindLocalGenerator(dirId)) { - lg = otherLG; - return true; - } - } - return false; -} - -cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( - std::string const& n, cmListFileBacktrace const& bt, - std::string const& linkFeature, LookupLinkItemScope* scope, - LookupSelf lookupSelf) const -{ - cm::optional<cmLinkItem> maybeItem; - if (this->IsLinkLookupScope(n, scope->LG)) { - return maybeItem; - } - - std::string name = this->CheckCMP0004(n); - if (name.empty() || - (lookupSelf == LookupSelf::No && name == this->GetName())) { - return maybeItem; - } - maybeItem = - this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); - return maybeItem; -} - -void cmGeneratorTarget::ExpandLinkItems( - std::string const& prop, cmBTStringRange entries, std::string const& config, - cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor, - LinkInterfaceField field, cmLinkInterface& iface) const -{ - if (entries.empty()) { - return; - } - // Keep this logic in sync with ComputeLinkImplementationLibraries. - cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr); - // The $<LINK_ONLY> expression may be in a link interface to specify - // private link dependencies that are otherwise excluded from usage - // requirements. - if (interfaceFor == LinkInterfaceFor::Usage) { - dagChecker.SetTransitivePropertiesOnly(); - dagChecker.SetTransitivePropertiesOnlyCMP0131(); - } - cmMakefile const* mf = this->LocalGenerator->GetMakefile(); - LookupLinkItemScope scope{ this->LocalGenerator }; - for (BT<std::string> const& entry : entries) { - cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), - entry.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value); - cge->SetEvaluateForBuildsystem(true); - cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, - &dagChecker, this, - headTarget->LinkerLanguage) }; - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& lib : libs) { - if (auto maybeLinkFeature = ParseLinkFeature(lib)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - lib, cge->GetBacktrace(), linkFeature, &scope, - field == LinkInterfaceField::Libraries ? LookupSelf::No - : LookupSelf::Yes)) { - cmLinkItem item = std::move(*maybeItem); - - if (field == LinkInterfaceField::HeadInclude) { - iface.HeadInclude.emplace_back(std::move(item)); - continue; - } - if (field == LinkInterfaceField::HeadExclude) { - iface.HeadExclude.emplace_back(std::move(item)); - continue; - } - if (!item.Target) { - // Report explicitly linked object files separately. - std::string const& maybeObj = item.AsStr(); - if (cmSystemTools::FileIsFullPath(maybeObj)) { - cmSourceFile const* sf = - mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); - if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - item.ObjectSource = sf; - iface.Objects.emplace_back(std::move(item)); - continue; - } - } - } - - iface.Libraries.emplace_back(std::move(item)); - } - } - if (cge->GetHadHeadSensitiveCondition()) { - iface.HadHeadSensitiveCondition = true; - } - if (cge->GetHadContextSensitiveCondition()) { - iface.HadContextSensitiveCondition = true; - } - if (cge->GetHadLinkLanguageSensitiveCondition()) { - iface.HadLinkLanguageSensitiveCondition = true; - } - } -} - -cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( - const std::string& config, cmGeneratorTarget const* head) const -{ - return this->GetLinkInterface(config, head, false); -} - -cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( - const std::string& config, cmGeneratorTarget const* head, - bool secondPass) const -{ - // Imported targets have their own link interface. - if (this->IsImported()) { - return this->GetImportLinkInterface(config, head, LinkInterfaceFor::Link, - secondPass); - } - - // Link interfaces are not supported for executables that do not - // export symbols. - if (this->GetType() == cmStateEnums::EXECUTABLE && - !this->IsExecutableWithExports()) { - return nullptr; - } - - // Lookup any existing link interface for this configuration. - cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[head]; - if (secondPass) { - iface = cmOptionalLinkInterface(); - } - if (!iface.LibrariesDone) { - iface.LibrariesDone = true; - this->ComputeLinkInterfaceLibraries(config, iface, head, - LinkInterfaceFor::Link); - } - if (!iface.AllDone) { - iface.AllDone = true; - if (iface.Exists) { - this->ComputeLinkInterface(config, iface, head, secondPass); - this->ComputeLinkInterfaceRuntimeLibraries(config, iface); - } - } - - return iface.Exists ? &iface : nullptr; -} - -void cmGeneratorTarget::ComputeLinkInterface( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget) const -{ - this->ComputeLinkInterface(config, iface, headTarget, false); -} - -void cmGeneratorTarget::ComputeLinkInterface( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget, bool secondPass) const -{ - if (iface.Explicit) { - if (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->GetType() == cmStateEnums::STATIC_LIBRARY || - this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - // Shared libraries may have runtime implementation dependencies - // on other shared libraries that are not in the interface. - std::set<cmLinkItem> emitted; - for (cmLinkItem const& lib : iface.Libraries) { - emitted.insert(lib); - } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - cmLinkImplementation const* impl = this->GetLinkImplementation( - config, LinkInterfaceFor::Link, secondPass); - for (cmLinkImplItem const& lib : impl->Libraries) { - if (emitted.insert(lib).second) { - if (lib.Target) { - // This is a runtime dependency on another shared library. - if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { - iface.SharedDeps.push_back(lib); - } - } else { - // TODO: Recognize shared library file names. Perhaps this - // should be moved to cmComputeLinkInformation, but that - // creates a chicken-and-egg problem since this list is needed - // for its construction. - } - } - } - } - } - } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || - this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { - // The link implementation is the default link interface. - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget, - LinkInterfaceFor::Link); - iface.ImplementationIsInterface = true; - iface.WrongConfigLibraries = impl->WrongConfigLibraries; - } - - if (this->LinkLanguagePropagatesToDependents()) { - // Targets using this archive need its language runtime libraries. - if (cmLinkImplementation const* impl = this->GetLinkImplementation( - config, LinkInterfaceFor::Link, secondPass)) { - iface.Languages = impl->Languages; - } - } - - if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { - // Construct the property name suffix for this configuration. - std::string suffix = "_"; - if (!config.empty()) { - suffix += cmSystemTools::UpperCase(config); - } else { - suffix += "NOCONFIG"; - } - - // How many repetitions are needed if this library has cyclic - // dependencies? - std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix); - if (cmValue config_reps = this->GetProperty(propName)) { - sscanf(config_reps->c_str(), "%u", &iface.Multiplicity); - } else if (cmValue reps = - this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { - sscanf(reps->c_str(), "%u", &iface.Multiplicity); - } - } -} - -const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( - const std::string& config, cmGeneratorTarget const* head, - LinkInterfaceFor interfaceFor) const -{ - // Imported targets have their own link interface. - if (this->IsImported()) { - return this->GetImportLinkInterface(config, head, interfaceFor); - } - - // Link interfaces are not supported for executables that do not - // export symbols. - if (this->GetType() == cmStateEnums::EXECUTABLE && - !this->IsExecutableWithExports()) { - return nullptr; - } - - // Lookup any existing link interface for this configuration. - cmHeadToLinkInterfaceMap& hm = - (interfaceFor == LinkInterfaceFor::Usage - ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) - : this->GetHeadToLinkInterfaceMap(config)); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[head]; - if (!iface.LibrariesDone) { - iface.LibrariesDone = true; - this->ComputeLinkInterfaceLibraries(config, iface, head, interfaceFor); - } - - return iface.Exists ? &iface : nullptr; -} - std::string cmGeneratorTarget::GetDirectory( const std::string& config, cmStateEnums::ArtifactType artifact) const { @@ -7470,304 +3981,6 @@ return true; } -void cmGeneratorTarget::ComputeLinkInterfaceLibraries( - const std::string& config, cmOptionalLinkInterface& iface, - cmGeneratorTarget const* headTarget, LinkInterfaceFor interfaceFor) const -{ - // Construct the property name suffix for this configuration. - std::string suffix = "_"; - if (!config.empty()) { - suffix += cmSystemTools::UpperCase(config); - } else { - suffix += "NOCONFIG"; - } - - // An explicit list of interface libraries may be set for shared - // libraries and executables that export symbols. - bool haveExplicitLibraries = false; - cmValue explicitLibrariesCMP0022OLD; - std::string linkIfacePropCMP0022OLD; - bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD && - this->GetPolicyStatusCMP0022() != cmPolicies::WARN); - if (cmp0022NEW) { - // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES. - haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() || - !this->Target->GetLinkInterfaceDirectEntries().empty() || - !this->Target->GetLinkInterfaceDirectExcludeEntries().empty(); - } else { - // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a - // shared lib or executable. - if (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->IsExecutableWithExports()) { - // Lookup the per-configuration property. - linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix); - explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD); - - // If not set, try the generic property. - if (!explicitLibrariesCMP0022OLD) { - linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES"; - explicitLibrariesCMP0022OLD = - this->GetProperty(linkIfacePropCMP0022OLD); - } - } - - if (explicitLibrariesCMP0022OLD && - this->GetPolicyStatusCMP0022() == cmPolicies::WARN && - !this->PolicyWarnedCMP0022) { - // Compare the explicitly set old link interface properties to the - // preferred new link interface property one and warn if different. - cmValue newExplicitLibraries = - this->GetProperty("INTERFACE_LINK_LIBRARIES"); - if (newExplicitLibraries && - (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) { - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" - "Target \"" << this->GetName() << "\" has an " - "INTERFACE_LINK_LIBRARIES property which differs from its " << - linkIfacePropCMP0022OLD << " properties." - "\n" - "INTERFACE_LINK_LIBRARIES:\n" - " " << *newExplicitLibraries << "\n" << - linkIfacePropCMP0022OLD << ":\n" - " " << *explicitLibrariesCMP0022OLD << "\n"; - /* clang-format on */ - this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, - w.str()); - this->PolicyWarnedCMP0022 = true; - } - } - - haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD); - } - - // There is no implicit link interface for executables or modules - // so if none was explicitly set then there is no link interface. - if (!haveExplicitLibraries && - (this->GetType() == cmStateEnums::EXECUTABLE || - (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { - return; - } - iface.Exists = true; - - // If CMP0022 is NEW then the plain tll signature sets the - // INTERFACE_LINK_LIBRARIES property. Even if the project - // clears it, the link interface is still explicit. - iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD; - - if (cmp0022NEW) { - // The interface libraries are specified by INTERFACE_LINK_LIBRARIES. - // Use its special representation directly to get backtraces. - this->ExpandLinkItems( - kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), - config, headTarget, interfaceFor, LinkInterfaceField::Libraries, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, - this->Target->GetLinkInterfaceDirectEntries(), - config, headTarget, interfaceFor, - LinkInterfaceField::HeadInclude, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, - this->Target->GetLinkInterfaceDirectExcludeEntries(), - config, headTarget, interfaceFor, - LinkInterfaceField::HeadExclude, iface); - } else if (explicitLibrariesCMP0022OLD) { - // The interface libraries have been explicitly set in pre-CMP0022 style. - std::vector<BT<std::string>> entries; - entries.emplace_back(*explicitLibrariesCMP0022OLD); - this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries), - config, headTarget, interfaceFor, - LinkInterfaceField::Libraries, iface); - } - - // If the link interface is explicit, do not fall back to the link impl. - if (iface.Explicit) { - return; - } - - // The link implementation is the default link interface. - if (cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget, - interfaceFor)) { - iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), - impl->Libraries.end()); - if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && - !this->PolicyWarnedCMP0022 && interfaceFor == LinkInterfaceFor::Link) { - // Compare the link implementation fallback link interface to the - // preferred new link interface property and warn if different. - cmLinkInterface ifaceNew; - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES, - this->Target->GetLinkInterfaceEntries(), config, - headTarget, interfaceFor, - LinkInterfaceField::Libraries, ifaceNew); - if (ifaceNew.Libraries != iface.Libraries) { - std::string oldLibraries = cmJoin(impl->Libraries, ";"); - std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); - if (oldLibraries.empty()) { - oldLibraries = "(empty)"; - } - if (newLibraries.empty()) { - newLibraries = "(empty)"; - } - - std::ostringstream w; - /* clang-format off */ - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" - "Target \"" << this->GetName() << "\" has an " - "INTERFACE_LINK_LIBRARIES property. " - "This should be preferred as the source of the link interface " - "for this library but because CMP0022 is not set CMake is " - "ignoring the property and using the link implementation " - "as the link interface instead." - "\n" - "INTERFACE_LINK_LIBRARIES:\n" - " " << newLibraries << "\n" - "Link implementation:\n" - " " << oldLibraries << "\n"; - /* clang-format on */ - this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, - w.str()); - this->PolicyWarnedCMP0022 = true; - } - } - } -} - -namespace { - -template <typename ReturnType> -ReturnType constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt); - -template <> -inline cmLinkImplItem constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt) -{ - return cmLinkImplItem(cmLinkItem(target, false, bt), false); -} - -template <> -inline cmLinkItem constructItem(cmGeneratorTarget* target, - cmListFileBacktrace const& bt) -{ - return cmLinkItem(target, false, bt); -} - -template <typename ValueType> -std::vector<ValueType> computeImplicitLanguageTargets( - std::string const& lang, std::string const& config, - cmGeneratorTarget const* currentTarget) -{ - cmListFileBacktrace bt; - std::vector<ValueType> result; - cmLocalGenerator* lg = currentTarget->GetLocalGenerator(); - - std::string const& runtimeLibrary = - currentTarget->GetRuntimeLinkLibrary(lang, config); - if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition( - "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) { - cmList libsList{ *runtimeLinkOptions }; - result.reserve(libsList.size()); - - for (auto const& i : libsList) { - cmGeneratorTarget::TargetOrString resolved = - currentTarget->ResolveTargetReference(i, lg); - if (resolved.Target) { - result.emplace_back(constructItem<ValueType>(resolved.Target, bt)); - } - } - } - - return result; -} -} - -void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries( - const std::string& config, cmOptionalLinkInterface& iface) const -{ - for (std::string const& lang : iface.Languages) { - if ((lang == "CUDA" || lang == "HIP") && - iface.LanguageRuntimeLibraries.find(lang) == - iface.LanguageRuntimeLibraries.end()) { - auto implicitTargets = - computeImplicitLanguageTargets<cmLinkItem>(lang, config, this); - iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); - } - } -} - -void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries( - const std::string& config, cmOptionalLinkImplementation& impl) const -{ - for (std::string const& lang : impl.Languages) { - if ((lang == "CUDA" || lang == "HIP") && - impl.LanguageRuntimeLibraries.find(lang) == - impl.LanguageRuntimeLibraries.end()) { - auto implicitTargets = - computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this); - impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); - } - } -} - -const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( - const std::string& config, cmGeneratorTarget const* headTarget, - LinkInterfaceFor interfaceFor, bool secondPass) const -{ - cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); - if (!info) { - return nullptr; - } - - cmHeadToLinkInterfaceMap& hm = - (interfaceFor == LinkInterfaceFor::Usage - ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) - : this->GetHeadToLinkInterfaceMap(config)); - - // If the link interface does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - headTarget = hm.begin()->first; - } - - cmOptionalLinkInterface& iface = hm[headTarget]; - if (secondPass) { - iface = cmOptionalLinkInterface(); - } - if (!iface.AllDone) { - iface.AllDone = true; - iface.LibrariesDone = true; - iface.Multiplicity = info->Multiplicity; - cmExpandList(info->Languages, iface.Languages); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, - cmMakeRange(info->LibrariesHeadInclude), config, - headTarget, interfaceFor, - LinkInterfaceField::HeadInclude, iface); - this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, - cmMakeRange(info->LibrariesHeadExclude), config, - headTarget, interfaceFor, - LinkInterfaceField::HeadExclude, iface); - this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), - config, headTarget, interfaceFor, - LinkInterfaceField::Libraries, iface); - cmList deps{ info->SharedDeps }; - LookupLinkItemScope scope{ this->LocalGenerator }; - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& dep : deps) { - if (auto maybeLinkFeature = ParseLinkFeature(dep)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( - dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { - iface.SharedDeps.emplace_back(std::move(*maybeItem)); - } - } - } - - return &iface; -} - cmGeneratorTarget::ImportInfo const* cmGeneratorTarget::GetImportInfo( const std::string& config) const { @@ -7891,9 +4104,9 @@ if (this->GetType() == cmStateEnums::SHARED_LIBRARY) { std::string soProp = cmStrCat("IMPORTED_NO_SONAME", suffix); if (cmValue config_no_soname = this->GetProperty(soProp)) { - info.NoSOName = cmIsOn(*config_no_soname); + info.NoSOName = config_no_soname.IsOn(); } else if (cmValue no_soname = this->GetProperty("IMPORTED_NO_SONAME")) { - info.NoSOName = cmIsOn(*no_soname); + info.NoSOName = no_soname.IsOn(); } } @@ -7957,69 +4170,6 @@ } } -cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( - const std::string& config) const -{ - return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)]; -} - -cmHeadToLinkInterfaceMap& -cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( - const std::string& config) const -{ - return this - ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; -} - -const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( - const std::string& config, LinkInterfaceFor implFor) const -{ - return this->GetLinkImplementation(config, implFor, false); -} - -const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( - const std::string& config, LinkInterfaceFor implFor, bool secondPass) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - return nullptr; - } - - HeadToLinkImplementationMap& hm = - (implFor == LinkInterfaceFor::Usage - ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) - : this->GetHeadToLinkImplementationMap(config)); - cmOptionalLinkImplementation& impl = hm[this]; - if (secondPass) { - impl = cmOptionalLinkImplementation(); - } - if (!impl.LibrariesDone) { - impl.LibrariesDone = true; - this->ComputeLinkImplementationLibraries(config, impl, this, implFor); - } - if (!impl.LanguagesDone) { - impl.LanguagesDone = true; - this->ComputeLinkImplementationLanguages(config, impl); - this->ComputeLinkImplementationRuntimeLibraries(config, impl); - } - return &impl; -} - -cmGeneratorTarget::HeadToLinkImplementationMap& -cmGeneratorTarget::GetHeadToLinkImplementationMap( - std::string const& config) const -{ - return this->LinkImplMap[cmSystemTools::UpperCase(config)]; -} - -cmGeneratorTarget::HeadToLinkImplementationMap& -cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap( - std::string const& config) const -{ - return this - ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; -} - bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode( std::vector<cmSourceFile*>& files) const { @@ -8263,170 +4413,140 @@ return true; } if (cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Link)) { + this->GetLinkImplementationLibraries(config, UseTo::Link)) { return !impl->Libraries.empty(); } return false; } -cmLinkImplementationLibraries const* -cmGeneratorTarget::GetLinkImplementationLibraries( - const std::string& config, LinkInterfaceFor implFor) const -{ - return this->GetLinkImplementationLibrariesInternal(config, this, implFor); -} - -cmLinkImplementationLibraries const* -cmGeneratorTarget::GetLinkImplementationLibrariesInternal( - const std::string& config, cmGeneratorTarget const* head, - LinkInterfaceFor implFor) const -{ - // There is no link implementation for targets that cannot compile sources. - if (!this->CanCompileSources()) { - return nullptr; - } - - // Populate the link implementation libraries for this configuration. - HeadToLinkImplementationMap& hm = - (implFor == LinkInterfaceFor::Usage - ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) - : this->GetHeadToLinkImplementationMap(config)); - - // If the link implementation does not depend on the head target - // then reuse the one from the head we computed first. - if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { - head = hm.begin()->first; - } - - cmOptionalLinkImplementation& impl = hm[head]; - if (!impl.LibrariesDone) { - impl.LibrariesDone = true; - this->ComputeLinkImplementationLibraries(config, impl, head, implFor); - } - return &impl; -} - bool cmGeneratorTarget::IsNullImpliedByLinkLibraries( const std::string& p) const { return cm::contains(this->LinkImplicitNullProperties, p); } -namespace { -class TransitiveLinkImpl +bool cmGeneratorTarget::ApplyCXXStdTargets() { - cmGeneratorTarget const* Self; - std::string const& Config; - LinkInterfaceFor ImplFor; - cmLinkImplementation& Impl; - - std::set<cmLinkItem> Emitted; - std::set<cmLinkItem> Excluded; - std::unordered_set<cmGeneratorTarget const*> Followed; - - void Follow(cmGeneratorTarget const* target); - -public: - TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config, - LinkInterfaceFor implFor, cmLinkImplementation& impl) - : Self(self) - , Config(config) - , ImplFor(implFor) - , Impl(impl) - { + 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"); + if (!std_prop) { + // TODO(cxxmodules): Add a target policy to flip the default here. Set + // `std_prop` based on it. + return true; } - void Compute(); -}; - -void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target) -{ - if (!target || !this->Followed.insert(target).second || - target->GetPolicyStatusCMP0022() == cmPolicies::OLD || - target->GetPolicyStatusCMP0022() == cmPolicies::WARN) { - return; - } - - // Get this target's usage requirements. - cmLinkInterfaceLibraries const* iface = - target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor); - if (!iface) { - return; - } - if (iface->HadContextSensitiveCondition) { - this->Impl.HadContextSensitiveCondition = true; - } - - // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements. - for (cmLinkItem const& item : iface->HeadInclude) { - // Inject direct dependencies from the item's usage requirements - // before the item itself. - this->Follow(item.Target); - - // Add the item itself, but at most once. - if (this->Emitted.insert(item).second) { - this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false); + std::string std_prop_value; + if (std_prop) { + // Evaluate generator expressions. + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance()); + auto cge = ge.Parse(*std_prop); + if (!cge) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), "\" is not a valid generator expression.")); + return false; + } + // But do not allow context-sensitive queries. Whether a target uses + // `import std` should not depend on configuration or properties of the + // consumer (head target). The link language also shouldn't matter, so ban + // it as well. + if (cge->GetHadHeadSensitiveCondition()) { + // Not reachable; all target-sensitive genexes actually fail to parse. + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), + "\" contains a condition that queries the " + "consuming target which is not supported.")); + return false; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + // Not reachable; all link language genexes actually fail to parse. + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), + "\" contains a condition that queries the " + "link language which is not supported.")); + return false; + } + std_prop_value = cge->Evaluate(this->LocalGenerator, ""); + if (cge->GetHadContextSensitiveCondition()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), + "\" contains a context-sensitive condition " + "that is not supported.")); + return false; } } + auto use_std = cmIsOn(std_prop_value); - // Follow transitive dependencies. - for (cmLinkItem const& item : iface->Libraries) { - this->Follow(item.Target); + // If we have a value and it is not true, there's nothing to do. + if (std_prop && !use_std) { + return true; } - // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' - // usage requirements. - for (cmLinkItem const& item : iface->HeadExclude) { - this->Excluded.insert(item); - } -} + for (auto const& config : configs) { + if (this->HaveCxxModuleSupport(config) != Cxx20SupportLevel::Supported) { + continue; + } -void TransitiveLinkImpl::Compute() -{ - // Save the original items and start with an empty list. - std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries); + cm::optional<cmStandardLevel> explicitLevel = + this->GetExplicitStandardLevel("CXX", config); + if (!explicitLevel || *explicitLevel < cxxStd23) { + continue; + } - // Avoid injecting any original items as usage requirements. - // This gives LINK_LIBRARIES final control over the order - // if it explicitly lists everything. - this->Emitted.insert(original.cbegin(), original.cend()); + auto const stdLevel = + standardResolver.GetLevelString("CXX", *explicitLevel); + auto const targetName = cmStrCat("__CMAKE::CXX", stdLevel); + if (!this->Makefile->FindTargetToUse(targetName)) { + auto basicReason = this->Makefile->GetDefinition(cmStrCat( + "CMAKE_CXX", stdLevel, "_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE")); + std::string reason; + if (!basicReason.IsEmpty()) { + reason = cmStrCat(" Reason:\n ", basicReason); + } + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), "\" requires that the \"", targetName, + "\" target exist, but it was not provided by the toolchain.", + reason)); + break; + } - // Process each original item. - for (cmLinkImplItem& item : original) { - // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT' - // usage requirements before the item itself. - this->Follow(item.Target); + // Check the experimental feature here as well. A toolchain may have + // provided the target and skipped the check in the toolchain preparation + // logic. + if (!cmExperimental::HasSupportEnabled( + *this->Makefile, cmExperimental::Feature::CxxImportStd)) { + break; + } - // Add the item itself. - this->Impl.Libraries.emplace_back(std::move(item)); + this->Target->AppendProperty( + "LINK_LIBRARIES", + cmStrCat("$<BUILD_LOCAL_INTERFACE:$<$<CONFIG:", config, ">:", targetName, + ">>")); } - // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' - // usage requirements found through any dependency above. - this->Impl.Libraries.erase( - std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(), - [this](cmLinkImplItem const& item) { - return this->Excluded.find(item) != this->Excluded.end(); - }), - this->Impl.Libraries.end()); -} - -void ComputeLinkImplTransitive(cmGeneratorTarget const* self, - std::string const& config, - LinkInterfaceFor implFor, - cmLinkImplementation& impl) -{ - TransitiveLinkImpl transitiveLinkImpl(self, config, implFor, impl); - transitiveLinkImpl.Compute(); -} + return true; } bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, std::string const& config) { + std::vector<std::string> allConfigs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); cmOptionalLinkImplementation impl; - this->ComputeLinkImplementationLibraries(config, impl, this, - LinkInterfaceFor::Link); + this->ComputeLinkImplementationLibraries(config, impl, this, UseTo::Link); cmCxxModuleUsageEffects usage(this); @@ -8502,9 +4622,22 @@ // 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. + gtp->ApplyCXXStdTargets(); + gtp->DiscoverSyntheticTargets(cache, config); + lg->AddGeneratorTarget(std::move(gtp)); } else { synthDep = cached->second; @@ -8517,237 +4650,6 @@ return true; } -void cmGeneratorTarget::ComputeLinkImplementationLibraries( - const std::string& config, cmOptionalLinkImplementation& impl, - cmGeneratorTarget const* head, LinkInterfaceFor implFor) const -{ - cmLocalGenerator const* lg = this->LocalGenerator; - cmMakefile const* mf = lg->GetMakefile(); - cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); - auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; - // Collect libraries directly linked in this configuration. - for (auto const& entry : entryRange) { - // Keep this logic in sync with ExpandLinkItems. - cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr, - nullptr); - // The $<LINK_ONLY> expression may be used to specify link dependencies - // that are otherwise excluded from usage requirements. - if (implFor == LinkInterfaceFor::Usage) { - dagChecker.SetTransitivePropertiesOnly(); - switch (this->GetPolicyStatusCMP0131()) { - case cmPolicies::WARN: - case cmPolicies::OLD: - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - dagChecker.SetTransitivePropertiesOnlyCMP0131(); - break; - } - } - cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), - entry.Backtrace); - std::unique_ptr<cmCompiledGeneratorExpression> const cge = - ge.Parse(entry.Value); - cge->SetEvaluateForBuildsystem(true); - std::string const& evaluated = - cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, - this->LinkerLanguage); - bool const checkCMP0027 = evaluated != entry.Value; - cmList llibs(evaluated); - if (cge->GetHadHeadSensitiveCondition()) { - impl.HadHeadSensitiveCondition = true; - } - if (cge->GetHadContextSensitiveCondition()) { - impl.HadContextSensitiveCondition = true; - } - if (cge->GetHadLinkLanguageSensitiveCondition()) { - impl.HadLinkLanguageSensitiveCondition = true; - } - - auto linkFeature = cmLinkItem::DEFAULT; - for (auto const& lib : llibs) { - if (auto maybeLinkFeature = ParseLinkFeature(lib)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (this->IsLinkLookupScope(lib, lg)) { - continue; - } - - // Skip entries that resolve to the target itself or are empty. - std::string name = this->CheckCMP0004(lib); - if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) { - // resolve alias name - auto* target = this->Makefile->FindTargetToUse(name); - if (target) { - name = target->GetName(); - } - } - if (name == this->GetName() || name.empty()) { - if (name == this->GetName()) { - bool noMessage = false; - MessageType messageType = MessageType::FATAL_ERROR; - std::ostringstream e; - switch (this->GetPolicyStatusCMP0038()) { - case cmPolicies::WARN: { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; - messageType = MessageType::AUTHOR_WARNING; - } break; - case cmPolicies::OLD: - noMessage = true; - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Issue the fatal message. - break; - } - - if (!noMessage) { - e << "Target \"" << this->GetName() << "\" links to itself."; - this->LocalGenerator->GetCMakeInstance()->IssueMessage( - messageType, e.str(), this->GetBacktrace()); - if (messageType == MessageType::FATAL_ERROR) { - return; - } - } - } - continue; - } - - // The entry is meant for this configuration. - cmLinkItem item = this->ResolveLinkItem( - BT<std::string>(name, entry.Backtrace), lg, linkFeature); - if (item.Target) { - auto depsForTarget = synthTargetsForConfig.find(item.Target); - if (depsForTarget != synthTargetsForConfig.end()) { - for (auto const* depForTarget : depsForTarget->second) { - cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); - impl.Libraries.emplace_back(std::move(synthItem), false); - } - } - } else { - // Report explicitly linked object files separately. - std::string const& maybeObj = item.AsStr(); - if (cmSystemTools::FileIsFullPath(maybeObj)) { - cmSourceFile const* sf = - mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); - if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { - item.ObjectSource = sf; - impl.Objects.emplace_back(std::move(item)); - continue; - } - } - } - - impl.Libraries.emplace_back(std::move(item), checkCMP0027); - } - - std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); - for (std::string const& sp : seenProps) { - if (!this->GetProperty(sp)) { - this->LinkImplicitNullProperties.insert(sp); - } - } - cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); - } - - // Update the list of direct link dependencies from usage requirements. - if (head == this) { - ComputeLinkImplTransitive(this, config, implFor, impl); - } - - // Get the list of configurations considered to be DEBUG. - std::vector<std::string> debugConfigs = - this->Makefile->GetCMakeInstance()->GetDebugConfigs(); - - cmTargetLinkLibraryType linkType = - CMP0003_ComputeLinkType(config, debugConfigs); - cmTarget::LinkLibraryVectorType const& oldllibs = - this->Target->GetOriginalLinkLibraries(); - - auto linkFeature = cmLinkItem::DEFAULT; - for (cmTarget::LibraryID const& oldllib : oldllibs) { - if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { - linkFeature = std::move(*maybeLinkFeature); - continue; - } - - if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { - std::string name = this->CheckCMP0004(oldllib.first); - if (name == this->GetName() || name.empty()) { - continue; - } - // Support OLD behavior for CMP0003. - impl.WrongConfigLibraries.push_back( - this->ResolveLinkItem(BT<std::string>(name), linkFeature)); - } - } -} - -cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( - std::string const& name) const -{ - return this->ResolveTargetReference(name, this->LocalGenerator); -} - -cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( - std::string const& name, cmLocalGenerator const* lg) const -{ - TargetOrString resolved; - - if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) { - resolved.Target = tgt; - } else { - resolved.String = name; - } - - return resolved; -} - -cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name, std::string const& linkFeature) const -{ - return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); -} - -cmLinkItem cmGeneratorTarget::ResolveLinkItem( - BT<std::string> const& name, cmLocalGenerator const* lg, - std::string const& linkFeature) const -{ - auto bt = name.Backtrace; - TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); - - if (!resolved.Target) { - return cmLinkItem(resolved.String, false, bt, linkFeature); - } - - // Check deprecation, issue message with `bt` backtrace. - if (resolved.Target->IsDeprecated()) { - std::ostringstream w; - /* clang-format off */ - w << - "The library that is being linked to, " << resolved.Target->GetName() << - ", 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); - } - - // Skip targets that will not really be linked. This is probably a - // name conflict between an external library and an executable - // within the project. - if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && - !resolved.Target->IsExecutableWithExports()) { - return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); - } - - return cmLinkItem(resolved.Target, false, bt, linkFeature); -} - bool cmGeneratorTarget::HasPackageReferences() const { return this->IsInBuildSystem() && @@ -9208,7 +5110,10 @@ // consider the headerFile as part of the entire language // unit within include-what-you-use and as a result allows // one to get IWYU advice for headers. - fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n"; + // 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; @@ -9596,3 +5501,45 @@ per_config.BuiltFileSetCache = true; } + +std::string cmGeneratorTarget::GetSwiftModuleName() const +{ + return this->GetPropertyOrDefault("Swift_MODULE_NAME", this->GetName()); +} + +std::string cmGeneratorTarget::GetSwiftModuleFileName() const +{ + return this->GetPropertyOrDefault( + "Swift_MODULE", this->GetSwiftModuleName() + ".swiftmodule"); +} + +std::string cmGeneratorTarget::GetSwiftModuleDirectory( + std::string const& config) const +{ + std::string moduleDirectory = + this->GetPropertyOrDefault("Swift_MODULE_DIRECTORY", ""); + + if (moduleDirectory.empty()) { + moduleDirectory = this->LocalGenerator->GetCurrentBinaryDirectory(); + this->LocalGenerator->GetGlobalGenerator()->AppendDirectoryForConfig( + "/", config, "", moduleDirectory); + } + + return moduleDirectory; +} + +std::string cmGeneratorTarget::GetSwiftModulePath( + std::string const& config) const +{ + return this->GetSwiftModuleDirectory(config) + "/" + + this->GetSwiftModuleFileName(); +} + +std::string cmGeneratorTarget::GetPropertyOrDefault( + std::string const& property, std::string defaultValue) const +{ + if (cmValue name = this->GetProperty(property)) { + return *name; + } + return defaultValue; +}
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 71785b6..1e6ff78 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h
@@ -15,9 +15,9 @@ #include <vector> #include <cm/optional> +#include <cm/string_view> #include "cmAlgorithms.h" -#include "cmComputeLinkInformation.h" #include "cmLinkItem.h" #include "cmListFileCache.h" #include "cmPolicies.h" @@ -25,7 +25,10 @@ #include "cmStateTypes.h" #include "cmValue.h" +class cmake; enum class cmBuildStep; +class cmCompiledGeneratorExpression; +class cmComputeLinkInformation; class cmCustomCommand; class cmFileSet; class cmGlobalGenerator; @@ -96,6 +99,13 @@ // Call this after generation is complete. void CheckLinkLibraries() const; + class CheckLinkLibrariesSuppressionRAII + { + public: + CheckLinkLibrariesSuppressionRAII(); + ~CheckLinkLibrariesSuppressionRAII(); + }; + cmStateEnums::TargetType GetType() const; const std::string& GetName() const; std::string GetExportName() const; @@ -257,20 +267,20 @@ cmOptionalLinkInterface& iface, const cmGeneratorTarget* head) const; - enum class LinkInterfaceFor + enum class UseTo { - Usage, // Interface for usage requirements excludes $<LINK_ONLY>. - Link, // Interface for linking includes $<LINK_ONLY>. + Compile, // Usage requirements for compiling. Excludes $<LINK_ONLY>. + Link, // Usage requirements for linking. Includes $<LINK_ONLY>. }; cmLinkInterfaceLibraries const* GetLinkInterfaceLibraries( const std::string& config, const cmGeneratorTarget* headTarget, - LinkInterfaceFor interfaceFor) const; + UseTo usage) const; void ComputeLinkInterfaceLibraries(const std::string& config, cmOptionalLinkInterface& iface, const cmGeneratorTarget* head, - LinkInterfaceFor interfaceFor) const; + UseTo usage) const; /** Get the library name for an imported interface library. */ std::string GetImportedLibName(std::string const& config) const; @@ -428,19 +438,19 @@ LinkClosure const* GetLinkClosure(const std::string& config) const; - cmLinkImplementation const* GetLinkImplementation( - const std::string& config, LinkInterfaceFor implFor) const; + cmLinkImplementation const* GetLinkImplementation(const std::string& config, + UseTo usage) const; void ComputeLinkImplementationLanguages( const std::string& config, cmOptionalLinkImplementation& impl) const; cmLinkImplementationLibraries const* GetLinkImplementationLibraries( - const std::string& config, LinkInterfaceFor implFor) const; + const std::string& config, UseTo usage) const; void ComputeLinkImplementationLibraries(const std::string& config, cmOptionalLinkImplementation& impl, const cmGeneratorTarget* head, - LinkInterfaceFor implFor) const; + UseTo usage) const; struct TargetOrString { @@ -534,7 +544,27 @@ std::string GetClangTidyExportFixesDirectory(const std::string& lang) const; + /** Return the swift module name for this target. */ + std::string GetSwiftModuleName() const; + + /** Return the path of the `.swiftmodule` for this target in + the given configuration. */ + std::string GetSwiftModulePath(std::string const& config) const; + + /** Return the directory containing Swift module interface + descriptions for this target (including its `.swiftmodule`, + `.abi.json`, and `.swiftdoc`) in the given configuration. */ + std::string GetSwiftModuleDirectory(std::string const& config) const; + private: + /** Return the given property of this target if it exists; otherwise + return defaultValue. */ + std::string GetPropertyOrDefault(std::string const& property, + std::string defaultValue) const; + + /** Return the name of the `.swiftmodule` file for this target. */ + std::string GetSwiftModuleFileName() const; + using ConfigAndLanguage = std::pair<std::string, std::string>; using ConfigAndLanguageToBTStrings = std::map<ConfigAndLanguage, std::vector<BT<std::string>>>; @@ -598,6 +628,8 @@ std::vector<BT<std::string>> GetPrecompileHeaders( const std::string& config, const std::string& language) const; + std::vector<std::string> GetPchArchs(std::string const& config, + std::string const& lang) const; std::string GetPchHeader(const std::string& config, const std::string& language, const std::string& arch = std::string()) const; @@ -883,8 +915,27 @@ std::string EvaluateInterfaceProperty( std::string const& prop, cmGeneratorExpressionContext* context, - cmGeneratorExpressionDAGChecker* dagCheckerParent, - LinkInterfaceFor interfaceFor = LinkInterfaceFor::Usage) const; + cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const; + + struct TransitiveProperty + { +#if defined(__SUNPRO_CC) || (defined(__ibmxl__) && defined(__clang__)) + TransitiveProperty(cm::string_view interfaceName, UseTo usage) + : InterfaceName(interfaceName) + , Usage(usage) + { + } +#endif + cm::string_view InterfaceName; + UseTo Usage; + }; + + static const std::map<cm::string_view, TransitiveProperty> + BuiltinTransitiveProperties; + + cm::optional<TransitiveProperty> IsTransitiveProperty( + cm::string_view prop, cmLocalGenerator const* lg, + std::string const& config, bool evaluatingLinkLibraries) const; bool HaveInstallTreeRPATH(const std::string& config) const; @@ -962,9 +1013,34 @@ std::string GetImportedXcFrameworkPath(const std::string& config) const; + bool ApplyCXXStdTargets(); bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, std::string const& config); + class CustomTransitiveProperty : public TransitiveProperty + { + std::unique_ptr<std::string> InterfaceNameBuf; + CustomTransitiveProperty(std::unique_ptr<std::string> interfaceNameBuf, + UseTo usage); + + public: + CustomTransitiveProperty(std::string interfaceName, UseTo usage); + }; + struct CustomTransitiveProperties + : public std::map<std::string, CustomTransitiveProperty> + { + void Add(cmValue props, UseTo usage); + }; + + enum class PropertyFor + { + Build, + Interface, + }; + + CustomTransitiveProperties const& GetCustomTransitiveProperties( + std::string const& config, PropertyFor propertyFor) const; + private: void AddSourceCommon(const std::string& src, bool before = false); @@ -1032,6 +1108,11 @@ std::string const& base, std::string const& suffix, std::string const& name, cmValue version) const; + mutable std::map<std::string, CustomTransitiveProperties> + CustomTransitiveBuildPropertiesMap; + mutable std::map<std::string, CustomTransitiveProperties> + CustomTransitiveInterfacePropertiesMap; + struct CompatibleInterfacesBase { std::set<std::string> PropsBool; @@ -1063,7 +1144,8 @@ { bool Done = false; }; - mutable std::map<std::string, LinkImplClosure> LinkImplClosureMap; + mutable std::map<std::string, LinkImplClosure> LinkImplClosureForLinkMap; + mutable std::map<std::string, LinkImplClosure> LinkImplClosureForUsageMap; using LinkInterfaceMapType = std::map<std::string, cmHeadToLinkInterfaceMap>; mutable LinkInterfaceMapType LinkInterfaceMap; @@ -1086,7 +1168,7 @@ const cmGeneratorTarget* head, bool secondPass) const; cmLinkImplementation const* GetLinkImplementation(const std::string& config, - LinkInterfaceFor implFor, + UseTo usage, bool secondPass) const; enum class LinkItemRole @@ -1127,7 +1209,7 @@ cmLinkInterface const* GetImportLinkInterface(const std::string& config, const cmGeneratorTarget* head, - LinkInterfaceFor interfaceFor, + UseTo usage, bool secondPass = false) const; using KindedSourcesMapType = std::map<std::string, KindedSources>; @@ -1141,7 +1223,7 @@ mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists; bool MaybeHaveInterfaceProperty(std::string const& prop, cmGeneratorExpressionContext* context, - LinkInterfaceFor interfaceFor) const; + UseTo usage) const; using TargetPropertyEntryVector = std::vector<std::unique_ptr<TargetPropertyEntry>>; @@ -1177,9 +1259,8 @@ }; void ExpandLinkItems(std::string const& prop, cmBTStringRange entries, std::string const& config, - const cmGeneratorTarget* headTarget, - LinkInterfaceFor interfaceFor, LinkInterfaceField field, - cmLinkInterface& iface) const; + const cmGeneratorTarget* headTarget, UseTo usage, + LinkInterfaceField field, cmLinkInterface& iface) const; struct LookupLinkItemScope { @@ -1218,7 +1299,7 @@ cmLinkImplementationLibraries const* GetLinkImplementationLibrariesInternal( const std::string& config, const cmGeneratorTarget* head, - LinkInterfaceFor implFor) const; + UseTo usage) const; bool ComputeOutputDir(const std::string& config, cmStateEnums::ArtifactType artifact, std::string& out) const; @@ -1282,9 +1363,15 @@ void ComputeLinkInterfaceRuntimeLibraries( const std::string& config, cmOptionalLinkInterface& iface) const; + // If this method is made public, or call sites are added outside of + // methods computing cached members, add dedicated caching members. + std::vector<cmGeneratorTarget const*> GetLinkInterfaceClosure( + std::string const& config, cmGeneratorTarget const* headTarget, + UseTo usage) const; + public: const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure( - const std::string& config) const; + const std::string& config, UseTo usage) const; mutable std::map<std::string, std::string> MaxLanguageStandards; std::map<std::string, std::string> const& GetMaxLanguageStandards() const @@ -1370,6 +1457,14 @@ TargetPropertyEntry(cmLinkImplItem 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, + const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem); + virtual const std::string& Evaluate( cmLocalGenerator* lg, const std::string& config, cmGeneratorTarget const* headTarget,
diff --git a/Source/cmGeneratorTarget_CompatibleInterface.cxx b/Source/cmGeneratorTarget_CompatibleInterface.cxx new file mode 100644 index 0000000..3af205e --- /dev/null +++ b/Source/cmGeneratorTarget_CompatibleInterface.cxx
@@ -0,0 +1,741 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <algorithm> +#include <cassert> +#include <cerrno> +#include <cstdlib> +#include <cstring> +#include <iterator> +#include <map> +#include <set> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cmext/algorithm> + +#include "cmComputeLinkInformation.h" +#include "cmGeneratorExpression.h" +#include "cmList.h" +#include "cmLocalGenerator.h" +#include "cmMessageType.h" +#include "cmRange.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmValue.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; +} + +const cmGeneratorTarget::CompatibleInterfacesBase& +cmGeneratorTarget::GetCompatibleInterfaces(std::string const& config) const +{ + cmGeneratorTarget::CompatibleInterfaces& compat = + this->CompatibleInterfacesMap[config]; + if (!compat.Done) { + compat.Done = true; + compat.PropsBool.insert("POSITION_INDEPENDENT_CODE"); + compat.PropsString.insert("AUTOUIC_OPTIONS"); + std::vector<cmGeneratorTarget const*> const& deps = + this->GetLinkImplementationClosure(config, UseTo::Compile); + for (cmGeneratorTarget const* li : deps) { +#define CM_READ_COMPATIBLE_INTERFACE(X, x) \ + if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) { \ + cmList props(*prop); \ + compat.Props##x.insert(props.begin(), props.end()); \ + } + CM_READ_COMPATIBLE_INTERFACE(BOOL, Bool) + CM_READ_COMPATIBLE_INTERFACE(STRING, String) + CM_READ_COMPATIBLE_INTERFACE(NUMBER_MIN, NumberMin) + CM_READ_COMPATIBLE_INTERFACE(NUMBER_MAX, NumberMax) +#undef CM_READ_COMPATIBLE_INTERFACE + } + } + return compat; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentBoolProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsBool.count(p) > 0; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentStringProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsString.count(p) > 0; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMinProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsNumberMin.count(p) > 0; +} + +bool cmGeneratorTarget::IsLinkInterfaceDependentNumberMaxProperty( + const std::string& p, const std::string& config) const +{ + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + return false; + } + return this->GetCompatibleInterfaces(config).PropsNumberMax.count(p) > 0; +} + +enum CompatibleType +{ + BoolType, + StringType, + NumberMinType, + NumberMaxType +}; + +template <typename PropertyType> +PropertyType getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, + const std::string& prop, + const std::string& config, + CompatibleType, PropertyType*); + +template <> +bool getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, + const std::string& prop, + const std::string& config, + CompatibleType /*unused*/, + bool* /*unused*/) +{ + return tgt->GetLinkInterfaceDependentBoolProperty(prop, config); +} + +template <> +const char* getLinkInterfaceDependentProperty(cmGeneratorTarget const* tgt, + const std::string& prop, + const std::string& config, + CompatibleType t, + const char** /*unused*/) +{ + switch (t) { + case BoolType: + assert(false && + "String compatibility check function called for boolean"); + return nullptr; + case StringType: + return tgt->GetLinkInterfaceDependentStringProperty(prop, config); + case NumberMinType: + return tgt->GetLinkInterfaceDependentNumberMinProperty(prop, config); + case NumberMaxType: + return tgt->GetLinkInterfaceDependentNumberMaxProperty(prop, config); + } + assert(false && "Unreachable!"); + return nullptr; +} + +template <typename PropertyType> +void checkPropertyConsistency(cmGeneratorTarget const* depender, + cmGeneratorTarget const* dependee, + const std::string& propName, + std::set<std::string>& emitted, + const std::string& config, CompatibleType t, + PropertyType* /*unused*/) +{ + cmValue prop = dependee->GetProperty(propName); + if (!prop) { + return; + } + + cmList props{ *prop }; + std::string pdir = + cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/prop_tgt/"); + + for (std::string const& p : props) { + std::string pname = cmSystemTools::HelpFileName(p); + std::string pfile = pdir + pname + ".rst"; + if (cmSystemTools::FileExists(pfile, true)) { + std::ostringstream e; + e << "Target \"" << dependee->GetName() << "\" has property \"" << p + << "\" listed in its " << propName + << " property. " + "This is not allowed. Only user-defined properties may appear " + "listed in the " + << propName << " property."; + depender->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, + e.str()); + return; + } + if (emitted.insert(p).second) { + getLinkInterfaceDependentProperty<PropertyType>(depender, p, config, t, + nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + } + } +} + +namespace { +std::string intersect(const std::set<std::string>& s1, + const std::set<std::string>& s2) +{ + std::set<std::string> intersect; + std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), + std::inserter(intersect, intersect.begin())); + if (!intersect.empty()) { + return *intersect.begin(); + } + return ""; +} + +std::string intersect(const std::set<std::string>& s1, + const std::set<std::string>& s2, + const std::set<std::string>& s3) +{ + std::string result; + result = intersect(s1, s2); + if (!result.empty()) { + return result; + } + result = intersect(s1, s3); + if (!result.empty()) { + return result; + } + return intersect(s2, s3); +} + +std::string intersect(const std::set<std::string>& s1, + const std::set<std::string>& s2, + const std::set<std::string>& s3, + const std::set<std::string>& s4) +{ + std::string result; + result = intersect(s1, s2); + if (!result.empty()) { + return result; + } + result = intersect(s1, s3); + if (!result.empty()) { + return result; + } + result = intersect(s1, s4); + if (!result.empty()) { + return result; + } + return intersect(s2, s3, s4); +} +} + +void cmGeneratorTarget::CheckPropertyCompatibility( + cmComputeLinkInformation& info, const std::string& config) const +{ + const cmComputeLinkInformation::ItemVector& deps = info.GetItems(); + + std::set<std::string> emittedBools; + static const std::string strBool = "COMPATIBLE_INTERFACE_BOOL"; + std::set<std::string> emittedStrings; + static const std::string strString = "COMPATIBLE_INTERFACE_STRING"; + std::set<std::string> emittedMinNumbers; + static const std::string strNumMin = "COMPATIBLE_INTERFACE_NUMBER_MIN"; + std::set<std::string> emittedMaxNumbers; + static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX"; + + for (auto const& dep : deps) { + if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + continue; + } + + checkPropertyConsistency<bool>(this, dep.Target, strBool, emittedBools, + config, BoolType, nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + checkPropertyConsistency<const char*>(this, dep.Target, strString, + emittedStrings, config, StringType, + nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + checkPropertyConsistency<const char*>(this, dep.Target, strNumMin, + emittedMinNumbers, config, + NumberMinType, nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + checkPropertyConsistency<const char*>(this, dep.Target, strNumMax, + emittedMaxNumbers, config, + NumberMaxType, nullptr); + if (cmSystemTools::GetErrorOccurredFlag()) { + return; + } + } + + std::string prop = intersect(emittedBools, emittedStrings, emittedMinNumbers, + emittedMaxNumbers); + + if (!prop.empty()) { + // Use a sorted std::vector to keep the error message sorted. + std::vector<std::string> props; + auto i = emittedBools.find(prop); + if (i != emittedBools.end()) { + props.push_back(strBool); + } + i = emittedStrings.find(prop); + if (i != emittedStrings.end()) { + props.push_back(strString); + } + i = emittedMinNumbers.find(prop); + if (i != emittedMinNumbers.end()) { + props.push_back(strNumMin); + } + i = emittedMaxNumbers.find(prop); + if (i != emittedMaxNumbers.end()) { + props.push_back(strNumMax); + } + std::sort(props.begin(), props.end()); + + std::string propsString = cmStrCat( + cmJoin(cmMakeRange(props).retreat(1), ", "), " and the ", props.back()); + + std::ostringstream e; + e << "Property \"" << prop << "\" appears in both the " << propsString + << " property in the dependencies of target \"" << this->GetName() + << "\". This is not allowed. A property may only require " + "compatibility " + "in a boolean interpretation, a numeric minimum, a numeric maximum " + "or a " + "string interpretation, but not a mixture."; + this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } +} + +template <typename PropertyType> +std::string valueAsString(PropertyType); +template <> +std::string valueAsString<bool>(bool value) +{ + return value ? "TRUE" : "FALSE"; +} +template <> +std::string valueAsString<const char*>(const char* value) +{ + return value ? value : "(unset)"; +} +template <> +std::string valueAsString<std::string>(std::string value) +{ + return value; +} +template <> +std::string valueAsString<cmValue>(cmValue value) +{ + return value ? *value : std::string("(unset)"); +} +template <> +std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/) +{ + return "(unset)"; +} + +static std::string compatibilityType(CompatibleType t) +{ + switch (t) { + case BoolType: + return "Boolean compatibility"; + case StringType: + return "String compatibility"; + case NumberMaxType: + return "Numeric maximum compatibility"; + case NumberMinType: + return "Numeric minimum compatibility"; + } + assert(false && "Unreachable!"); + return ""; +} + +static std::string compatibilityAgree(CompatibleType t, bool dominant) +{ + switch (t) { + case BoolType: + case StringType: + return dominant ? "(Disagree)\n" : "(Agree)\n"; + case NumberMaxType: + case NumberMinType: + return dominant ? "(Dominant)\n" : "(Ignored)\n"; + } + assert(false && "Unreachable!"); + return ""; +} + +template <typename PropertyType> +PropertyType getTypedProperty( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter = nullptr); + +template <> +bool getTypedProperty<bool>(cmGeneratorTarget const* tgt, + const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) +{ + if (genexInterpreter == nullptr) { + return tgt->GetPropertyAsBool(prop); + } + + cmValue value = tgt->GetProperty(prop); + return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop)); +} + +template <> +const char* getTypedProperty<const char*>( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) +{ + cmValue value = tgt->GetProperty(prop); + + if (genexInterpreter == nullptr) { + return value.GetCStr(); + } + + return genexInterpreter->Evaluate(value ? *value : "", prop).c_str(); +} + +template <> +std::string getTypedProperty<std::string>( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) +{ + cmValue value = tgt->GetProperty(prop); + + if (genexInterpreter == nullptr) { + return valueAsString(value); + } + + return genexInterpreter->Evaluate(value ? *value : "", prop); +} + +template <typename PropertyType> +PropertyType impliedValue(PropertyType); +template <> +bool impliedValue<bool>(bool /*unused*/) +{ + return false; +} +template <> +const char* impliedValue<const char*>(const char* /*unused*/) +{ + return ""; +} +template <> +std::string impliedValue<std::string>(std::string /*unused*/) // NOLINT(*) +{ + return std::string(); +} + +template <typename PropertyType> +std::pair<bool, PropertyType> consistentProperty(PropertyType lhs, + PropertyType rhs, + CompatibleType t); + +template <> +std::pair<bool, bool> consistentProperty(bool lhs, bool rhs, + CompatibleType /*unused*/) +{ + return { lhs == rhs, lhs }; +} + +static std::pair<bool, const char*> consistentStringProperty(const char* lhs, + const char* rhs) +{ + const bool b = strcmp(lhs, rhs) == 0; + return { b, b ? lhs : nullptr }; +} + +static std::pair<bool, std::string> consistentStringProperty( + const std::string& lhs, const std::string& rhs) +{ + const bool b = lhs == rhs; + return { b, b ? lhs : valueAsString(nullptr) }; +} + +static std::pair<bool, const char*> consistentNumberProperty(const char* lhs, + const char* rhs, + CompatibleType t) +{ + char* pEnd; + + long lnum = strtol(lhs, &pEnd, 0); + if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) { + return { false, nullptr }; + } + + long rnum = strtol(rhs, &pEnd, 0); + if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) { + return { false, nullptr }; + } + + if (t == NumberMaxType) { + return { true, std::max(lnum, rnum) == lnum ? lhs : rhs }; + } + + return { true, std::min(lnum, rnum) == lnum ? lhs : rhs }; +} + +template <> +std::pair<bool, const char*> consistentProperty(const char* lhs, + const char* rhs, + CompatibleType t) +{ + if (!lhs && !rhs) { + return { true, lhs }; + } + if (!lhs) { + return { true, rhs }; + } + if (!rhs) { + return { true, lhs }; + } + + switch (t) { + case BoolType: { + bool same = cmIsOn(lhs) == cmIsOn(rhs); + return { same, same ? lhs : nullptr }; + } + case StringType: + return consistentStringProperty(lhs, rhs); + case NumberMinType: + case NumberMaxType: + return consistentNumberProperty(lhs, rhs, t); + } + assert(false && "Unreachable!"); + return { false, nullptr }; +} + +static std::pair<bool, std::string> consistentProperty(const std::string& lhs, + const std::string& rhs, + CompatibleType t) +{ + const std::string null_ptr = valueAsString(nullptr); + + if (lhs == null_ptr && rhs == null_ptr) { + return { true, lhs }; + } + if (lhs == null_ptr) { + return { true, rhs }; + } + if (rhs == null_ptr) { + return { true, lhs }; + } + + switch (t) { + case BoolType: { + bool same = cmIsOn(lhs) == cmIsOn(rhs); + return { same, same ? lhs : null_ptr }; + } + case StringType: + return consistentStringProperty(lhs, rhs); + case NumberMinType: + case NumberMaxType: { + auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t); + return { value.first, + value.first ? std::string(value.second) : null_ptr }; + } + } + assert(false && "Unreachable!"); + return { false, null_ptr }; +} + +template <typename PropertyType> +PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, + const std::string& p, + const std::string& config, + const char* defaultValue, + CompatibleType t, + PropertyType* /*unused*/) +{ + PropertyType propContent = getTypedProperty<PropertyType>(tgt, p); + + std::vector<std::string> headPropKeys = tgt->GetPropertyKeys(); + const bool explicitlySet = cm::contains(headPropKeys, p); + + const bool impliedByUse = tgt->IsNullImpliedByLinkLibraries(p); + assert((impliedByUse ^ explicitlySet) || (!impliedByUse && !explicitlySet)); + + std::vector<cmGeneratorTarget const*> const& deps = + tgt->GetLinkImplementationClosure(config, UseTo::Compile); + + if (deps.empty()) { + return propContent; + } + bool propInitialized = explicitlySet; + + std::string report = cmStrCat(" * Target \"", tgt->GetName()); + if (explicitlySet) { + report += "\" has property content \""; + report += valueAsString<PropertyType>(propContent); + report += "\"\n"; + } else if (impliedByUse) { + report += "\" property is implied by use.\n"; + } else { + report += "\" property not set.\n"; + } + + std::string interfaceProperty = "INTERFACE_" + p; + std::unique_ptr<cmGeneratorExpressionInterpreter> genexInterpreter; + if (p == "POSITION_INDEPENDENT_CODE") { + // Corresponds to EvaluatingPICExpression. + genexInterpreter = cm::make_unique<cmGeneratorExpressionInterpreter>( + tgt->GetLocalGenerator(), config, tgt); + } + + for (cmGeneratorTarget const* theTarget : deps) { + // An error should be reported if one dependency + // has INTERFACE_POSITION_INDEPENDENT_CODE ON and the other + // has INTERFACE_POSITION_INDEPENDENT_CODE OFF, or if the + // target itself has a POSITION_INDEPENDENT_CODE which disagrees + // with a dependency. + + std::vector<std::string> propKeys = theTarget->GetPropertyKeys(); + + const bool ifaceIsSet = cm::contains(propKeys, interfaceProperty); + PropertyType ifacePropContent = getTypedProperty<PropertyType>( + theTarget, interfaceProperty, genexInterpreter.get()); + + std::string reportEntry; + if (ifaceIsSet) { + reportEntry += " * Target \""; + reportEntry += theTarget->GetName(); + reportEntry += "\" property value \""; + reportEntry += valueAsString<PropertyType>(ifacePropContent); + reportEntry += "\" "; + } + + if (explicitlySet) { + if (ifaceIsSet) { + std::pair<bool, PropertyType> consistent = + consistentProperty(propContent, ifacePropContent, t); + report += reportEntry; + report += compatibilityAgree(t, propContent != consistent.second); + if (!consistent.first) { + std::ostringstream e; + e << "Property " << p << " on target \"" << tgt->GetName() + << "\" does\nnot match the " + "INTERFACE_" + << p + << " property requirement\nof " + "dependency \"" + << theTarget->GetName() << "\".\n"; + cmSystemTools::Error(e.str()); + break; + } + propContent = consistent.second; + continue; + } + // Explicitly set on target and not set in iface. Can't disagree. + continue; + } + if (impliedByUse) { + propContent = impliedValue<PropertyType>(propContent); + + if (ifaceIsSet) { + std::pair<bool, PropertyType> consistent = + consistentProperty(propContent, ifacePropContent, t); + report += reportEntry; + report += compatibilityAgree(t, propContent != consistent.second); + if (!consistent.first) { + std::ostringstream e; + e << "Property " << p << " on target \"" << tgt->GetName() + << "\" is\nimplied to be " << defaultValue + << " because it was used to determine the link libraries\n" + "already. The INTERFACE_" + << p << " property on\ndependency \"" << theTarget->GetName() + << "\" is in conflict.\n"; + cmSystemTools::Error(e.str()); + break; + } + propContent = consistent.second; + continue; + } + // Implicitly set on target and not set in iface. Can't disagree. + continue; + } + if (ifaceIsSet) { + if (propInitialized) { + std::pair<bool, PropertyType> consistent = + consistentProperty(propContent, ifacePropContent, t); + report += reportEntry; + report += compatibilityAgree(t, propContent != consistent.second); + if (!consistent.first) { + std::ostringstream e; + e << "The INTERFACE_" << p << " property of \"" + << theTarget->GetName() << "\" does\nnot agree with the value of " + << p << " already determined\nfor \"" << tgt->GetName() << "\".\n"; + cmSystemTools::Error(e.str()); + break; + } + propContent = consistent.second; + continue; + } + report += reportEntry + "(Interface set)\n"; + propContent = ifacePropContent; + propInitialized = true; + } else { + // Not set. Nothing to agree on. + continue; + } + } + + tgt->ReportPropertyOrigin(p, valueAsString<PropertyType>(propContent), + report, compatibilityType(t)); + return propContent; +} + +bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<bool>(this, p, config, "FALSE", + BoolType, nullptr); +} + +std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<std::string>( + this, p, config, "FALSE", BoolType, nullptr); +} + +const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<const char*>( + this, p, config, "empty", StringType, nullptr); +} + +const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMinProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<const char*>( + this, p, config, "empty", NumberMinType, nullptr); +} + +const char* cmGeneratorTarget::GetLinkInterfaceDependentNumberMaxProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<const char*>( + this, p, config, "empty", NumberMaxType, nullptr); +}
diff --git a/Source/cmGeneratorTarget_IncludeDirectories.cxx b/Source/cmGeneratorTarget_IncludeDirectories.cxx new file mode 100644 index 0000000..6db467f --- /dev/null +++ b/Source/cmGeneratorTarget_IncludeDirectories.cxx
@@ -0,0 +1,338 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include "cmConfigure.h" + +#include <set> +#include <sstream> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/optional> +#include <cmext/algorithm> + +#include "cmEvaluatedTargetProperty.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +enum class IncludeDirectoryFallBack +{ + BINARY, + OBJECT +}; + +std::string AddLangSpecificInterfaceIncludeDirectories( + const cmGeneratorTarget* root, const cmGeneratorTarget* target, + const std::string& lang, const std::string& config, + const std::string& propertyName, IncludeDirectoryFallBack mode, + cmGeneratorExpressionDAGChecker* context) +{ + cmGeneratorExpressionDAGChecker dag{ + target->GetBacktrace(), target, propertyName, nullptr, context, + target->GetLocalGenerator(), config + }; + switch (dag.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dag.ReportError( + nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName"); + CM_FALLTHROUGH; + 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 ""; + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + std::string directories; + if (const auto* link_interface = + target->GetLinkInterfaceLibraries(config, root, UseTo::Compile)) { + for (const cmLinkItem& library : link_interface->Libraries) { + if (const cmGeneratorTarget* dependency = library.Target) { + if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { + auto* lg = dependency->GetLocalGenerator(); + std::string value = dependency->GetSafeProperty(propertyName); + if (value.empty()) { + if (mode == IncludeDirectoryFallBack::BINARY) { + value = lg->GetCurrentBinaryDirectory(); + } else if (mode == IncludeDirectoryFallBack::OBJECT) { + value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', + lg->GetTargetDirectory(dependency)); + } + } + + if (!directories.empty()) { + directories += ";"; + } + directories += value; + } + } + } + } + return directories; +} + +void AddLangSpecificImplicitIncludeDirectories( + const cmGeneratorTarget* target, const std::string& lang, + const std::string& config, const std::string& propertyName, + IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries) +{ + if (const auto* libraries = + target->GetLinkImplementationLibraries(config, UseTo::Compile)) { + cmGeneratorExpressionDAGChecker dag{ + target->GetBacktrace(), target, propertyName, nullptr, nullptr, + target->GetLocalGenerator(), config + }; + + for (const cmLinkImplItem& library : libraries->Libraries) { + if (const cmGeneratorTarget* dependency = library.Target) { + if (!dependency->IsInBuildSystem()) { + continue; + } + if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { + auto* lg = dependency->GetLocalGenerator(); + EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; + + if (lang == "Swift") { + entry.Values.emplace_back( + dependency->GetSwiftModuleDirectory(config)); + } else if (cmValue val = dependency->GetProperty(propertyName)) { + entry.Values.emplace_back(*val); + } else { + if (mode == IncludeDirectoryFallBack::BINARY) { + entry.Values.emplace_back(lg->GetCurrentBinaryDirectory()); + } else if (mode == IncludeDirectoryFallBack::OBJECT) { + entry.Values.emplace_back( + dependency->GetObjectDirectory(config)); + } + } + + cmExpandList( + AddLangSpecificInterfaceIncludeDirectories( + target, dependency, lang, config, propertyName, mode, &dag), + entry.Values); + entries.Entries.emplace_back(std::move(entry)); + } + } + } + } +} + +void processIncludeDirectories(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries& entries, + std::vector<BT<std::string>>& includes, + std::unordered_set<std::string>& uniqueIncludes, + bool debugIncludes) +{ + for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + cmLinkImplItem const& item = entry.LinkImplItem; + std::string const& targetName = item.AsStr(); + bool const fromImported = item.Target && item.Target->IsImported(); + bool const checkCMP0027 = item.CheckCMP0027; + + std::string usedIncludes; + for (std::string& entryInclude : entry.Values) { + if (fromImported && !cmSystemTools::FileExists(entryInclude)) { + std::ostringstream e; + MessageType messageType = MessageType::FATAL_ERROR; + if (checkCMP0027) { + switch (tgt->GetPolicyStatusCMP0027()) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0027) << "\n"; + CM_FALLTHROUGH; + case cmPolicies::OLD: + messageType = MessageType::AUTHOR_WARNING; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::NEW: + break; + } + } + /* clang-format off */ + e << "Imported target \"" << targetName << "\" includes " + "non-existent path\n \"" << entryInclude << "\"\nin its " + "INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:\n" + "* The path was deleted, renamed, or moved to another " + "location.\n" + "* An install or uninstall procedure did not complete " + "successfully.\n" + "* The installation package was faulty and references files it " + "does not provide.\n"; + /* clang-format on */ + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + return; + } + + if (!cmSystemTools::FileIsFullPath(entryInclude)) { + 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_INCLUDE_DIRECTORIES:\n" + " \"" << entryInclude << "\""; + /* clang-format on */ + } else { + switch (tgt->GetPolicyStatusCMP0021()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0021) << "\n"; + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + e << "Found relative path while evaluating include directories of " + "\"" + << tgt->GetName() << "\":\n \"" << entryInclude << "\"\n"; + } + if (!noMessage) { + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + + if (!cmIsOff(entryInclude)) { + cmSystemTools::ConvertToUnixSlashes(entryInclude); + } + + if (uniqueIncludes.insert(entryInclude).second) { + includes.emplace_back(entryInclude, entry.Backtrace); + if (debugIncludes) { + usedIncludes += " * " + entryInclude + "\n"; + } + } + } + if (!usedIncludes.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used includes for target ") + tgt->GetName() + ":\n" + + usedIncludes, + entry.Backtrace); + } + } +} +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( + const std::string& config, const std::string& lang) const +{ + ConfigAndLanguage cacheKey(config, lang); + { + auto it = this->IncludeDirectoriesCache.find(cacheKey); + if (it != this->IncludeDirectoriesCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> includes; + std::unordered_set<std::string> uniqueIncludes; + + cmGeneratorExpressionDAGChecker dagChecker(this, "INCLUDE_DIRECTORIES", + nullptr, nullptr, + this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugIncludes = !this->DebugIncludesDone && + cm::contains(debugProperties, "INCLUDE_DIRECTORIES"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugIncludesDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, lang, &dagChecker, this->IncludeDirectoriesEntries); + + if (lang == "Swift") { + AddLangSpecificImplicitIncludeDirectories( + this, lang, config, "Swift_MODULE_DIRECTORY", + IncludeDirectoryFallBack::BINARY, entries); + } + + if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) { + + const std::string propertyName = "ISPC_HEADER_DIRECTORY"; + + // If this target has ISPC sources make sure to add the header + // directory to other compilation units + if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) { + if (cmValue val = this->GetProperty(propertyName)) { + includes.emplace_back(*val); + } else { + includes.emplace_back(this->GetObjectDirectory(config)); + } + } + + AddLangSpecificImplicitIncludeDirectories( + this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT, + entries); + } + + AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + processIncludeDirectories(this, entries, includes, uniqueIncludes, + debugIncludes); + + if (this->IsApple()) { + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibraries(config, UseTo::Compile)) { + for (cmLinkImplItem const& lib : impl->Libraries) { + std::string libDir; + if (lib.Target == nullptr) { + libDir = cmSystemTools::CollapseFullPath( + lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); + } else if (lib.Target->Target->IsFrameworkOnApple() || + this->IsImportedFrameworkFolderOnApple(config)) { + libDir = lib.Target->GetLocation(config); + } else { + continue; + } + + auto fwDescriptor = + this->GetGlobalGenerator()->SplitFrameworkPath(libDir); + if (!fwDescriptor) { + continue; + } + + auto fwInclude = fwDescriptor->GetFrameworkPath(); + if (uniqueIncludes.insert(fwInclude).second) { + includes.emplace_back(fwInclude, cmListFileBacktrace()); + } + } + } + } + + this->IncludeDirectoriesCache.emplace(cacheKey, includes); + return includes; +}
diff --git a/Source/cmGeneratorTarget_Link.cxx b/Source/cmGeneratorTarget_Link.cxx new file mode 100644 index 0000000..50bcefa --- /dev/null +++ b/Source/cmGeneratorTarget_Link.cxx
@@ -0,0 +1,1541 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <algorithm> +#include <cassert> +#include <cstdio> +#include <map> +#include <set> +#include <sstream> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmAlgorithms.h" +#include "cmComputeLinkInformation.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmRange.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocationKind.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetLinkLibraryType.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +const std::string kINTERFACE_LINK_LIBRARIES = "INTERFACE_LINK_LIBRARIES"; +const std::string kINTERFACE_LINK_LIBRARIES_DIRECT = + "INTERFACE_LINK_LIBRARIES_DIRECT"; +const std::string kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE = + "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"; + +unsigned int CheckLinkLibrariesSuppressionRAIICount; +void MaybeEnableCheckLinkLibraries(cmOptionalLinkImplementation& impl) +{ + if (CheckLinkLibrariesSuppressionRAIICount == 0) { + impl.CheckLinkLibraries = true; + } +} +void MaybeEnableCheckLinkLibraries(cmOptionalLinkInterface& iface) +{ + if (CheckLinkLibrariesSuppressionRAIICount == 0) { + iface.CheckLinkLibraries = true; + } +} +} + +class cmTargetCollectLinkLanguages +{ +public: + cmTargetCollectLinkLanguages(cmGeneratorTarget const* target, + std::string config, + std::unordered_set<std::string>& languages, + cmGeneratorTarget const* head, bool secondPass) + : Config(std::move(config)) + , Languages(languages) + , HeadTarget(head) + , SecondPass(secondPass) + { + this->Visited.insert(target); + } + + void Visit(cmLinkItem const& item) + { + if (!item.Target) { + return; + } + if (!this->Visited.insert(item.Target).second) { + return; + } + cmLinkInterface const* iface = item.Target->GetLinkInterface( + this->Config, this->HeadTarget, this->SecondPass); + if (!iface) { + return; + } + if (iface->HadLinkLanguageSensitiveCondition) { + this->HadLinkLanguageSensitiveCondition = true; + } + + for (std::string const& language : iface->Languages) { + this->Languages.insert(language); + } + + for (cmLinkItem const& lib : iface->Libraries) { + this->Visit(lib); + } + } + + bool GetHadLinkLanguageSensitiveCondition() const + { + return this->HadLinkLanguageSensitiveCondition; + } + +private: + std::string Config; + std::unordered_set<std::string>& Languages; + cmGeneratorTarget const* HeadTarget; + std::set<cmGeneratorTarget const*> Visited; + bool SecondPass; + bool HadLinkLanguageSensitiveCondition = false; +}; + +cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( + const std::string& config) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static LinkClosure const empty = { {}, {} }; + return ∅ + } + + std::string key(cmSystemTools::UpperCase(config)); + auto i = this->LinkClosureMap.find(key); + if (i == this->LinkClosureMap.end()) { + LinkClosure lc; + this->ComputeLinkClosure(config, lc); + LinkClosureMapType::value_type entry(key, lc); + i = this->LinkClosureMap.insert(entry).first; + } + return &i->second; +} + +class cmTargetSelectLinker +{ + int Preference = 0; + cmGeneratorTarget const* Target; + cmGlobalGenerator* GG; + std::set<std::string> Preferred; + +public: + cmTargetSelectLinker(cmGeneratorTarget const* target) + : Target(target) + { + this->GG = this->Target->GetLocalGenerator()->GetGlobalGenerator(); + } + void Consider(const std::string& lang) + { + int preference = this->GG->GetLinkerPreference(lang); + if (preference > this->Preference) { + this->Preference = preference; + this->Preferred.clear(); + } + if (preference == this->Preference) { + this->Preferred.insert(lang); + } + } + std::string Choose() + { + if (this->Preferred.empty()) { + return ""; + } + if (this->Preferred.size() > 1) { + std::ostringstream e; + e << "Target " << this->Target->GetName() + << " contains multiple languages with the highest linker preference" + << " (" << this->Preference << "):\n"; + for (std::string const& li : this->Preferred) { + e << " " << li << "\n"; + } + e << "Set the LINKER_LANGUAGE property for this target."; + cmake* cm = this->Target->GetLocalGenerator()->GetCMakeInstance(); + cm->IssueMessage(MessageType::FATAL_ERROR, e.str(), + this->Target->GetBacktrace()); + } + return *this->Preferred.begin(); + } +}; + +bool cmGeneratorTarget::ComputeLinkClosure(const std::string& config, + LinkClosure& lc, + bool secondPass) const +{ + // Get languages built in this target. + std::unordered_set<std::string> languages; + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass); + assert(impl); + languages.insert(impl->Languages.cbegin(), impl->Languages.cend()); + + // Add interface languages from linked targets. + // cmTargetCollectLinkLanguages cll(this, config, languages, this, + // secondPass); + cmTargetCollectLinkLanguages cll(this, config, languages, this, secondPass); + for (cmLinkImplItem const& lib : impl->Libraries) { + cll.Visit(lib); + } + + // Store the transitive closure of languages. + cm::append(lc.Languages, languages); + + // Choose the language whose linker should be used. + if (secondPass || lc.LinkerLanguage.empty()) { + // Find the language with the highest preference value. + cmTargetSelectLinker tsl(this); + + // First select from the languages compiled directly in this target. + for (std::string const& l : impl->Languages) { + tsl.Consider(l); + } + + // Now consider languages that propagate from linked targets. + for (std::string const& lang : languages) { + std::string propagates = + "CMAKE_" + lang + "_LINKER_PREFERENCE_PROPAGATES"; + if (this->Makefile->IsOn(propagates)) { + tsl.Consider(lang); + } + } + + lc.LinkerLanguage = tsl.Choose(); + } + + return impl->HadLinkLanguageSensitiveCondition || + cll.GetHadLinkLanguageSensitiveCondition(); +} + +void cmGeneratorTarget::ComputeLinkClosure(const std::string& config, + LinkClosure& lc) const +{ + bool secondPass = false; + + { + LinkClosure linkClosure; + linkClosure.LinkerLanguage = this->LinkerLanguage; + + bool hasHardCodedLinkerLanguage = this->Target->GetProperty("HAS_CXX") || + !this->Target->GetSafeProperty("LINKER_LANGUAGE").empty(); + + // Get languages built in this target. + secondPass = this->ComputeLinkClosure(config, linkClosure, false) && + !hasHardCodedLinkerLanguage; + this->LinkerLanguage = linkClosure.LinkerLanguage; + if (!secondPass) { + lc = std::move(linkClosure); + } + } + + if (secondPass) { + LinkClosure linkClosure; + + this->ComputeLinkClosure(config, linkClosure, secondPass); + lc = std::move(linkClosure); + + // linker language must not be changed between the two passes + if (this->LinkerLanguage != lc.LinkerLanguage) { + std::ostringstream e; + e << "Evaluation of $<LINK_LANGUAGE:...> or $<LINK_LAND_AND_ID:...> " + "changes\nthe linker language for target \"" + << this->GetName() << "\" (from '" << this->LinkerLanguage << "' to '" + << lc.LinkerLanguage << "') which is invalid."; + cmSystemTools::Error(e.str()); + } + } +} + +static void processILibs(const std::string& config, + cmGeneratorTarget const* headTarget, + cmLinkItem const& item, cmGlobalGenerator* gg, + std::vector<cmGeneratorTarget const*>& tgts, + std::set<cmGeneratorTarget const*>& emitted, + UseTo usage) +{ + if (item.Target && emitted.insert(item.Target).second) { + tgts.push_back(item.Target); + if (cmLinkInterfaceLibraries const* iface = + item.Target->GetLinkInterfaceLibraries(config, headTarget, usage)) { + for (cmLinkItem const& lib : iface->Libraries) { + processILibs(config, headTarget, lib, gg, tgts, emitted, usage); + } + } + } +} + +std::vector<cmGeneratorTarget const*> +cmGeneratorTarget::GetLinkInterfaceClosure(std::string const& config, + cmGeneratorTarget const* headTarget, + UseTo usage) const +{ + cmGlobalGenerator* gg = this->GetLocalGenerator()->GetGlobalGenerator(); + std::vector<cmGeneratorTarget const*> tgts; + std::set<cmGeneratorTarget const*> emitted; + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(config, headTarget, usage)) { + for (cmLinkItem const& lib : iface->Libraries) { + processILibs(config, headTarget, lib, gg, tgts, emitted, usage); + } + } + return tgts; +} + +const std::vector<const cmGeneratorTarget*>& +cmGeneratorTarget::GetLinkImplementationClosure(const std::string& config, + UseTo usage) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static std::vector<const cmGeneratorTarget*> const empty; + return empty; + } + + LinkImplClosure& tgts = + (usage == UseTo::Compile ? this->LinkImplClosureForUsageMap[config] + : this->LinkImplClosureForLinkMap[config]); + if (!tgts.Done) { + tgts.Done = true; + std::set<cmGeneratorTarget const*> emitted; + + cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibraries(config, usage); + assert(impl); + + for (cmLinkImplItem const& lib : impl->Libraries) { + processILibs(config, this, lib, + this->LocalGenerator->GetGlobalGenerator(), tgts, emitted, + usage); + } + } + return tgts; +} + +cmComputeLinkInformation* cmGeneratorTarget::GetLinkInformation( + const std::string& config) const +{ + // Lookup any existing information for this configuration. + std::string key(cmSystemTools::UpperCase(config)); + auto i = this->LinkInformation.find(key); + if (i == this->LinkInformation.end()) { + // Compute information for this configuration. + auto info = cm::make_unique<cmComputeLinkInformation>(this, config); + if (info && !info->Compute()) { + info.reset(); + } + + // Store the information for this configuration. + i = this->LinkInformation.emplace(key, std::move(info)).first; + + if (i->second) { + this->CheckPropertyCompatibility(*i->second, config); + } + } + return i->second.get(); +} + +void cmGeneratorTarget::CheckLinkLibraries() const +{ + bool linkLibrariesOnlyTargets = + this->GetPropertyAsBool("LINK_LIBRARIES_ONLY_TARGETS"); + + // Evaluate the link interface of this target if needed for extra checks. + if (linkLibrariesOnlyTargets) { + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& config : configs) { + this->GetLinkInterfaceLibraries(config, this, UseTo::Link); + } + } + + // Check link the implementation for each generated configuration. + for (auto const& hmp : this->LinkImplMap) { + HeadToLinkImplementationMap const& hm = hmp.second; + // There could be several entries used when computing the pre-CMP0022 + // default link interface. Check only the entry for our own link impl. + auto const hmi = hm.find(this); + if (hmi == hm.end() || !hmi->second.LibrariesDone || + !hmi->second.CheckLinkLibraries) { + continue; + } + for (cmLinkImplItem const& item : hmi->second.Libraries) { + if (!this->VerifyLinkItemColons(LinkItemRole::Implementation, item)) { + return; + } + if (linkLibrariesOnlyTargets && + !this->VerifyLinkItemIsTarget(LinkItemRole::Implementation, item)) { + return; + } + } + } + + // Check link the interface for each generated combination of + // configuration and consuming head target. We should not need to + // consider LinkInterfaceUsageRequirementsOnlyMap because its entries + // should be a subset of LinkInterfaceMap (with LINK_ONLY left out). + for (auto const& hmp : this->LinkInterfaceMap) { + for (auto const& hmi : hmp.second) { + if (!hmi.second.LibrariesDone || !hmi.second.CheckLinkLibraries) { + continue; + } + for (cmLinkItem const& item : hmi.second.Libraries) { + if (!this->VerifyLinkItemColons(LinkItemRole::Interface, item)) { + return; + } + if (linkLibrariesOnlyTargets && + !this->VerifyLinkItemIsTarget(LinkItemRole::Interface, item)) { + return; + } + } + } + } +} + +cmGeneratorTarget::CheckLinkLibrariesSuppressionRAII:: + CheckLinkLibrariesSuppressionRAII() +{ + ++CheckLinkLibrariesSuppressionRAIICount; +} + +cmGeneratorTarget::CheckLinkLibrariesSuppressionRAII:: + ~CheckLinkLibrariesSuppressionRAII() +{ + --CheckLinkLibrariesSuppressionRAIICount; +} + +namespace { +cm::string_view missingTargetPossibleReasons = + "Possible reasons include:\n" + " * There is a typo in the target name.\n" + " * A find_package call is missing for an IMPORTED target.\n" + " * An ALIAS target is missing.\n"_s; +} + +bool cmGeneratorTarget::VerifyLinkItemColons(LinkItemRole role, + cmLinkItem const& item) const +{ + if (item.Target || cmHasPrefix(item.AsStr(), "<LINK_GROUP:"_s) || + item.AsStr().find("::") == std::string::npos) { + return true; + } + MessageType messageType = MessageType::FATAL_ERROR; + std::string e; + switch (this->GetLocalGenerator()->GetPolicyStatus(cmPolicies::CMP0028)) { + case cmPolicies::WARN: { + e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0028), "\n"); + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + + if (role == LinkItemRole::Implementation) { + e = cmStrCat(e, "Target \"", this->GetName(), "\" links to"); + } else { + e = cmStrCat(e, "The link interface of target \"", this->GetName(), + "\" contains"); + } + e = + cmStrCat(e, ":\n ", item.AsStr(), "\n", "but the target was not found. ", + missingTargetPossibleReasons); + cmListFileBacktrace backtrace = item.Backtrace; + if (backtrace.Empty()) { + backtrace = this->GetBacktrace(); + } + this->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(messageType, e, + backtrace); + return false; +} + +bool cmGeneratorTarget::VerifyLinkItemIsTarget(LinkItemRole role, + cmLinkItem const& item) const +{ + if (item.Target) { + return true; + } + std::string const& str = item.AsStr(); + if (!str.empty() && + (str[0] == '-' || str[0] == '$' || str[0] == '`' || + str.find_first_of("/\\") != std::string::npos || + cmHasPrefix(str, "<LINK_LIBRARY:"_s) || + cmHasPrefix(str, "<LINK_GROUP:"_s))) { + return true; + } + + std::string e = cmStrCat("Target \"", this->GetName(), + "\" has LINK_LIBRARIES_ONLY_TARGETS enabled, but ", + role == LinkItemRole::Implementation + ? "it links to" + : "its link interface contains", + ":\n ", item.AsStr(), "\nwhich is not a target. ", + missingTargetPossibleReasons); + cmListFileBacktrace backtrace = item.Backtrace; + if (backtrace.Empty()) { + backtrace = this->GetBacktrace(); + } + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e, backtrace); + return false; +} + +bool cmGeneratorTarget::IsLinkLookupScope(std::string const& n, + cmLocalGenerator const*& lg) const +{ + if (cmHasLiteralPrefix(n, CMAKE_DIRECTORY_ID_SEP)) { + cmDirectoryId const dirId = n.substr(cmStrLen(CMAKE_DIRECTORY_ID_SEP)); + if (dirId.String.empty()) { + lg = this->LocalGenerator; + return true; + } + if (cmLocalGenerator const* otherLG = + this->GlobalGenerator->FindLocalGenerator(dirId)) { + lg = otherLG; + return true; + } + } + return false; +} + +cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem( + std::string const& n, cmListFileBacktrace const& bt, + std::string const& linkFeature, LookupLinkItemScope* scope, + LookupSelf lookupSelf) const +{ + cm::optional<cmLinkItem> maybeItem; + if (this->IsLinkLookupScope(n, scope->LG)) { + return maybeItem; + } + + std::string name = this->CheckCMP0004(n); + if (name.empty() || + (lookupSelf == LookupSelf::No && name == this->GetName())) { + return maybeItem; + } + maybeItem = + this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature); + return maybeItem; +} + +void cmGeneratorTarget::ExpandLinkItems(std::string const& prop, + cmBTStringRange entries, + std::string const& config, + cmGeneratorTarget const* headTarget, + UseTo usage, LinkInterfaceField field, + cmLinkInterface& iface) const +{ + if (entries.empty()) { + return; + } + // Keep this logic in sync with ComputeLinkImplementationLibraries. + cmGeneratorExpressionDAGChecker dagChecker(this, prop, nullptr, nullptr, + this->LocalGenerator, config); + // The $<LINK_ONLY> expression may be in a link interface to specify + // private link dependencies that are otherwise excluded from usage + // requirements. + if (usage == UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + dagChecker.SetTransitivePropertiesOnlyCMP0131(); + } + cmMakefile const* mf = this->LocalGenerator->GetMakefile(); + LookupLinkItemScope scope{ this->LocalGenerator }; + for (BT<std::string> const& entry : entries) { + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), + entry.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(entry.Value); + cge->SetEvaluateForBuildsystem(true); + cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget, + &dagChecker, this, + headTarget->LinkerLanguage) }; + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& lib : libs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + lib, cge->GetBacktrace(), linkFeature, &scope, + field == LinkInterfaceField::Libraries ? LookupSelf::No + : LookupSelf::Yes)) { + cmLinkItem item = std::move(*maybeItem); + + if (field == LinkInterfaceField::HeadInclude) { + iface.HeadInclude.emplace_back(std::move(item)); + continue; + } + if (field == LinkInterfaceField::HeadExclude) { + iface.HeadExclude.emplace_back(std::move(item)); + continue; + } + if (!item.Target) { + // Report explicitly linked object files separately. + std::string const& maybeObj = item.AsStr(); + if (cmSystemTools::FileIsFullPath(maybeObj)) { + cmSourceFile const* sf = + mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); + if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + item.ObjectSource = sf; + iface.Objects.emplace_back(std::move(item)); + continue; + } + } + } + + iface.Libraries.emplace_back(std::move(item)); + } + } + if (cge->GetHadHeadSensitiveCondition()) { + iface.HadHeadSensitiveCondition = true; + } + if (cge->GetHadContextSensitiveCondition()) { + iface.HadContextSensitiveCondition = true; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + iface.HadLinkLanguageSensitiveCondition = true; + } + } +} + +cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( + const std::string& config, cmGeneratorTarget const* head) const +{ + return this->GetLinkInterface(config, head, false); +} + +cmLinkInterface const* cmGeneratorTarget::GetLinkInterface( + const std::string& config, cmGeneratorTarget const* head, + bool secondPass) const +{ + // Imported targets have their own link interface. + if (this->IsImported()) { + return this->GetImportLinkInterface(config, head, UseTo::Link, secondPass); + } + + // Link interfaces are not supported for executables that do not + // export symbols. + if (this->GetType() == cmStateEnums::EXECUTABLE && + !this->IsExecutableWithExports()) { + return nullptr; + } + + // Lookup any existing link interface for this configuration. + cmHeadToLinkInterfaceMap& hm = this->GetHeadToLinkInterfaceMap(config); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[head]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } + MaybeEnableCheckLinkLibraries(iface); + if (!iface.LibrariesDone) { + iface.LibrariesDone = true; + this->ComputeLinkInterfaceLibraries(config, iface, head, UseTo::Link); + } + if (!iface.AllDone) { + iface.AllDone = true; + if (iface.Exists) { + this->ComputeLinkInterface(config, iface, head, secondPass); + this->ComputeLinkInterfaceRuntimeLibraries(config, iface); + } + } + + return iface.Exists ? &iface : nullptr; +} + +void cmGeneratorTarget::ComputeLinkInterface( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget) const +{ + this->ComputeLinkInterface(config, iface, headTarget, false); +} + +void cmGeneratorTarget::ComputeLinkInterface( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget, bool secondPass) const +{ + if (iface.Explicit) { + if (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->GetType() == cmStateEnums::STATIC_LIBRARY || + this->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + // Shared libraries may have runtime implementation dependencies + // on other shared libraries that are not in the interface. + std::set<cmLinkItem> emitted; + for (cmLinkItem const& lib : iface.Libraries) { + emitted.insert(lib); + } + if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass); + for (cmLinkImplItem const& lib : impl->Libraries) { + if (emitted.insert(lib).second) { + if (lib.Target) { + // This is a runtime dependency on another shared library. + if (lib.Target->GetType() == cmStateEnums::SHARED_LIBRARY) { + iface.SharedDeps.push_back(lib); + } + } else { + // TODO: Recognize shared library file names. Perhaps this + // should be moved to cmComputeLinkInformation, but that + // creates a chicken-and-egg problem since this list is needed + // for its construction. + } + } + } + } + } + } else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN || + this->GetPolicyStatusCMP0022() == cmPolicies::OLD) { + // The link implementation is the default link interface. + cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget, + UseTo::Link); + iface.ImplementationIsInterface = true; + iface.WrongConfigLibraries = impl->WrongConfigLibraries; + } + + if (this->LinkLanguagePropagatesToDependents()) { + // Targets using this archive need its language runtime libraries. + if (cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Link, secondPass)) { + iface.Languages = impl->Languages; + } + } + + if (this->GetType() == cmStateEnums::STATIC_LIBRARY) { + // Construct the property name suffix for this configuration. + std::string suffix = "_"; + if (!config.empty()) { + suffix += cmSystemTools::UpperCase(config); + } else { + suffix += "NOCONFIG"; + } + + // How many repetitions are needed if this library has cyclic + // dependencies? + std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix); + if (cmValue config_reps = this->GetProperty(propName)) { + sscanf(config_reps->c_str(), "%u", &iface.Multiplicity); + } else if (cmValue reps = + this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) { + sscanf(reps->c_str(), "%u", &iface.Multiplicity); + } + } +} + +const cmLinkInterfaceLibraries* cmGeneratorTarget::GetLinkInterfaceLibraries( + const std::string& config, cmGeneratorTarget const* head, UseTo usage) const +{ + // Imported targets have their own link interface. + if (this->IsImported()) { + return this->GetImportLinkInterface(config, head, usage); + } + + // Link interfaces are not supported for executables that do not + // export symbols. + if (this->GetType() == cmStateEnums::EXECUTABLE && + !this->IsExecutableWithExports()) { + return nullptr; + } + + // Lookup any existing link interface for this configuration. + cmHeadToLinkInterfaceMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) + : this->GetHeadToLinkInterfaceMap(config)); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[head]; + MaybeEnableCheckLinkLibraries(iface); + if (!iface.LibrariesDone) { + iface.LibrariesDone = true; + this->ComputeLinkInterfaceLibraries(config, iface, head, usage); + } + + return iface.Exists ? &iface : nullptr; +} + +void cmGeneratorTarget::ComputeLinkInterfaceLibraries( + const std::string& config, cmOptionalLinkInterface& iface, + cmGeneratorTarget const* headTarget, UseTo usage) const +{ + // Construct the property name suffix for this configuration. + std::string suffix = "_"; + if (!config.empty()) { + suffix += cmSystemTools::UpperCase(config); + } else { + suffix += "NOCONFIG"; + } + + // An explicit list of interface libraries may be set for shared + // libraries and executables that export symbols. + bool haveExplicitLibraries = false; + cmValue explicitLibrariesCMP0022OLD; + std::string linkIfacePropCMP0022OLD; + bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD && + this->GetPolicyStatusCMP0022() != cmPolicies::WARN); + if (cmp0022NEW) { + // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES. + haveExplicitLibraries = !this->Target->GetLinkInterfaceEntries().empty() || + !this->Target->GetLinkInterfaceDirectEntries().empty() || + !this->Target->GetLinkInterfaceDirectExcludeEntries().empty(); + } else { + // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a + // shared lib or executable. + if (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->IsExecutableWithExports()) { + // Lookup the per-configuration property. + linkIfacePropCMP0022OLD = cmStrCat("LINK_INTERFACE_LIBRARIES", suffix); + explicitLibrariesCMP0022OLD = this->GetProperty(linkIfacePropCMP0022OLD); + + // If not set, try the generic property. + if (!explicitLibrariesCMP0022OLD) { + linkIfacePropCMP0022OLD = "LINK_INTERFACE_LIBRARIES"; + explicitLibrariesCMP0022OLD = + this->GetProperty(linkIfacePropCMP0022OLD); + } + } + + if (explicitLibrariesCMP0022OLD && + this->GetPolicyStatusCMP0022() == cmPolicies::WARN && + !this->PolicyWarnedCMP0022) { + // Compare the explicitly set old link interface properties to the + // preferred new link interface property one and warn if different. + cmValue newExplicitLibraries = + this->GetProperty("INTERFACE_LINK_LIBRARIES"); + if (newExplicitLibraries && + (*newExplicitLibraries != *explicitLibrariesCMP0022OLD)) { + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" + "Target \"" << this->GetName() << "\" has an " + "INTERFACE_LINK_LIBRARIES property which differs from its " << + linkIfacePropCMP0022OLD << " properties." + "\n" + "INTERFACE_LINK_LIBRARIES:\n" + " " << *newExplicitLibraries << "\n" << + linkIfacePropCMP0022OLD << ":\n" + " " << *explicitLibrariesCMP0022OLD << "\n"; + /* clang-format on */ + this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); + this->PolicyWarnedCMP0022 = true; + } + } + + haveExplicitLibraries = static_cast<bool>(explicitLibrariesCMP0022OLD); + } + + // There is no implicit link interface for executables or modules + // so if none was explicitly set then there is no link interface. + if (!haveExplicitLibraries && + (this->GetType() == cmStateEnums::EXECUTABLE || + (this->GetType() == cmStateEnums::MODULE_LIBRARY))) { + return; + } + iface.Exists = true; + + // If CMP0022 is NEW then the plain tll signature sets the + // INTERFACE_LINK_LIBRARIES property. Even if the project + // clears it, the link interface is still explicit. + iface.Explicit = cmp0022NEW || explicitLibrariesCMP0022OLD; + + if (cmp0022NEW) { + // The interface libraries are specified by INTERFACE_LINK_LIBRARIES. + // Use its special representation directly to get backtraces. + this->ExpandLinkItems( + kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), + config, headTarget, usage, LinkInterfaceField::Libraries, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + this->Target->GetLinkInterfaceDirectEntries(), + config, headTarget, usage, + LinkInterfaceField::HeadInclude, iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + this->Target->GetLinkInterfaceDirectExcludeEntries(), + config, headTarget, usage, + LinkInterfaceField::HeadExclude, iface); + } else if (explicitLibrariesCMP0022OLD) { + // The interface libraries have been explicitly set in pre-CMP0022 style. + std::vector<BT<std::string>> entries; + entries.emplace_back(*explicitLibrariesCMP0022OLD); + this->ExpandLinkItems(linkIfacePropCMP0022OLD, cmMakeRange(entries), + config, headTarget, usage, + LinkInterfaceField::Libraries, iface); + } + + // If the link interface is explicit, do not fall back to the link impl. + if (iface.Explicit) { + return; + } + + // The link implementation is the default link interface. + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget, + usage)) { + iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), + impl->Libraries.end()); + if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && + !this->PolicyWarnedCMP0022 && usage == UseTo::Link) { + // Compare the link implementation fallback link interface to the + // preferred new link interface property and warn if different. + cmLinkInterface ifaceNew; + this->ExpandLinkItems( + kINTERFACE_LINK_LIBRARIES, this->Target->GetLinkInterfaceEntries(), + config, headTarget, usage, LinkInterfaceField::Libraries, ifaceNew); + if (ifaceNew.Libraries != iface.Libraries) { + std::string oldLibraries = cmJoin(impl->Libraries, ";"); + std::string newLibraries = cmJoin(ifaceNew.Libraries, ";"); + if (oldLibraries.empty()) { + oldLibraries = "(empty)"; + } + if (newLibraries.empty()) { + newLibraries = "(empty)"; + } + + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0022) << "\n" + "Target \"" << this->GetName() << "\" has an " + "INTERFACE_LINK_LIBRARIES property. " + "This should be preferred as the source of the link interface " + "for this library but because CMP0022 is not set CMake is " + "ignoring the property and using the link implementation " + "as the link interface instead." + "\n" + "INTERFACE_LINK_LIBRARIES:\n" + " " << newLibraries << "\n" + "Link implementation:\n" + " " << oldLibraries << "\n"; + /* clang-format on */ + this->LocalGenerator->IssueMessage(MessageType::AUTHOR_WARNING, + w.str()); + this->PolicyWarnedCMP0022 = true; + } + } + } +} + +namespace { + +template <typename ReturnType> +ReturnType constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt); + +template <> +inline cmLinkImplItem constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt) +{ + return cmLinkImplItem(cmLinkItem(target, false, bt), false); +} + +template <> +inline cmLinkItem constructItem(cmGeneratorTarget* target, + cmListFileBacktrace const& bt) +{ + return cmLinkItem(target, false, bt); +} + +template <typename ValueType> +std::vector<ValueType> computeImplicitLanguageTargets( + std::string const& lang, std::string const& config, + cmGeneratorTarget const* currentTarget) +{ + cmListFileBacktrace bt; + std::vector<ValueType> result; + cmLocalGenerator* lg = currentTarget->GetLocalGenerator(); + + std::string const& runtimeLibrary = + currentTarget->GetRuntimeLinkLibrary(lang, config); + if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition( + "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) { + cmList libsList{ *runtimeLinkOptions }; + result.reserve(libsList.size()); + + for (auto const& i : libsList) { + cmGeneratorTarget::TargetOrString resolved = + currentTarget->ResolveTargetReference(i, lg); + if (resolved.Target) { + result.emplace_back(constructItem<ValueType>(resolved.Target, bt)); + } + } + } + + return result; +} +} + +void cmGeneratorTarget::ComputeLinkInterfaceRuntimeLibraries( + const std::string& config, cmOptionalLinkInterface& iface) const +{ + for (std::string const& lang : iface.Languages) { + if ((lang == "CUDA" || lang == "HIP") && + iface.LanguageRuntimeLibraries.find(lang) == + iface.LanguageRuntimeLibraries.end()) { + auto implicitTargets = + computeImplicitLanguageTargets<cmLinkItem>(lang, config, this); + iface.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); + } + } +} + +void cmGeneratorTarget::ComputeLinkImplementationRuntimeLibraries( + const std::string& config, cmOptionalLinkImplementation& impl) const +{ + for (std::string const& lang : impl.Languages) { + if ((lang == "CUDA" || lang == "HIP") && + impl.LanguageRuntimeLibraries.find(lang) == + impl.LanguageRuntimeLibraries.end()) { + auto implicitTargets = + computeImplicitLanguageTargets<cmLinkImplItem>(lang, config, this); + impl.LanguageRuntimeLibraries[lang] = std::move(implicitTargets); + } + } +} + +const cmLinkInterface* cmGeneratorTarget::GetImportLinkInterface( + const std::string& config, cmGeneratorTarget const* headTarget, UseTo usage, + bool secondPass) const +{ + cmGeneratorTarget::ImportInfo const* info = this->GetImportInfo(config); + if (!info) { + return nullptr; + } + + cmHeadToLinkInterfaceMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkInterfaceUsageRequirementsMap(config) + : this->GetHeadToLinkInterfaceMap(config)); + + // If the link interface does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + headTarget = hm.begin()->first; + } + + cmOptionalLinkInterface& iface = hm[headTarget]; + if (secondPass) { + iface = cmOptionalLinkInterface(); + } + MaybeEnableCheckLinkLibraries(iface); + if (!iface.AllDone) { + iface.AllDone = true; + iface.LibrariesDone = true; + iface.Multiplicity = info->Multiplicity; + cmExpandList(info->Languages, iface.Languages); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT, + cmMakeRange(info->LibrariesHeadInclude), config, + headTarget, usage, LinkInterfaceField::HeadInclude, + iface); + this->ExpandLinkItems(kINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + cmMakeRange(info->LibrariesHeadExclude), config, + headTarget, usage, LinkInterfaceField::HeadExclude, + iface); + this->ExpandLinkItems(info->LibrariesProp, cmMakeRange(info->Libraries), + config, headTarget, usage, + LinkInterfaceField::Libraries, iface); + cmList deps{ info->SharedDeps }; + LookupLinkItemScope scope{ this->LocalGenerator }; + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& dep : deps) { + if (auto maybeLinkFeature = ParseLinkFeature(dep)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem( + dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) { + iface.SharedDeps.emplace_back(std::move(*maybeItem)); + } + } + } + + return &iface; +} + +cmHeadToLinkInterfaceMap& cmGeneratorTarget::GetHeadToLinkInterfaceMap( + const std::string& config) const +{ + return this->LinkInterfaceMap[cmSystemTools::UpperCase(config)]; +} + +cmHeadToLinkInterfaceMap& +cmGeneratorTarget::GetHeadToLinkInterfaceUsageRequirementsMap( + const std::string& config) const +{ + return this + ->LinkInterfaceUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; +} + +const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( + const std::string& config, UseTo usage) const +{ + return this->GetLinkImplementation(config, usage, false); +} + +const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( + const std::string& config, UseTo usage, bool secondPass) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + return nullptr; + } + + HeadToLinkImplementationMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) + : this->GetHeadToLinkImplementationMap(config)); + cmOptionalLinkImplementation& impl = hm[this]; + if (secondPass) { + impl = cmOptionalLinkImplementation(); + } + MaybeEnableCheckLinkLibraries(impl); + if (!impl.LibrariesDone) { + impl.LibrariesDone = true; + this->ComputeLinkImplementationLibraries(config, impl, this, usage); + } + if (!impl.LanguagesDone) { + impl.LanguagesDone = true; + this->ComputeLinkImplementationLanguages(config, impl); + this->ComputeLinkImplementationRuntimeLibraries(config, impl); + } + return &impl; +} + +cmGeneratorTarget::HeadToLinkImplementationMap& +cmGeneratorTarget::GetHeadToLinkImplementationMap( + std::string const& config) const +{ + return this->LinkImplMap[cmSystemTools::UpperCase(config)]; +} + +cmGeneratorTarget::HeadToLinkImplementationMap& +cmGeneratorTarget::GetHeadToLinkImplementationUsageRequirementsMap( + std::string const& config) const +{ + return this + ->LinkImplUsageRequirementsOnlyMap[cmSystemTools::UpperCase(config)]; +} + +cmLinkImplementationLibraries const* +cmGeneratorTarget::GetLinkImplementationLibraries(const std::string& config, + UseTo usage) const +{ + return this->GetLinkImplementationLibrariesInternal(config, this, usage); +} + +cmLinkImplementationLibraries const* +cmGeneratorTarget::GetLinkImplementationLibrariesInternal( + const std::string& config, cmGeneratorTarget const* head, UseTo usage) const +{ + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + return nullptr; + } + + // Populate the link implementation libraries for this configuration. + HeadToLinkImplementationMap& hm = + (usage == UseTo::Compile + ? this->GetHeadToLinkImplementationUsageRequirementsMap(config) + : this->GetHeadToLinkImplementationMap(config)); + + // If the link implementation does not depend on the head target + // then reuse the one from the head we computed first. + if (!hm.empty() && !hm.begin()->second.HadHeadSensitiveCondition) { + head = hm.begin()->first; + } + + cmOptionalLinkImplementation& impl = hm[head]; + MaybeEnableCheckLinkLibraries(impl); + if (!impl.LibrariesDone) { + impl.LibrariesDone = true; + this->ComputeLinkImplementationLibraries(config, impl, head, usage); + } + return &impl; +} + +namespace { +class TransitiveLinkImpl +{ + cmGeneratorTarget const* Self; + std::string const& Config; + UseTo ImplFor; + cmLinkImplementation& Impl; + + std::set<cmLinkItem> Emitted; + std::set<cmLinkItem> Excluded; + std::unordered_set<cmGeneratorTarget const*> Followed; + + void Follow(cmGeneratorTarget const* target); + +public: + TransitiveLinkImpl(cmGeneratorTarget const* self, std::string const& config, + UseTo usage, cmLinkImplementation& impl) + : Self(self) + , Config(config) + , ImplFor(usage) + , Impl(impl) + { + } + + void Compute(); +}; + +void TransitiveLinkImpl::Follow(cmGeneratorTarget const* target) +{ + if (!target || !this->Followed.insert(target).second || + target->GetPolicyStatusCMP0022() == cmPolicies::OLD || + target->GetPolicyStatusCMP0022() == cmPolicies::WARN) { + return; + } + + // Get this target's usage requirements. + cmLinkInterfaceLibraries const* iface = + target->GetLinkInterfaceLibraries(this->Config, this->Self, this->ImplFor); + if (!iface) { + return; + } + if (iface->HadContextSensitiveCondition) { + this->Impl.HadContextSensitiveCondition = true; + } + + // Process 'INTERFACE_LINK_LIBRARIES_DIRECT' usage requirements. + for (cmLinkItem const& item : iface->HeadInclude) { + // Inject direct dependencies from the item's usage requirements + // before the item itself. + this->Follow(item.Target); + + // Add the item itself, but at most once. + if (this->Emitted.insert(item).second) { + this->Impl.Libraries.emplace_back(item, /* checkCMP0027= */ false); + } + } + + // Follow transitive dependencies. + for (cmLinkItem const& item : iface->Libraries) { + this->Follow(item.Target); + } + + // Record exclusions from 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' + // usage requirements. + for (cmLinkItem const& item : iface->HeadExclude) { + this->Excluded.insert(item); + } +} + +void TransitiveLinkImpl::Compute() +{ + // Save the original items and start with an empty list. + std::vector<cmLinkImplItem> original = std::move(this->Impl.Libraries); + + // Avoid injecting any original items as usage requirements. + // This gives LINK_LIBRARIES final control over the order + // if it explicitly lists everything. + this->Emitted.insert(original.cbegin(), original.cend()); + + // Process each original item. + for (cmLinkImplItem& item : original) { + // Inject direct dependencies listed in 'INTERFACE_LINK_LIBRARIES_DIRECT' + // usage requirements before the item itself. + this->Follow(item.Target); + + // Add the item itself. + this->Impl.Libraries.emplace_back(std::move(item)); + } + + // Remove items listed in 'INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE' + // usage requirements found through any dependency above. + this->Impl.Libraries.erase( + std::remove_if(this->Impl.Libraries.begin(), this->Impl.Libraries.end(), + [this](cmLinkImplItem const& item) { + return this->Excluded.find(item) != this->Excluded.end(); + }), + this->Impl.Libraries.end()); +} + +void ComputeLinkImplTransitive(cmGeneratorTarget const* self, + std::string const& config, UseTo usage, + cmLinkImplementation& impl) +{ + TransitiveLinkImpl transitiveLinkImpl(self, config, usage, impl); + transitiveLinkImpl.Compute(); +} +} + +void cmGeneratorTarget::ComputeLinkImplementationLibraries( + const std::string& config, cmOptionalLinkImplementation& impl, + cmGeneratorTarget const* head, UseTo usage) const +{ + cmLocalGenerator const* lg = this->LocalGenerator; + cmMakefile const* mf = lg->GetMakefile(); + cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); + auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; + // Collect libraries directly linked in this configuration. + for (auto const& entry : entryRange) { + // Keep this logic in sync with ExpandLinkItems. + cmGeneratorExpressionDAGChecker dagChecker( + this, "LINK_LIBRARIES", nullptr, nullptr, this->LocalGenerator, config); + // The $<LINK_ONLY> expression may be used to specify link dependencies + // that are otherwise excluded from usage requirements. + if (usage == UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + switch (this->GetPolicyStatusCMP0131()) { + case cmPolicies::WARN: + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + dagChecker.SetTransitivePropertiesOnlyCMP0131(); + break; + } + } + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance(), + entry.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> const cge = + ge.Parse(entry.Value); + cge->SetEvaluateForBuildsystem(true); + std::string const& evaluated = + cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr, + this->LinkerLanguage); + bool const checkCMP0027 = evaluated != entry.Value; + cmList llibs(evaluated); + if (cge->GetHadHeadSensitiveCondition()) { + impl.HadHeadSensitiveCondition = true; + } + if (cge->GetHadContextSensitiveCondition()) { + impl.HadContextSensitiveCondition = true; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + impl.HadLinkLanguageSensitiveCondition = true; + } + + auto linkFeature = cmLinkItem::DEFAULT; + for (auto const& lib : llibs) { + if (auto maybeLinkFeature = ParseLinkFeature(lib)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (this->IsLinkLookupScope(lib, lg)) { + continue; + } + + // Skip entries that resolve to the target itself or are empty. + std::string name = this->CheckCMP0004(lib); + if (this->GetPolicyStatusCMP0108() == cmPolicies::NEW) { + // resolve alias name + auto* target = this->Makefile->FindTargetToUse(name); + if (target) { + name = target->GetName(); + } + } + if (name == this->GetName() || name.empty()) { + if (name == this->GetName()) { + bool noMessage = false; + MessageType messageType = MessageType::FATAL_ERROR; + std::ostringstream e; + switch (this->GetPolicyStatusCMP0038()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0038) << "\n"; + messageType = MessageType::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + + if (!noMessage) { + e << "Target \"" << this->GetName() << "\" links to itself."; + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + messageType, e.str(), this->GetBacktrace()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + continue; + } + + // The entry is meant for this configuration. + cmLinkItem item = this->ResolveLinkItem( + BT<std::string>(name, entry.Backtrace), lg, linkFeature); + if (item.Target) { + auto depsForTarget = synthTargetsForConfig.find(item.Target); + if (depsForTarget != synthTargetsForConfig.end()) { + for (auto const* depForTarget : depsForTarget->second) { + cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); + impl.Libraries.emplace_back(std::move(synthItem), false); + } + } + } else { + // Report explicitly linked object files separately. + std::string const& maybeObj = item.AsStr(); + if (cmSystemTools::FileIsFullPath(maybeObj)) { + cmSourceFile const* sf = + mf->GetSource(maybeObj, cmSourceFileLocationKind::Known); + if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + item.ObjectSource = sf; + impl.Objects.emplace_back(std::move(item)); + continue; + } + } + } + + impl.Libraries.emplace_back(std::move(item), checkCMP0027); + } + + std::set<std::string> const& seenProps = cge->GetSeenTargetProperties(); + for (std::string const& sp : seenProps) { + if (!this->GetProperty(sp)) { + this->LinkImplicitNullProperties.insert(sp); + } + } + cge->GetMaxLanguageStandard(this, this->MaxLanguageStandards); + } + + // Update the list of direct link dependencies from usage requirements. + if (head == this) { + ComputeLinkImplTransitive(this, config, usage, impl); + } + + // Get the list of configurations considered to be DEBUG. + std::vector<std::string> debugConfigs = + this->Makefile->GetCMakeInstance()->GetDebugConfigs(); + + cmTargetLinkLibraryType linkType = + CMP0003_ComputeLinkType(config, debugConfigs); + cmTarget::LinkLibraryVectorType const& oldllibs = + this->Target->GetOriginalLinkLibraries(); + + auto linkFeature = cmLinkItem::DEFAULT; + for (cmTarget::LibraryID const& oldllib : oldllibs) { + if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) { + linkFeature = std::move(*maybeLinkFeature); + continue; + } + + if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) { + std::string name = this->CheckCMP0004(oldllib.first); + if (name == this->GetName() || name.empty()) { + continue; + } + // Support OLD behavior for CMP0003. + impl.WrongConfigLibraries.push_back( + this->ResolveLinkItem(BT<std::string>(name), linkFeature)); + } + } +} + +cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( + std::string const& name) const +{ + return this->ResolveTargetReference(name, this->LocalGenerator); +} + +cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference( + std::string const& name, cmLocalGenerator const* lg) const +{ + TargetOrString resolved; + + if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(name)) { + resolved.Target = tgt; + } else { + resolved.String = name; + } + + return resolved; +} + +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, std::string const& linkFeature) const +{ + return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature); +} + +cmLinkItem cmGeneratorTarget::ResolveLinkItem( + BT<std::string> const& name, cmLocalGenerator const* lg, + std::string const& linkFeature) const +{ + auto bt = name.Backtrace; + TargetOrString resolved = this->ResolveTargetReference(name.Value, lg); + + if (!resolved.Target) { + return cmLinkItem(resolved.String, false, bt, linkFeature); + } + + // Check deprecation, issue message with `bt` backtrace. + if (resolved.Target->IsDeprecated()) { + std::ostringstream w; + /* clang-format off */ + w << + "The library that is being linked to, " << resolved.Target->GetName() << + ", 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); + } + + // Skip targets that will not really be linked. This is probably a + // name conflict between an external library and an executable + // within the project. + if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE && + !resolved.Target->IsExecutableWithExports()) { + return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature); + } + + return cmLinkItem(resolved.Target, false, bt, linkFeature); +}
diff --git a/Source/cmGeneratorTarget_LinkDirectories.cxx b/Source/cmGeneratorTarget_LinkDirectories.cxx new file mode 100644 index 0000000..bd89497 --- /dev/null +++ b/Source/cmGeneratorTarget_LinkDirectories.cxx
@@ -0,0 +1,154 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <map> +#include <sstream> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cmext/algorithm> + +#include "cmEvaluatedTargetProperty.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +void processLinkDirectories(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries& entries, + std::vector<BT<std::string>>& directories, + std::unordered_set<std::string>& uniqueDirectories, + bool debugDirectories) +{ + for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + cmLinkImplItem const& item = entry.LinkImplItem; + 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::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + e << "Found relative path while evaluating link directories of " + "\"" + << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; + } + if (!noMessage) { + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + if (messageType == MessageType::FATAL_ERROR) { + return; + } + } + } + + // Sanitize the path the same way the link_directories command does + // in case projects set the LINK_DIRECTORIES property directly. + cmSystemTools::ConvertToUnixSlashes(entryDirectory); + if (uniqueDirectories.insert(entryDirectory).second) { + directories.emplace_back(entryDirectory, entry.Backtrace); + if (debugDirectories) { + usedDirectories += " * " + entryDirectory + "\n"; + } + } + } + if (!usedDirectories.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used link directories for target ") + tgt->GetName() + + ":\n" + usedDirectories, + entry.Backtrace); + } + } +} +} + +void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = + this->GetLinkDirectories(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey( + config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); + { + auto it = this->LinkDirectoriesCache.find(cacheKey); + if (it != this->LinkDirectoriesCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueDirectories; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "LINK_DIRECTORIES", nullptr, nullptr, this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugDirectories = !this->DebugLinkDirectoriesDone && + cm::contains(debugProperties, "LINK_DIRECTORIES"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugLinkDirectoriesDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->LinkDirectoriesEntries); + + AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes, + this->GetPolicyStatusCMP0099() == cmPolicies::NEW + ? UseTo::Link + : UseTo::Compile); + + processLinkDirectories(this, entries, result, uniqueDirectories, + debugDirectories); + + this->LinkDirectoriesCache.emplace(cacheKey, result); + return result; +}
diff --git a/Source/cmGeneratorTarget_Options.cxx b/Source/cmGeneratorTarget_Options.cxx new file mode 100644 index 0000000..f77ef72 --- /dev/null +++ b/Source/cmGeneratorTarget_Options.cxx
@@ -0,0 +1,660 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include "cmConfigure.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <string> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmEvaluatedTargetProperty.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.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 "cmValue.h" +#include "cmake.h" + +enum class OptionsParse +{ + None, + Shell +}; + +namespace { +const auto DL_BEGIN = "<DEVICE_LINK>"_s; +const auto DL_END = "</DEVICE_LINK>"_s; + +void processOptions(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries const& entries, + std::vector<BT<std::string>>& options, + std::unordered_set<std::string>& uniqueOptions, + bool debugOptions, const char* logName, OptionsParse parse, + bool processDeviceOptions = false) +{ + bool splitOption = !processDeviceOptions; + for (EvaluatedTargetPropertyEntry const& entry : entries.Entries) { + std::string usedOptions; + for (std::string const& opt : entry.Values) { + if (processDeviceOptions && (opt == DL_BEGIN || opt == DL_END)) { + options.emplace_back(opt, entry.Backtrace); + splitOption = opt == DL_BEGIN; + continue; + } + + if (uniqueOptions.insert(opt).second) { + if (parse == OptionsParse::Shell && + cmHasLiteralPrefix(opt, "SHELL:")) { + if (splitOption) { + 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(std::string(opt.c_str() + 6), + entry.Backtrace); + } + } else { + options.emplace_back(opt, entry.Backtrace); + } + if (debugOptions) { + usedOptions += " * " + opt + "\n"; + } + } + } + if (!usedOptions.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used ") + logName + std::string(" for target ") + + tgt->GetName() + ":\n" + usedOptions, + entry.Backtrace); + } + } +} + +std::vector<BT<std::string>> wrapOptions( + std::vector<std::string>& options, const cmListFileBacktrace& bt, + const std::vector<std::string>& wrapperFlag, const std::string& wrapperSep, + bool concatFlagAndArgs) +{ + std::vector<BT<std::string>> result; + + if (options.empty()) { + return result; + } + + if (wrapperFlag.empty()) { + // nothing specified, insert elements as is + result.reserve(options.size()); + for (std::string& o : options) { + result.emplace_back(std::move(o), bt); + } + return result; + } + + for (std::vector<std::string>::size_type index = 0; index < options.size(); + index++) { + if (cmHasLiteralPrefix(options[index], "LINKER:")) { + // LINKER wrapper specified, insert elements as is + result.emplace_back(std::move(options[index]), bt); + continue; + } + if (cmHasLiteralPrefix(options[index], "-Wl,")) { + // replace option by LINKER wrapper + result.emplace_back(options[index].replace(0, 4, "LINKER:"), bt); + continue; + } + if (cmHasLiteralPrefix(options[index], "-Xlinker=")) { + // replace option by LINKER wrapper + result.emplace_back(options[index].replace(0, 9, "LINKER:"), bt); + continue; + } + if (options[index] == "-Xlinker") { + // replace option by LINKER wrapper + if (index + 1 < options.size()) { + result.emplace_back("LINKER:" + options[++index], bt); + } else { + result.emplace_back(std::move(options[index]), bt); + } + continue; + } + + // collect all options which must be transformed + std::vector<std::string> opts; + while (index < options.size()) { + if (!cmHasLiteralPrefix(options[index], "LINKER:") && + !cmHasLiteralPrefix(options[index], "-Wl,") && + !cmHasLiteralPrefix(options[index], "-Xlinker")) { + opts.emplace_back(std::move(options[index++])); + } else { + --index; + break; + } + } + if (opts.empty()) { + continue; + } + + if (!wrapperSep.empty()) { + if (concatFlagAndArgs) { + // insert flag elements except last one + for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { + result.emplace_back(*i, bt); + } + // concatenate last flag element and all list values + // in one option + result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); + } else { + for (std::string const& i : wrapperFlag) { + result.emplace_back(i, bt); + } + // concatenate all list values in one option + result.emplace_back(cmJoin(opts, wrapperSep), bt); + } + } else { + // prefix each element of list with wrapper + if (concatFlagAndArgs) { + std::transform(opts.begin(), opts.end(), opts.begin(), + [&wrapperFlag](std::string const& o) -> std::string { + return wrapperFlag.back() + o; + }); + } + for (std::string& o : opts) { + for (auto i = wrapperFlag.begin(), + e = concatFlagAndArgs ? wrapperFlag.end() - 1 + : wrapperFlag.end(); + i != e; ++i) { + result.emplace_back(*i, bt); + } + result.emplace_back(std::move(o), bt); + } + } + } + return result; +} +} + +void cmGeneratorTarget::GetCompileOptions(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = this->GetCompileOptions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::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; + } + } + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "COMPILE_OPTIONS", nullptr, nullptr, this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugOptions = !this->DebugCompileOptionsDone && + cm::contains(debugProperties, "COMPILE_OPTIONS"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugCompileOptionsDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->CompileOptionsEntries); + + AddInterfaceEntries(this, config, "INTERFACE_COMPILE_OPTIONS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + processOptions(this, entries, result, uniqueOptions, debugOptions, + "compile options", OptionsParse::Shell); + + CompileOptionsCache.emplace(cacheKey, result); + return result; +} + +void cmGeneratorTarget::GetCompileFeatures(std::vector<std::string>& result, + const std::string& config) const +{ + std::vector<BT<std::string>> tmp = this->GetCompileFeatures(config); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetCompileFeatures( + std::string const& config) const +{ + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueFeatures; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "COMPILE_FEATURES", nullptr, nullptr, this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugFeatures = !this->DebugCompileFeaturesDone && + cm::contains(debugProperties, "COMPILE_FEATURES"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugCompileFeaturesDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, std::string(), &dagChecker, this->CompileFeaturesEntries); + + AddInterfaceEntries(this, config, "INTERFACE_COMPILE_FEATURES", + std::string(), &dagChecker, entries, + IncludeRuntimeInterface::Yes); + + processOptions(this, entries, result, uniqueFeatures, debugFeatures, + "compile features", OptionsParse::None); + + return result; +} + +void cmGeneratorTarget::GetCompileDefinitions( + std::vector<std::string>& result, const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = + this->GetCompileDefinitions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::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; + } + } + std::vector<BT<std::string>> list; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker(this, "COMPILE_DEFINITIONS", + nullptr, nullptr, + this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugDefines = !this->DebugCompileDefinitionsDone && + cm::contains(debugProperties, "COMPILE_DEFINITIONS"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugCompileDefinitionsDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->CompileDefinitionsEntries); + + AddInterfaceEntries(this, config, "INTERFACE_COMPILE_DEFINITIONS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + if (!config.empty()) { + std::string configPropName = + "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); + cmValue configProp = this->GetProperty(configPropName); + if (configProp) { + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) { + case cmPolicies::WARN: { + this->LocalGenerator->IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0043)); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: { + std::unique_ptr<TargetPropertyEntry> entry = + TargetPropertyEntry::Create( + *this->LocalGenerator->GetCMakeInstance(), *configProp); + entries.Entries.emplace_back(EvaluateTargetPropertyEntry( + this, config, language, &dagChecker, *entry)); + } break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + break; + } + } + } + + processOptions(this, entries, list, uniqueOptions, debugDefines, + "compile definitions", OptionsParse::None); + + this->CompileDefinitionsCache.emplace(cacheKey, list); + return list; +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders( + const std::string& config, const std::string& language) const +{ + ConfigAndLanguage cacheKey(config, language); + { + auto it = this->PrecompileHeadersCache.find(cacheKey); + if (it != this->PrecompileHeadersCache.end()) { + return it->second; + } + } + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS", + nullptr, nullptr, + this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugDefines = !this->DebugPrecompileHeadersDone && + std::find(debugProperties.begin(), debugProperties.end(), + "PRECOMPILE_HEADERS") != debugProperties.end(); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugPrecompileHeadersDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->PrecompileHeadersEntries); + + AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes); + + std::vector<BT<std::string>> list; + processOptions(this, entries, list, uniqueOptions, debugDefines, + "precompile headers", OptionsParse::None); + + this->PrecompileHeadersCache.emplace(cacheKey, list); + return list; +} + +void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + if (this->IsDeviceLink() && + this->GetPolicyStatusCMP0105() != cmPolicies::NEW) { + // link options are not propagated to the device link step + return; + } + + std::vector<BT<std::string>> tmp = this->GetLinkOptions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions( + std::string const& config, std::string const& language) const +{ + ConfigAndLanguage cacheKey( + config, cmStrCat(language, this->IsDeviceLink() ? "-device" : "")); + { + auto it = this->LinkOptionsCache.find(cacheKey); + if (it != this->LinkOptionsCache.end()) { + return it->second; + } + } + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker( + this, "LINK_OPTIONS", nullptr, nullptr, this->LocalGenerator, config); + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugOptions = !this->DebugLinkOptionsDone && + cm::contains(debugProperties, "LINK_OPTIONS"); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugLinkOptionsDone = true; + } + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, language, &dagChecker, this->LinkOptionsEntries); + + AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes, + this->GetPolicyStatusCMP0099() == cmPolicies::NEW + ? UseTo::Link + : UseTo::Compile); + + processOptions(this, entries, result, uniqueOptions, debugOptions, + "link options", OptionsParse::Shell, this->IsDeviceLink()); + + if (this->IsDeviceLink()) { + // wrap host link options + const std::string wrapper(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG")); + cmList wrapperFlag{ wrapper }; + const std::string wrapperSep(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + "_DEVICE_COMPILER_WRAPPER_FLAG_SEP")); + bool concatFlagAndArgs = true; + if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { + concatFlagAndArgs = false; + wrapperFlag.pop_back(); + } + + auto it = result.begin(); + while (it != result.end()) { + if (it->Value == DL_BEGIN) { + // device link options, no treatment + it = result.erase(it); + it = std::find_if(it, result.end(), [](const BT<std::string>& item) { + return item.Value == DL_END; + }); + if (it != result.end()) { + it = result.erase(it); + } + } else { + // host link options must be wrapped + std::vector<std::string> options; + cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options); + auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag, + wrapperSep, concatFlagAndArgs); + it = result.erase(it); + // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect + // C++11 standard: 'std::vector::insert()' do not returns an iterator, + // so need to recompute the iterator after insertion. + if (it == result.end()) { + cm::append(result, hostOptions); + it = result.end(); + } else { + auto index = it - result.begin(); + result.insert(it, hostOptions.begin(), hostOptions.end()); + it = result.begin() + index + hostOptions.size(); + } + } + } + } + + // Last step: replace "LINKER:" prefixed elements by + // actual linker wrapper + result = this->ResolveLinkerWrapper(result, language); + + this->LinkOptionsCache.emplace(cacheKey, result); + return result; +} + +std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper( + std::vector<BT<std::string>>& result, const std::string& language, + bool joinItems) const +{ + // replace "LINKER:" prefixed elements by actual linker wrapper + const std::string wrapper(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + + (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG" + : "_LINKER_WRAPPER_FLAG"))); + cmList wrapperFlag{ wrapper }; + const std::string wrapperSep(this->Makefile->GetSafeDefinition( + "CMAKE_" + language + + (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG_SEP" + : "_LINKER_WRAPPER_FLAG_SEP"))); + bool concatFlagAndArgs = true; + if (!wrapperFlag.empty() && wrapperFlag.back() == " ") { + concatFlagAndArgs = false; + wrapperFlag.pop_back(); + } + + const std::string LINKER{ "LINKER:" }; + const std::string SHELL{ "SHELL:" }; + const std::string LINKER_SHELL = LINKER + SHELL; + + std::vector<BT<std::string>>::iterator entry; + while ((entry = std::find_if(result.begin(), result.end(), + [&LINKER](BT<std::string> const& item) -> bool { + return item.Value.compare(0, LINKER.length(), + LINKER) == 0; + })) != result.end()) { + std::string value = std::move(entry->Value); + cmListFileBacktrace bt = std::move(entry->Backtrace); + entry = result.erase(entry); + + std::vector<std::string> linkerOptions; + if (value.compare(0, LINKER_SHELL.length(), LINKER_SHELL) == 0) { + cmSystemTools::ParseUnixCommandLine( + value.c_str() + LINKER_SHELL.length(), linkerOptions); + } else { + linkerOptions = cmTokenize(value.substr(LINKER.length()), ","); + } + + if (linkerOptions.empty() || + (linkerOptions.size() == 1 && linkerOptions.front().empty())) { + continue; + } + + // for now, raise an error if prefix SHELL: is part of arguments + if (std::find_if(linkerOptions.begin(), linkerOptions.end(), + [&SHELL](const std::string& item) -> bool { + return item.find(SHELL) != std::string::npos; + }) != linkerOptions.end()) { + this->LocalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "'SHELL:' prefix is not supported as part of 'LINKER:' arguments.", + this->GetBacktrace()); + return result; + } + + std::vector<BT<std::string>> options = wrapOptions( + linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs); + if (joinItems) { + result.insert(entry, + cmJoin(cmRange<decltype(options.cbegin())>( + options.cbegin(), options.cend()), + " "_s)); + } else { + result.insert(entry, options.begin(), options.end()); + } + } + return result; +} + +void cmGeneratorTarget::GetStaticLibraryLinkOptions( + std::vector<std::string>& result, const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = + this->GetStaticLibraryLinkOptions(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( + std::string const& config, std::string const& language) const +{ + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + + cmGeneratorExpressionDAGChecker dagChecker(this, "STATIC_LIBRARY_OPTIONS", + nullptr, nullptr, + this->LocalGenerator, config); + + 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, config, language, &dagChecker, *entry)); + } + processOptions(this, entries, result, uniqueOptions, false, + "static library link options", OptionsParse::Shell); + + return result; +} + +void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::vector<BT<std::string>> tmp = this->GetLinkDepends(config, language); + result.reserve(tmp.size()); + for (BT<std::string>& v : tmp) { + result.emplace_back(std::move(v.Value)); + } +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( + std::string const& config, std::string const& language) const +{ + std::vector<BT<std::string>> result; + std::unordered_set<std::string> uniqueOptions; + cmGeneratorExpressionDAGChecker dagChecker( + this, "LINK_DEPENDS", nullptr, nullptr, this->LocalGenerator, config); + + EvaluatedTargetPropertyEntries entries; + if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) { + cmList depends{ *linkDepends }; + for (const auto& depend : depends) { + std::unique_ptr<TargetPropertyEntry> entry = TargetPropertyEntry::Create( + *this->LocalGenerator->GetCMakeInstance(), depend); + entries.Entries.emplace_back(EvaluateTargetPropertyEntry( + this, config, language, &dagChecker, *entry)); + } + } + AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language, + &dagChecker, entries, IncludeRuntimeInterface::Yes, + this->GetPolicyStatusCMP0099() == cmPolicies::NEW + ? UseTo::Link + : UseTo::Compile); + + processOptions(this, entries, result, uniqueOptions, false, "link depends", + OptionsParse::None); + + return result; +}
diff --git a/Source/cmGeneratorTarget_Sources.cxx b/Source/cmGeneratorTarget_Sources.cxx new file mode 100644 index 0000000..94e8897 --- /dev/null +++ b/Source/cmGeneratorTarget_Sources.cxx
@@ -0,0 +1,560 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <cstddef> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <string> +#include <type_traits> +#include <unordered_set> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmsys/RegularExpression.hxx" + +#include "cmAlgorithms.h" +#include "cmEvaluatedTargetProperty.h" +#include "cmFileSet.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGlobalGenerator.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmSourceFileLocation.h" +#include "cmSourceGroup.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" +#include "cmake.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; + +void AddObjectEntries(cmGeneratorTarget const* headTarget, + std::string const& config, + cmGeneratorExpressionDAGChecker* dagChecker, + EvaluatedTargetPropertyEntries& entries) +{ + if (cmLinkImplementationLibraries const* impl = + headTarget->GetLinkImplementationLibraries(config, UseTo::Compile)) { + entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition; + for (cmLinkImplItem const& lib : impl->Libraries) { + if (lib.Target && + lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { + std::string uniqueName = + headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely( + lib.Target); + std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">"; + cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(), + lib.Backtrace); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); + cge->SetEvaluateForBuildsystem(true); + + EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace); + cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config, + headTarget, dagChecker), + ee.Values); + if (cge->GetHadContextSensitiveCondition()) { + ee.ContextDependent = true; + } + entries.Entries.emplace_back(std::move(ee)); + } + } + } +} + +void addFileSetEntry(cmGeneratorTarget const* headTarget, + std::string const& config, + cmGeneratorExpressionDAGChecker* dagChecker, + cmFileSet const* fileSet, + EvaluatedTargetPropertyEntries& entries) +{ + auto dirCges = fileSet->CompileDirectoryEntries(); + auto dirs = fileSet->EvaluateDirectoryEntries( + dirCges, headTarget->GetLocalGenerator(), config, 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 tpe = cmGeneratorTarget::TargetPropertyEntry::CreateFileSet( + dirs, contextSensitiveDirs, std::move(entryCge), fileSet); + entries.Entries.emplace_back( + EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, *tpe)); + 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, + std::string const& config, + cmGeneratorExpressionDAGChecker* dagChecker, + 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, config, 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, config, dagChecker, cxxModuleSet, entries); + } + } +} + +bool processSources(cmGeneratorTarget const* tgt, + EvaluatedTargetPropertyEntries& entries, + std::vector<BT<std::string>>& srcs, + std::unordered_set<std::string>& uniqueSrcs, + bool debugSources) +{ + cmMakefile* mf = tgt->Target->GetMakefile(); + + bool contextDependent = entries.HadContextSensitiveCondition; + + for (EvaluatedTargetPropertyEntry& entry : entries.Entries) { + if (entry.ContextDependent) { + contextDependent = true; + } + + cmLinkImplItem const& item = entry.LinkImplItem; + std::string const& targetName = item.AsStr(); + + for (std::string& src : entry.Values) { + cmSourceFile* sf = mf->GetOrCreateSource(src); + std::string e; + std::string w; + std::string fullPath = sf->ResolveFullPath(&e, &w); + cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); + if (!w.empty()) { + cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); + } + if (fullPath.empty()) { + if (!e.empty()) { + cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); + } + return contextDependent; + } + + if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) { + std::ostringstream err; + if (!targetName.empty()) { + err << "Target \"" << targetName + << "\" contains relative path in its INTERFACE_SOURCES:\n \"" + << src << "\""; + } else { + err << "Found relative path while evaluating sources of \"" + << tgt->GetName() << "\":\n \"" << src << "\"\n"; + } + tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, + err.str()); + return contextDependent; + } + src = fullPath; + } + std::string usedSources; + for (std::string const& src : entry.Values) { + if (uniqueSrcs.insert(src).second) { + srcs.emplace_back(src, entry.Backtrace); + if (debugSources) { + usedSources += " * " + src + "\n"; + } + } + } + if (!usedSources.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::LOG, + std::string("Used sources for target ") + tgt->GetName() + ":\n" + + usedSources, + entry.Backtrace); + } + } + return contextDependent; +} +} + +std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( + std::string const& config) const +{ + std::vector<BT<std::string>> files; + + if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { + // At configure-time, this method can be called as part of getting the + // LOCATION property or to export() a file to be include()d. However + // there is no cmGeneratorTarget at configure-time, so search the SOURCES + // for TARGET_OBJECTS instead for backwards compatibility with OLD + // behavior of CMP0024 and CMP0026 only. + + cmBTStringRange sourceEntries = this->Target->GetSourceEntries(); + for (auto const& entry : sourceEntries) { + cmList items{ entry.Value }; + for (auto const& item : items) { + if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") && + item.back() == '>') { + continue; + } + files.emplace_back(item); + } + } + return files; + } + + cmList debugProperties{ this->Makefile->GetDefinition( + "CMAKE_DEBUG_TARGET_PROPERTIES") }; + bool debugSources = + !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES"); + + if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { + this->DebugSourcesDone = true; + } + + cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, nullptr, + this->LocalGenerator, config); + + EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries( + this, config, std::string(), &dagChecker, this->SourceEntries); + + std::unordered_set<std::string> uniqueSrcs; + bool contextDependentDirectSources = + processSources(this, entries, files, uniqueSrcs, debugSources); + + // Collect INTERFACE_SOURCES of all direct link-dependencies. + EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries; + AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(), + &dagChecker, linkInterfaceSourcesEntries, + IncludeRuntimeInterface::No, UseTo::Compile); + bool contextDependentInterfaceSources = processSources( + this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources); + + // Collect TARGET_OBJECTS of direct object link-dependencies. + bool contextDependentObjects = false; + if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) { + EvaluatedTargetPropertyEntries linkObjectsEntries; + AddObjectEntries(this, config, &dagChecker, linkObjectsEntries); + contextDependentObjects = processSources(this, 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 + // are specified without per-config generator expressions. + } + + // Collect this target's file sets. + EvaluatedTargetPropertyEntries fileSetEntries; + AddFileSetEntries(this, config, &dagChecker, fileSetEntries); + bool contextDependentFileSets = + processSources(this, fileSetEntries, files, uniqueSrcs, debugSources); + + // Determine if sources are context-dependent or not. + if (!contextDependentDirectSources && !contextDependentInterfaceSources && + !contextDependentObjects && !contextDependentFileSets) { + this->SourcesAreContextDependent = Tribool::False; + } else { + this->SourcesAreContextDependent = Tribool::True; + } + + return files; +} + +void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files, + const std::string& config) const +{ + std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config); + files.reserve(tmp.size()); + for (BT<cmSourceFile*>& v : tmp) { + files.push_back(v.Value); + } +} + +std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles( + std::string const& config) const +{ + std::vector<BT<cmSourceFile*>> files; + if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) { + // Since we are still configuring not all sources may exist yet, + // so we need to avoid full source classification because that + // requires the absolute paths to all sources to be determined. + // Since this is only for compatibility with old policies that + // projects should not depend on anymore, just compute the files + // without memoizing them. + std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); + std::set<cmSourceFile*> emitted; + for (BT<std::string> const& s : srcs) { + cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); + if (emitted.insert(sf).second) { + files.emplace_back(sf, s.Backtrace); + } + } + return files; + } + + KindedSources const& kinded = this->GetKindedSources(config); + files.reserve(kinded.Sources.size()); + for (SourceAndKind const& si : kinded.Sources) { + files.push_back(si.Source); + } + return files; +} + +void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( + std::vector<cmSourceFile*>& files, const std::string& config) const +{ + std::vector<BT<cmSourceFile*>> tmp = + this->GetSourceFilesWithoutObjectLibraries(config); + files.reserve(tmp.size()); + for (BT<cmSourceFile*>& v : tmp) { + files.push_back(v.Value); + } +} + +std::vector<BT<cmSourceFile*>> +cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( + std::string const& config) const +{ + std::vector<BT<cmSourceFile*>> files; + KindedSources const& kinded = this->GetKindedSources(config); + files.reserve(kinded.Sources.size()); + for (SourceAndKind const& si : kinded.Sources) { + if (si.Source.Value->GetObjectLibrary().empty()) { + files.push_back(si.Source); + } + } + return files; +} + +cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( + std::string const& config) const +{ + // If we already processed one configuration and found no dependency + // on configuration then always use the one result. + if (this->SourcesAreContextDependent == Tribool::False) { + return this->KindedSourcesMap.begin()->second; + } + + // Lookup any existing link implementation for this configuration. + std::string const key = cmSystemTools::UpperCase(config); + auto it = this->KindedSourcesMap.find(key); + if (it != this->KindedSourcesMap.end()) { + if (!it->second.Initialized) { + std::ostringstream e; + e << "The SOURCES of \"" << this->GetName() + << "\" use a generator expression that depends on the " + "SOURCES themselves."; + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); + static KindedSources empty; + return empty; + } + return it->second; + } + + // Add an entry to the map for this configuration. + KindedSources& files = this->KindedSourcesMap[key]; + this->ComputeKindedSources(files, config); + files.Initialized = true; + return files; +} + +void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, + std::string const& config) const +{ + // Get the source file paths by string. + std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config); + + cmsys::RegularExpression header_regex(CM_HEADER_REGEX); + std::vector<cmSourceFile*> badObjLib; + + std::set<cmSourceFile*> emitted; + for (BT<std::string> const& s : srcs) { + // Create each source at most once. + cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value); + if (!emitted.insert(sf).second) { + continue; + } + + // Compute the kind (classification) of this source file. + SourceKind kind; + std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); + cmFileSet 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)) { + kind = SourceKindCxxModuleSource; + } else if (this->Target->GetType() == cmStateEnums::UTILITY || + this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY + // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 + // NOLINTNEXTLINE(bugprone-branch-clone) + ) { + kind = SourceKindExtra; + } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) { + kind = SourceKindUnityBatched; + // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 + // NOLINTNEXTLINE(bugprone-branch-clone) + } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) { + kind = SourceKindHeader; + } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) { + kind = SourceKindExternalObject; + } else if (!sf->GetOrDetermineLanguage().empty()) { + kind = SourceKindObjectSource; + } else if (ext == "def") { + kind = SourceKindModuleDefinition; + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { + badObjLib.push_back(sf); + } + } else if (ext == "idl") { + kind = SourceKindIDL; + if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) { + badObjLib.push_back(sf); + } + } else if (ext == "resx") { + kind = SourceKindResx; + } else if (ext == "appxmanifest") { + kind = SourceKindAppManifest; + } else if (ext == "manifest") { + if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) { + kind = SourceKindExtra; + } else { + kind = SourceKindManifest; + } + } else if (ext == "pfx") { + kind = SourceKindCertificate; + } else if (ext == "xaml") { + kind = SourceKindXaml; + } else if (header_regex.find(sf->ResolveFullPath())) { + kind = SourceKindHeader; + } else { + kind = SourceKindExtra; + } + + // Save this classified source file in the result vector. + files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind }); + } + + if (!badObjLib.empty()) { + std::ostringstream e; + e << "OBJECT library \"" << this->GetName() << "\" contains:\n"; + for (cmSourceFile* i : badObjLib) { + e << " " << i->GetLocation().GetName() << "\n"; + } + e << "but may contain only sources that compile, header files, and " + "other files that would not affect linking of a normal library."; + this->GlobalGenerator->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, e.str(), this->GetBacktrace()); + } +} + +std::vector<cmGeneratorTarget::AllConfigSource> const& +cmGeneratorTarget::GetAllConfigSources() const +{ + if (this->AllConfigSources.empty()) { + this->ComputeAllConfigSources(); + } + return this->AllConfigSources; +} + +void cmGeneratorTarget::ComputeAllConfigSources() const +{ + std::vector<std::string> configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + std::map<cmSourceFile const*, size_t> index; + + for (size_t ci = 0; ci < configs.size(); ++ci) { + KindedSources const& sources = this->GetKindedSources(configs[ci]); + for (SourceAndKind const& src : sources.Sources) { + auto mi = index.find(src.Source.Value); + if (mi == index.end()) { + AllConfigSource acs; + acs.Source = src.Source.Value; + acs.Kind = src.Kind; + this->AllConfigSources.push_back(std::move(acs)); + std::map<cmSourceFile const*, size_t>::value_type entry( + src.Source.Value, this->AllConfigSources.size() - 1); + mi = index.insert(entry).first; + } + this->AllConfigSources[mi->second].Configs.push_back(ci); + } + } +} + +std::vector<cmGeneratorTarget::AllConfigSource> +cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const +{ + std::vector<AllConfigSource> result; + for (AllConfigSource const& source : this->GetAllConfigSources()) { + if (source.Kind == kind) { + result.push_back(source); + } + } + return result; +} + +std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const +{ + std::set<std::string> languages; + std::vector<AllConfigSource> const& sources = this->GetAllConfigSources(); + for (AllConfigSource const& si : sources) { + std::string const& lang = si.Source->GetOrDetermineLanguage(); + if (!lang.empty()) { + languages.emplace(lang); + } + } + return languages; +}
diff --git a/Source/cmGeneratorTarget_TargetPropertyEntry.cxx b/Source/cmGeneratorTarget_TargetPropertyEntry.cxx new file mode 100644 index 0000000..9b70f63 --- /dev/null +++ b/Source/cmGeneratorTarget_TargetPropertyEntry.cxx
@@ -0,0 +1,189 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt 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 cmLocalGenerator; +class cmake; +struct cmGeneratorExpressionDAGChecker; + +cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem; + +class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryString(BT<std::string> propertyValue, + cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , PropertyValue(std::move(propertyValue)) + { + } + + const std::string& Evaluate(cmLocalGenerator*, const std::string&, + cmGeneratorTarget const*, + cmGeneratorExpressionDAGChecker*, + std::string const&) const override + { + return this->PropertyValue.Value; + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->PropertyValue.Backtrace; + } + std::string const& GetInput() const override + { + return this->PropertyValue.Value; + } + +private: + BT<std::string> PropertyValue; +}; + +class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, + cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , ge(std::move(cge)) + { + } + + const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& language) const override + { + return this->ge->Evaluate(lg, config, headTarget, dagChecker, nullptr, + language); + } + + 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: + const std::unique_ptr<cmCompiledGeneratorExpression> ge; +}; + +class TargetPropertyEntryFileSet + : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryFileSet( + std::vector<std::string> dirs, bool contextSensitiveDirs, + std::unique_ptr<cmCompiledGeneratorExpression> entryCge, + const cmFileSet* fileSet, cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , BaseDirs(std::move(dirs)) + , ContextSensitiveDirs(contextSensitiveDirs) + , EntryCge(std::move(entryCge)) + , FileSet(fileSet) + { + } + + const std::string& Evaluate(cmLocalGenerator* lg, const std::string& config, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& /*lang*/) const override + { + std::map<std::string, std::vector<std::string>> filesPerDir; + this->FileSet->EvaluateFileEntry(this->BaseDirs, filesPerDir, + this->EntryCge, lg, config, 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: + const std::vector<std::string> BaseDirs; + const bool ContextSensitiveDirs; + const std::unique_ptr<cmCompiledGeneratorExpression> EntryCge; + const cmFileSet* 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, + const cmFileSet* fileSet, cmLinkImplItem const& item) +{ + return cm::make_unique<TargetPropertyEntryFileSet>( + std::move(dirs), contextSensitiveDirs, std::move(entryCge), fileSet, item); +} + +cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry( + cmLinkImplItem const& item) + : LinkImplItem(item) +{ +} + +bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition() + const +{ + return false; +}
diff --git a/Source/cmGeneratorTarget_TransitiveProperty.cxx b/Source/cmGeneratorTarget_TransitiveProperty.cxx new file mode 100644 index 0000000..ac929eb --- /dev/null +++ b/Source/cmGeneratorTarget_TransitiveProperty.cxx
@@ -0,0 +1,294 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +/* clang-format off */ +#include "cmGeneratorTarget.h" +/* clang-format on */ + +#include <map> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionContext.h" +#include "cmGeneratorExpressionDAGChecker.h" +#include "cmGeneratorExpressionNode.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmLocalGenerator.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmValue.h" + +namespace { +using UseTo = cmGeneratorTarget::UseTo; +using TransitiveProperty = cmGeneratorTarget::TransitiveProperty; +} + +const std::map<cm::string_view, TransitiveProperty> + cmGeneratorTarget::BuiltinTransitiveProperties = { + { "AUTOMOC_MACRO_NAMES"_s, + { "INTERFACE_AUTOMOC_MACRO_NAMES"_s, UseTo::Compile } }, + { "AUTOUIC_OPTIONS"_s, { "INTERFACE_AUTOUIC_OPTIONS"_s, UseTo::Compile } }, + { "COMPILE_DEFINITIONS"_s, + { "INTERFACE_COMPILE_DEFINITIONS"_s, UseTo::Compile } }, + { "COMPILE_FEATURES"_s, + { "INTERFACE_COMPILE_FEATURES"_s, UseTo::Compile } }, + { "COMPILE_OPTIONS"_s, { "INTERFACE_COMPILE_OPTIONS"_s, UseTo::Compile } }, + { "INCLUDE_DIRECTORIES"_s, + { "INTERFACE_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, + { "LINK_DEPENDS"_s, { "INTERFACE_LINK_DEPENDS"_s, UseTo::Link } }, + { "LINK_DIRECTORIES"_s, { "INTERFACE_LINK_DIRECTORIES"_s, UseTo::Link } }, + { "LINK_OPTIONS"_s, { "INTERFACE_LINK_OPTIONS"_s, UseTo::Link } }, + { "PRECOMPILE_HEADERS"_s, + { "INTERFACE_PRECOMPILE_HEADERS"_s, UseTo::Compile } }, + { "SOURCES"_s, { "INTERFACE_SOURCES"_s, UseTo::Compile } }, + { "SYSTEM_INCLUDE_DIRECTORIES"_s, + { "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"_s, UseTo::Compile } }, + }; + +bool cmGeneratorTarget::MaybeHaveInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + UseTo usage) const +{ + std::string const key = prop + '@' + 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; + + // If this target itself has a non-empty property value, we are done. + maybeInterfaceProp = cmNonempty(this->GetProperty(prop)); + + // Otherwise, recurse to interface dependencies. + if (!maybeInterfaceProp) { + cmGeneratorTarget const* headTarget = + context->HeadTarget ? context->HeadTarget : this; + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(context->Config, headTarget, + usage)) { + 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->MaybeHaveInterfaceProperty(prop, context, usage)) { + maybeInterfaceProp = true; + break; + } + } + } + } + } + } + return i->second; +} + +std::string cmGeneratorTarget::EvaluateInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const +{ + std::string result; + + // If the property does not appear transitively at all, we are done. + if (!this->MaybeHaveInterfaceProperty(prop, context, usage)) { + return result; + } + + // Evaluate $<TARGET_PROPERTY:this,prop> as if it were compiled. This is + // a subset of TargetPropertyNode::Evaluate without stringify/parse steps + // but sufficient for transitive interface properties. + cmGeneratorExpressionDAGChecker dagChecker( + context->Backtrace, this, prop, nullptr, dagCheckerParent, + this->LocalGenerator, context->Config); + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.ReportError( + context, "$<TARGET_PROPERTY:" + this->GetName() + "," + prop + ">"); + return result; + 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 result; + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + cmGeneratorTarget const* headTarget = + context->HeadTarget ? context->HeadTarget : this; + + if (cmValue p = this->GetProperty(prop)) { + result = cmGeneratorExpressionNode::EvaluateDependentExpression( + *p, context->LG, context, headTarget, &dagChecker, this); + } + + if (cmLinkInterfaceLibraries const* iface = + this->GetLinkInterfaceLibraries(context->Config, headTarget, usage)) { + context->HadContextSensitiveCondition = + context->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) { + // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in the + // above property and hand-evaluate it as if it were compiled. + // Create a context as cmCompiledGeneratorExpression::Evaluate does. + cmGeneratorExpressionContext libContext( + context->LG, context->Config, context->Quiet, headTarget, this, + context->EvaluateForBuildsystem, context->Backtrace, + context->Language); + std::string libResult = cmGeneratorExpression::StripEmptyListElements( + lib.Target->EvaluateInterfaceProperty(prop, &libContext, &dagChecker, + usage)); + if (!libResult.empty()) { + if (result.empty()) { + result = std::move(libResult); + } else { + result.reserve(result.size() + 1 + libResult.size()); + result += ";"; + result += libResult; + } + } + context->HadContextSensitiveCondition = + context->HadContextSensitiveCondition || + libContext.HadContextSensitiveCondition; + context->HadHeadSensitiveCondition = + context->HadHeadSensitiveCondition || + libContext.HadHeadSensitiveCondition; + } + } + } + + return result; +} + +cm::optional<cmGeneratorTarget::TransitiveProperty> +cmGeneratorTarget::IsTransitiveProperty(cm::string_view prop, + cmLocalGenerator const* lg, + std::string const& config, + bool evaluatingLinkLibraries) const +{ + cm::optional<TransitiveProperty> result; + static const cm::string_view kINTERFACE_ = "INTERFACE_"_s; + PropertyFor const propertyFor = cmHasPrefix(prop, kINTERFACE_) + ? PropertyFor::Interface + : PropertyFor::Build; + if (propertyFor == PropertyFor::Interface) { + prop = prop.substr(kINTERFACE_.length()); + } + auto i = BuiltinTransitiveProperties.find(prop); + if (i != BuiltinTransitiveProperties.end()) { + result = i->second; + if (result->Usage != cmGeneratorTarget::UseTo::Compile) { + cmPolicies::PolicyStatus cmp0166 = + lg->GetPolicyStatus(cmPolicies::CMP0166); + if ((cmp0166 == cmPolicies::WARN || cmp0166 == cmPolicies::OLD) && + (prop == "LINK_DIRECTORIES"_s || prop == "LINK_DEPENDS"_s || + prop == "LINK_OPTIONS"_s)) { + result->Usage = cmGeneratorTarget::UseTo::Compile; + } + } + } else if (cmHasLiteralPrefix(prop, "COMPILE_DEFINITIONS_")) { + cmPolicies::PolicyStatus cmp0043 = + lg->GetPolicyStatus(cmPolicies::CMP0043); + if (cmp0043 == cmPolicies::WARN || cmp0043 == cmPolicies::OLD) { + result = TransitiveProperty{ "INTERFACE_COMPILE_DEFINITIONS"_s, + UseTo::Compile }; + } + } else if (!evaluatingLinkLibraries) { + // Honor TRANSITIVE_COMPILE_PROPERTIES and TRANSITIVE_LINK_PROPERTIES + // from the link closure when we are not evaluating the closure itself. + CustomTransitiveProperties const& ctp = + this->GetCustomTransitiveProperties(config, propertyFor); + auto ci = ctp.find(std::string(prop)); + if (ci != ctp.end()) { + result = ci->second; + } + } + return result; +} + +cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty( + std::string interfaceName, UseTo usage) + : CustomTransitiveProperty( + cm::make_unique<std::string>(std::move(interfaceName)), usage) +{ +} +cmGeneratorTarget::CustomTransitiveProperty::CustomTransitiveProperty( + std::unique_ptr<std::string> interfaceNameBuf, UseTo usage) + : TransitiveProperty{ *interfaceNameBuf, usage } + , InterfaceNameBuf(std::move(interfaceNameBuf)) +{ +} + +void cmGeneratorTarget::CustomTransitiveProperties::Add(cmValue props, + UseTo usage) +{ + if (props) { + cmList propsList(*props); + for (std::string p : propsList) { + std::string ip; + static const cm::string_view kINTERFACE_ = "INTERFACE_"_s; + if (cmHasPrefix(p, kINTERFACE_)) { + ip = std::move(p); + p = ip.substr(kINTERFACE_.length()); + } else { + ip = cmStrCat(kINTERFACE_, p); + } + this->emplace(std::move(p), + CustomTransitiveProperty(std::move(ip), usage)); + } + } +} + +cmGeneratorTarget::CustomTransitiveProperties const& +cmGeneratorTarget::GetCustomTransitiveProperties(std::string const& config, + PropertyFor propertyFor) const +{ + std::map<std::string, CustomTransitiveProperties>& ctpm = + propertyFor == PropertyFor::Build + ? this->CustomTransitiveBuildPropertiesMap + : this->CustomTransitiveInterfacePropertiesMap; + auto i = ctpm.find(config); + if (i == ctpm.end()) { + CustomTransitiveProperties ctp; + auto addTransitiveProperties = [this, &config, propertyFor, + &ctp](std::string const& tp, UseTo usage) { + // Add transitive properties named by the target itself. + ctp.Add(this->GetProperty(tp), usage); + // Add transitive properties named by the target's link dependencies. + if (propertyFor == PropertyFor::Build) { + for (cmGeneratorTarget const* gt : + this->GetLinkImplementationClosure(config, usage)) { + ctp.Add(gt->GetProperty(tp), usage); + } + } else { + // The set of custom transitive INTERFACE_ properties does not + // depend on the consumer. Use the target as its own head. + cmGeneratorTarget const* headTarget = this; + for (cmGeneratorTarget const* gt : + this->GetLinkInterfaceClosure(config, headTarget, usage)) { + ctp.Add(gt->GetProperty(tp), usage); + } + } + }; + addTransitiveProperties("TRANSITIVE_LINK_PROPERTIES", UseTo::Link); + addTransitiveProperties("TRANSITIVE_COMPILE_PROPERTIES", UseTo::Compile); + i = ctpm.emplace(config, std::move(ctp)).first; + } + return i->second; +}
diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx index 880756d..8e21da0 100644 --- a/Source/cmGetPropertyCommand.cxx +++ b/Source/cmGetPropertyCommand.cxx
@@ -3,6 +3,10 @@ #include "cmGetPropertyCommand.h" #include <cstddef> +#include <functional> + +#include <cm/string_view> +#include <cmext/string_view> #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" @@ -267,6 +271,38 @@ return true; } +namespace GetPropertyCommand { +bool GetSourceFilePropertyGENERATED( + const std::string& name, cmMakefile& mf, + const std::function<bool(bool)>& storeResult) +{ + // Globally set as generated? + // Note: If the given "name" only contains a filename or a relative path + // the file's location is ambiguous. In general, one would expect + // it in the source-directory, because that is where source files + // are located normally. However, generated files are normally + // generated in the build-directory. Therefore, we first check for + // a generated file in the build-directory before we check for a + // generated file in the source-directory. + { + auto file = + cmSystemTools::CollapseFullPath(name, mf.GetCurrentBinaryDirectory()); + if (mf.GetGlobalGenerator()->IsGeneratedFile(file)) { + return storeResult(true); + } + } + { + auto file = + cmSystemTools::CollapseFullPath(name, mf.GetCurrentSourceDirectory()); + if (mf.GetGlobalGenerator()->IsGeneratedFile(file)) { + return storeResult(true); + } + } + // Skip checking the traditional/local property. + return storeResult(false); +} +} + namespace { // Implementation of result storage. @@ -405,12 +441,32 @@ return false; } + // Special handling for GENERATED property. + // Note: Only, if CMP0163 is set to NEW! + if (propertyName == "GENERATED"_s) { + auto& mf = status.GetMakefile(); + auto cmp0163 = directory_makefile.GetPolicyStatus(cmPolicies::CMP0163); + bool const cmp0163new = + cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN; + if (cmp0163new) { + return GetPropertyCommand::GetSourceFilePropertyGENERATED( + name, mf, [infoType, &variable, &mf](bool isGenerated) -> bool { + // Set the value on the original Makefile scope, not the scope of the + // requested directory. + return StoreResult(infoType, mf, variable, + (isGenerated) ? cmValue("1") : cmValue("0")); + }); + } + } + // Get the source file. const std::string source_file_absolute_path = SetPropertyCommand::MakeSourceFilePathAbsoluteIfNeeded( status, name, source_file_paths_should_be_absolute); if (cmSourceFile* sf = directory_makefile.GetOrCreateSource(source_file_absolute_path)) { + // Set the value on the original Makefile scope, not the scope of the + // requested directory. return StoreResult(infoType, status.GetMakefile(), variable, sf->GetPropertyForUser(propertyName)); }
diff --git a/Source/cmGetSourceFilePropertyCommand.cxx b/Source/cmGetSourceFilePropertyCommand.cxx index 40ae112..209030a 100644 --- a/Source/cmGetSourceFilePropertyCommand.cxx +++ b/Source/cmGetSourceFilePropertyCommand.cxx
@@ -2,12 +2,24 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGetSourceFilePropertyCommand.h" +#include <functional> + +#include <cm/string_view> +#include <cmext/string_view> + #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmPolicies.h" #include "cmSetPropertyCommand.h" #include "cmSourceFile.h" #include "cmValue.h" +namespace GetPropertyCommand { +bool GetSourceFilePropertyGENERATED( + const std::string& name, cmMakefile& mf, + const std::function<bool(bool)>& storeResult); +} + bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -23,11 +35,11 @@ bool source_file_target_option_enabled = false; int property_arg_index = 2; - if (args[2] == "DIRECTORY" && args_size == 5) { + if (args[2] == "DIRECTORY"_s && args_size == 5) { property_arg_index = 4; source_file_directory_option_enabled = true; source_file_directories.push_back(args[3]); - } else if (args[2] == "TARGET_DIRECTORY" && args_size == 5) { + } else if (args[2] == "TARGET_DIRECTORY"_s && args_size == 5) { property_arg_index = 4; source_file_target_option_enabled = true; source_file_target_directories.push_back(args[3]); @@ -44,23 +56,44 @@ } std::string const& var = args[0]; + std::string const& propName = args[property_arg_index]; bool source_file_paths_should_be_absolute = source_file_directory_option_enabled || source_file_target_option_enabled; + cmMakefile& directory_makefile = *source_file_directory_makefiles[0]; + + // Special handling for GENERATED property. + // Note: Only, if CMP0163 is set to NEW! + if (propName == "GENERATED"_s) { + auto& mf = status.GetMakefile(); + auto cmp0163 = directory_makefile.GetPolicyStatus(cmPolicies::CMP0163); + bool const cmp0163new = + cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN; + if (cmp0163new) { + return GetPropertyCommand::GetSourceFilePropertyGENERATED( + args[1], mf, [&var, &mf](bool isGenerated) -> bool { + // Set the value on the original Makefile scope, not the scope of the + // requested directory. + mf.AddDefinition(var, (isGenerated) ? cmValue("1") : cmValue("0")); + return true; + }); + } + } + + // Get the source file. std::string const file = SetPropertyCommand::MakeSourceFilePathAbsoluteIfNeeded( status, args[1], source_file_paths_should_be_absolute); - cmMakefile& mf = *source_file_directory_makefiles[0]; - cmSourceFile* sf = mf.GetSource(file); + cmSourceFile* sf = directory_makefile.GetSource(file); // for the location we must create a source file first - if (!sf && args[property_arg_index] == "LOCATION") { - sf = mf.CreateSource(file); + if (!sf && propName == "LOCATION"_s) { + sf = directory_makefile.CreateSource(file); } if (sf) { cmValue prop = nullptr; - if (!args[property_arg_index].empty()) { - prop = sf->GetPropertyForUser(args[property_arg_index]); + if (!propName.empty()) { + prop = sf->GetPropertyForUser(propName); } if (prop) { // Set the value on the original Makefile scope, not the scope of the @@ -70,6 +103,8 @@ } } + // Set the value on the original Makefile scope, not the scope of the + // requested directory. status.GetMakefile().AddDefinition(var, "NOTFOUND"); return true; }
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 940f49d..a92faef 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -593,7 +593,7 @@ for (auto& sg : groupFilesList) { std::ostream* fout; bool useProjectFile = - cmIsOn(this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) || + this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE").IsOn() || this->Makefile->IsOn("CMAKE_GHS_NO_SOURCE_GROUP_FILE"); if (useProjectFile || sg.empty()) { fout = &fout_proj; @@ -764,7 +764,7 @@ bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() { if (cmValue p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) { - return cmIsOn(*p); + return p.IsOn(); } std::vector<cmSourceFile*> sources; this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName);
diff --git a/Source/cmGlobCacheEntry.h b/Source/cmGlobCacheEntry.h new file mode 100644 index 0000000..acc410d --- /dev/null +++ b/Source/cmGlobCacheEntry.h
@@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +struct cmGlobCacheEntry +{ + const bool Recurse; + const bool ListDirectories; + const bool FollowSymlinks; + const std::string Relative; + const std::string Expression; + std::vector<std::string> Files; + + cmGlobCacheEntry(bool recurse, bool listDirectories, bool followSymlinks, + std::string relative, std::string expression, + std::vector<std::string> files) + : Recurse(recurse) + , ListDirectories(listDirectories) + , FollowSymlinks(followSymlinks) + , Relative(std::move(relative)) + , Expression(std::move(expression)) + , Files(std::move(files)) + { + } +};
diff --git a/Source/cmGlobVerificationManager.cxx b/Source/cmGlobVerificationManager.cxx index 89c5b01..73365d2 100644 --- a/Source/cmGlobVerificationManager.cxx +++ b/Source/cmGlobVerificationManager.cxx
@@ -7,6 +7,7 @@ #include "cmsys/FStream.hxx" #include "cmGeneratedFileStream.h" +#include "cmGlobCacheEntry.h" #include "cmListFileCache.h" #include "cmMessageType.h" #include "cmMessenger.h" @@ -145,19 +146,18 @@ } void cmGlobVerificationManager::AddCacheEntry( - const bool recurse, const bool listDirectories, const bool followSymlinks, - const std::string& relative, const std::string& expression, - const std::vector<std::string>& files, const std::string& variable, + const cmGlobCacheEntry& entry, const std::string& variable, const cmListFileBacktrace& backtrace, cmMessenger* messenger) { - CacheEntryKey key = CacheEntryKey(recurse, listDirectories, followSymlinks, - relative, expression); + CacheEntryKey key = + CacheEntryKey(entry.Recurse, entry.ListDirectories, entry.FollowSymlinks, + entry.Relative, entry.Expression); CacheEntryValue& value = this->Cache[key]; if (!value.Initialized) { - value.Files = files; + value.Files = entry.Files; value.Initialized = true; value.Backtraces.emplace_back(variable, backtrace); - } else if (value.Initialized && value.Files != files) { + } else if (value.Initialized && value.Files != entry.Files) { std::ostringstream message; message << std::boolalpha; message << "The glob expression\n "; @@ -176,6 +176,21 @@ } } +std::vector<cmGlobCacheEntry> cmGlobVerificationManager::GetCacheEntries() + const +{ + std::vector<cmGlobCacheEntry> entries; + for (auto const& i : this->Cache) { + CacheEntryKey k = std::get<0>(i); + CacheEntryValue v = std::get<1>(i); + if (v.Initialized) { + entries.emplace_back(k.Recurse, k.ListDirectories, k.FollowSymlinks, + k.Relative, k.Expression, v.Files); + } + } + return entries; +} + void cmGlobVerificationManager::Reset() { this->Cache.clear();
diff --git a/Source/cmGlobVerificationManager.h b/Source/cmGlobVerificationManager.h index 52d71aa..e9c519b 100644 --- a/Source/cmGlobVerificationManager.h +++ b/Source/cmGlobVerificationManager.h
@@ -13,6 +13,7 @@ #include "cmListFileCache.h" class cmMessenger; +struct cmGlobCacheEntry; /** \class cmGlobVerificationManager * \brief Class for expressing build-time dependencies on glob expressions. @@ -28,13 +29,13 @@ bool SaveVerificationScript(const std::string& path, cmMessenger* messenger); //! Add an entry into the glob cache - void AddCacheEntry(bool recurse, bool listDirectories, bool followSymlinks, - const std::string& relative, - const std::string& expression, - const std::vector<std::string>& files, + void AddCacheEntry(const cmGlobCacheEntry& entry, const std::string& variable, const cmListFileBacktrace& bt, cmMessenger* messenger); + //! Get all cache entries + std::vector<cmGlobCacheEntry> GetCacheEntries() const; + //! Clear the glob cache for state reset. void Reset();
diff --git a/Source/cmGlobalBorlandMakefileGenerator.cxx b/Source/cmGlobalBorlandMakefileGenerator.cxx index 2fd7f8a..6eabe9c 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.cxx +++ b/Source/cmGlobalBorlandMakefileGenerator.cxx
@@ -24,6 +24,7 @@ this->ToolSupportsColor = true; this->UseLinkScript = false; cm->GetState()->SetWindowsShell(true); + cm->GetState()->SetBorlandMake(true); this->IncludeDirective = "!include"; this->DefineWindowsNULL = true; this->PassMakeflags = true;
diff --git a/Source/cmGlobalCommonGenerator.cxx b/Source/cmGlobalCommonGenerator.cxx index 513e3bf..b1b96d0 100644 --- a/Source/cmGlobalCommonGenerator.cxx +++ b/Source/cmGlobalCommonGenerator.cxx
@@ -12,8 +12,8 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmLocalCommonGenerator.h" #include "cmLocalGenerator.h" -#include "cmMakefile.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" @@ -38,8 +38,8 @@ lg->GetStateSnapshot().GetDirectory().GetCurrentBinary(); DirectoryTarget& dirTarget = dirTargets[currentBinaryDir]; dirTarget.LG = lg.get(); - const std::vector<std::string>& configs = - lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + std::vector<std::string> const& configs = + static_cast<cmLocalCommonGenerator const*>(lg.get())->GetConfigNames(); // The directory-level rule should depend on the target-level rules // for all targets in the directory.
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index b2ee306..019271b 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx
@@ -4,13 +4,11 @@ #include <algorithm> #include <cassert> -#include <chrono> #include <cstdio> #include <cstdlib> #include <cstring> #include <functional> #include <initializer_list> -#include <iomanip> #include <iterator> #include <sstream> #include <type_traits> @@ -224,7 +222,7 @@ if (cmIsOff(makeProgram)) { cmValue makeProgramCSTR = this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); - if (cmIsOff(makeProgramCSTR)) { + if (makeProgramCSTR.IsOff()) { makeProgram = makeDefault; } else { makeProgram = *makeProgramCSTR; @@ -334,7 +332,7 @@ for (const auto& localGen : this->LocalGenerators) { for (const auto& target : localGen->GetGeneratorTargets()) { if (!target->CanCompileSources() || - cmIsOn(target->GetProperty("ghs_integrity_app"))) { + target->GetProperty("ghs_integrity_app").IsOn()) { continue; } @@ -406,7 +404,7 @@ for (const auto& generator : this->LocalGenerators) { for (const auto& target : generator->GetGeneratorTargets()) { if (!target->CanCompileSources() || - cmIsOn(target->GetProperty("ghs_integrity_app"))) { + target->GetProperty("ghs_integrity_app").IsOn()) { continue; } @@ -450,13 +448,13 @@ "all generators must specify this->FindMakeProgramFile"); return false; } - if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { + if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) { std::string setMakeProgram = mf->GetModulesFile(this->FindMakeProgramFile); if (!setMakeProgram.empty()) { mf->ReadListFile(setMakeProgram); } } - if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { + if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) { std::ostringstream err; err << "CMake was unable to find a build program corresponding to \"" << this->GetName() << "\". CMAKE_MAKE_PROGRAM is not set. You " @@ -1351,8 +1349,6 @@ void cmGlobalGenerator::Configure() { - auto startTime = std::chrono::steady_clock::now(); - this->FirstTimeProgress = 0.0f; this->ClearGeneratorMembers(); this->NextDeferId = 0; @@ -1407,21 +1403,6 @@ this->GetCMakeInstance()->AddCacheEntry( "CMAKE_NUMBER_OF_MAKEFILES", std::to_string(this->Makefiles.size()), "number of local generators", cmStateEnums::INTERNAL); - - auto endTime = std::chrono::steady_clock::now(); - - if (this->CMakeInstance->GetWorkingMode() == cmake::NORMAL_MODE) { - std::ostringstream msg; - if (cmSystemTools::GetErrorOccurredFlag()) { - msg << "Configuring incomplete, errors occurred!"; - } else { - auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( - endTime - startTime); - msg << "Configuring done (" << std::fixed << std::setprecision(1) - << ms.count() / 1000.0L << "s)"; - } - this->CMakeInstance->UpdateProgress(msg.str(), -1); - } } void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes) @@ -1581,6 +1562,29 @@ } #endif + // Perform up-front computation in order to handle errors (such as unknown + // features) at this point. While processing the compile features we also + // calculate and cache the language standard required by the compile + // features. + // + // Synthetic targets performed this inside of + // `cmLocalGenerator::DiscoverSyntheticTargets` + for (const auto& localGen : this->LocalGenerators) { + if (!localGen->ComputeTargetCompileFeatures()) { + return false; + } + } + + // We now have all targets set up and std levels constructed. Add + // `__CMAKE::CXX*` targets as link dependencies to all targets which need + // them. + // + // Synthetic targets performed this inside of + // `cmLocalGenerator::DiscoverSyntheticTargets` + if (!this->ApplyCXXStdTargets()) { + return false; + } + // Iterate through all targets and set up C++20 module targets. // Create target templates for each imported target with C++20 modules. // INTERFACE library with BMI-generating rules and a collation step? @@ -1588,6 +1592,9 @@ // Make `add_dependencies(imported_target // $<$<TARGET_NAME_IF_EXISTS:uses_imported>:synth1> // $<$<TARGET_NAME_IF_EXISTS:other_uses_imported>:synth2>)` + // + // Note that synthetic target creation performs the above marked + // steps on the created targets. if (!this->DiscoverSyntheticTargets()) { return false; } @@ -1597,16 +1604,6 @@ localGen->AddHelperCommands(); } - // Perform up-front computation in order to handle errors (such as unknown - // features) at this point. While processing the compile features we also - // calculate and cache the language standard required by the compile - // features. - for (const auto& localGen : this->LocalGenerators) { - if (!localGen->ComputeTargetCompileFeatures()) { - return false; - } - } - // Add automatically generated sources (e.g. unity build). // Add unity sources after computing compile features. Unity sources do // not change the set of languages or features, but we need to know them @@ -1667,8 +1664,6 @@ void cmGlobalGenerator::Generate() { - auto startTime = std::chrono::steady_clock::now(); - // Create a map from local generator to the complete set of targets // it builds by default. this->InitializeProgressMarks(); @@ -1759,14 +1754,6 @@ this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); } - - auto endTime = std::chrono::steady_clock::now(); - auto ms = - std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime); - std::ostringstream msg; - msg << "Generating done (" << std::fixed << std::setprecision(1) - << ms.count() / 1000.0L << "s)"; - this->CMakeInstance->UpdateProgress(msg.str(), -1); } bool cmGlobalGenerator::ComputeTargetDepends() @@ -1824,6 +1811,19 @@ entry->second = index++; } +bool cmGlobalGenerator::ApplyCXXStdTargets() +{ + for (auto const& gen : this->LocalGenerators) { + for (auto const& tgt : gen->GetGeneratorTargets()) { + if (!tgt->ApplyCXXStdTargets()) { + return false; + } + } + } + + return true; +} + bool cmGlobalGenerator::DiscoverSyntheticTargets() { cmSyntheticTargetCache cache; @@ -2823,7 +2823,7 @@ } else { cmValue noPackageAll = mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY"); - if (cmIsOff(noPackageAll)) { + if (noPackageAll.IsOff()) { gti.Depends.emplace_back(this->GetAllTargetName()); } } @@ -2891,7 +2891,7 @@ // by default. Enable it only if CMAKE_SKIP_TEST_ALL_DEPENDENCY is // explicitly set to OFF. if (cmValue noall = mf->GetDefinition("CMAKE_SKIP_TEST_ALL_DEPENDENCY")) { - if (cmIsOff(noall)) { + if (noall.IsOff()) { gti.Depends.emplace_back(this->GetAllTargetName()); } } @@ -3015,7 +3015,7 @@ gti.Depends.emplace_back(this->GetPreinstallTargetName()); } else { cmValue noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"); - if (cmIsOff(noall)) { + if (noall.IsOff()) { gti.Depends.emplace_back(this->GetAllTargetName()); } } @@ -3048,7 +3048,9 @@ if (const char* install_local = this->GetInstallLocalTargetName()) { gti.Name = install_local; gti.Message = "Installing only the local directory..."; - gti.UsesTerminal = true; + gti.UsesTerminal = + !this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool( + "INSTALL_PARALLEL"); gti.CommandLines.clear(); cmCustomCommandLine localCmdLine = singleLine; @@ -3097,7 +3099,7 @@ // If this property is defined, let the setter turn this on or off. if (prop) { - return cmIsOn(*prop); + return prop.IsOn(); } // If CMP0143 is NEW `treat` "USE_FOLDERS" as ON. Otherwise `treat` it as OFF
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index ba39768..1ca02d9 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h
@@ -674,6 +674,7 @@ virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; + bool ApplyCXXStdTargets(); bool DiscoverSyntheticTargets(); bool AddHeaderSetVerification();
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 4ba53ec..37bcc2c 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -124,7 +124,7 @@ { /* set primary target */ cmValue t = mf->GetDefinition("GHS_PRIMARY_TARGET"); - if (cmIsOff(t)) { + if (t.IsOff()) { /* Use the value from `-A` or use `arm` */ std::string arch = "arm"; if (!cmIsOff(p)) { @@ -297,18 +297,18 @@ // Specify BSP option if supplied by user // -- not all platforms require this entry in the project file cmValue bspName = root->GetMakefile()->GetDefinition("GHS_BSP_NAME"); - if (!cmIsOff(bspName)) { + if (!bspName.IsOff()) { fout << " -bsp " << *bspName << '\n'; } // Specify OS DIR if supplied by user // -- not all platforms require this entry in the project file cmValue osDir = root->GetMakefile()->GetDefinition("GHS_OS_DIR"); - if (!cmIsOff(osDir)) { + if (!osDir.IsOff()) { cmValue osDirOption = root->GetMakefile()->GetDefinition("GHS_OS_DIR_OPTION"); fout << " "; - if (cmIsOff(osDirOption)) { + if (osDirOption.IsOff()) { fout << ""; } else { fout << *osDirOption;
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 250546f..96c8f25 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -603,6 +603,13 @@ "Generates build.ninja files." }; } +std::vector<std::string> const& cmGlobalNinjaGenerator::GetConfigNames() const +{ + return static_cast<cmLocalNinjaGenerator const*>( + this->LocalGenerators.front().get()) + ->GetConfigNames(); +} + // Implemented in all cmGlobaleGenerator sub-classes. // Used in: // Source/cmLocalGenerator.cxx @@ -655,8 +662,7 @@ if (cmSystemTools::GetErrorOccurredFlag()) { this->RulesFileStream->setstate(std::ios::failbit); - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config : this->GetConfigNames()) { this->GetImplFileStream(config)->setstate(std::ios::failbit); this->GetConfigFileStream(config)->setstate(std::ios::failbit); } @@ -1491,9 +1497,7 @@ for (std::string const& output : outputs) { this->TargetAliases[output].GeneratorTarget = nullptr; this->DefaultTargetAliases[output].GeneratorTarget = nullptr; - for (const std::string& config2 : - this->Makefiles.front()->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config2 : this->GetConfigNames()) { this->Configs[config2].TargetAliases[output].GeneratorTarget = nullptr; } } @@ -1567,8 +1571,7 @@ } if (this->IsMultiConfig()) { - for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config : this->GetConfigNames()) { for (auto const& ta : this->Configs[config].TargetAliases) { // Don't write ambiguous aliases. if (!ta.second.GeneratorTarget) { @@ -1630,8 +1633,7 @@ std::string const& currentBinaryDir = it.first; DirectoryTarget const& dt = it.second; std::vector<std::string> configs = - dt.LG->GetMakefile()->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig); + static_cast<cmLocalNinjaGenerator const*>(dt.LG)->GetConfigNames(); // Setup target cmNinjaDeps configDeps; @@ -1827,14 +1829,28 @@ this->WriteTargetClean(os); this->WriteTargetHelp(os); - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config : this->GetConfigNames()) { this->WriteTargetDefault(*this->GetConfigFileStream(config)); } if (!this->DefaultFileConfig.empty()) { this->WriteTargetDefault(*this->GetDefaultFileStream()); } + + if (this->InstallTargetEnabled && + this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool( + "INSTALL_PARALLEL") && + !this->Makefiles[0]->IsOn("CMAKE_SKIP_INSTALL_RULES")) { + cmNinjaBuild build("phony"); + build.Comment = "Install every subdirectory in parallel"; + build.Outputs.emplace_back(this->GetInstallParallelTargetName()); + for (auto const& mf : this->Makefiles) { + build.ExplicitDeps.emplace_back( + this->ConvertToNinjaPath(cmStrCat(mf->GetCurrentBinaryDirectory(), "/", + this->GetInstallLocalTargetName()))); + } + WriteBuild(os, build); + } } void cmGlobalNinjaGenerator::WriteTargetDefault(std::ostream& os) @@ -2010,8 +2026,7 @@ std::string cleanScriptRel = "CMakeFiles/clean_additional.cmake"; std::string cleanScriptAbs = cmStrCat(lgr->GetBinaryDirectory(), '/', cleanScriptRel); - std::vector<std::string> configs = - this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + std::vector<std::string> const& configs = this->GetConfigNames(); // Check if there are additional files to clean bool empty = true; @@ -2106,16 +2121,13 @@ WriteRule(*this->RulesFileStream, rule); } - auto const configs = this->Makefiles.front()->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig); - // Write build { cmNinjaBuild build("CLEAN"); build.Comment = "Clean all the built files."; build.Outputs.emplace_back(); - for (auto const& config : configs) { + for (std::string const& config : this->GetConfigNames()) { build.Outputs.front() = this->BuildAlias( this->NinjaOutputPath(this->GetCleanTargetName()), config); if (this->IsMultiConfig()) { @@ -2130,7 +2142,7 @@ this->NinjaOutputPath(this->GetAdditionalCleanTargetName()), config)); } - for (auto const& fileConfig : configs) { + for (std::string const& fileConfig : this->GetConfigNames()) { if (fileConfig != config && !this->EnableCrossConfigBuild()) { continue; } @@ -2166,7 +2178,7 @@ byproducts.emplace_back(GetByproductsForCleanTargetName()); build.Variables["TARGETS"] = cmJoin(byproducts, " "); - for (auto const& fileConfig : configs) { + for (std::string const& fileConfig : this->GetConfigNames()) { build.Variables["FILE_ARG"] = cmStrCat( "-f ", this->NinjaOutputPath( @@ -2182,7 +2194,7 @@ this->NinjaOutputPath(this->GetCleanTargetName())); build.ExplicitDeps.emplace_back(); - for (auto const& config : configs) { + for (std::string const& config : this->GetConfigNames()) { build.ExplicitDeps.front() = this->BuildAlias( this->NinjaOutputPath(this->GetCleanTargetName()), config); this->WriteBuild(*this->GetConfigFileStream(config), build); @@ -2207,7 +2219,7 @@ build.ExplicitDeps = this->ByproductsForCleanTarget; this->WriteBuild(os, build); - for (auto const& config : configs) { + for (std::string const& config : this->GetConfigNames()) { build.Outputs.front() = this->BuildAlias( this->ConvertToNinjaPath(GetByproductsForCleanTargetName()), config); build.ExplicitDeps = this->Configs[config].ByproductsForCleanTarget; @@ -3104,8 +3116,7 @@ << "# This file contains build statements common to all " "configurations.\n\n"; - auto const& configs = - this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + std::vector<std::string> const& configs = this->GetConfigNames(); return std::all_of( configs.begin(), configs.end(), [this](std::string const& config) -> bool { // Open impl file. @@ -3148,8 +3159,7 @@ this->DefaultFileStream.reset(); } // No error if it wasn't open - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config : this->GetConfigNames()) { if (this->ImplFileStreams[config]) { this->ImplFileStreams[config].reset(); } else { @@ -3191,8 +3201,7 @@ void cmGlobalNinjaMultiGenerator::AddRebuildManifestOutputs( cmNinjaDeps& outputs) const { - for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs( - cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config : this->GetConfigNames()) { outputs.push_back(this->NinjaOutputPath(GetNinjaImplFilename(config))); outputs.push_back(this->NinjaOutputPath(GetNinjaConfigFilename(config))); } @@ -3204,18 +3213,15 @@ void cmGlobalNinjaMultiGenerator::GetQtAutoGenConfigs( std::vector<std::string>& configs) const { - auto allConfigs = - this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + std::vector<std::string> const& allConfigs = this->GetConfigNames(); configs.insert(configs.end(), cm::cbegin(allConfigs), cm::cend(allConfigs)); } bool cmGlobalNinjaMultiGenerator::InspectConfigTypeVariables() { - cmList configsList{ this->Makefiles.front()->GetDefinition( - "CMAKE_CONFIGURATION_TYPES") }; - if (configsList.empty()) { - configsList.emplace_back(); - } + std::vector<std::string> configsList = + this->Makefiles.front()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); std::set<std::string> configs(configsList.cbegin(), configsList.cend()); this->DefaultFileConfig =
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 64eed4d..6ad38fb 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h
@@ -218,6 +218,10 @@ { return "install/strip"; } + const char* GetInstallParallelTargetName() const + { + return "install/parallel"; + } const char* GetTestTargetName() const override { return "test"; } const char* GetPackageTargetName() const override { return "package"; } const char* GetPackageSourceTargetName() const override @@ -481,6 +485,8 @@ bool CheckCxxModuleSupport(CxxModuleSupportQuery query) override; protected: + std::vector<std::string> const& GetConfigNames() const; + void Generate() override; bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const override { return true; }
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 30206b5..56748a5 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -698,7 +698,7 @@ if (cmValue tgtMsg = this->GetCMakeInstance()->GetState()->GetGlobalProperty( "TARGET_MESSAGES")) { - targetMessages = cmIsOn(*tgtMsg); + targetMessages = tgtMsg.IsOn(); } if (targetMessages) {
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index f24610d..51d6d8a 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -516,7 +516,7 @@ this->DefaultPlatformToolset = this->SelectWindowsCEToolset(); - if (this->GetVersion() == cmGlobalVisualStudioGenerator::VSVersion::VS12) { + if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS12) { // VS 12 .NET CF defaults to .NET framework 3.9 for Windows CE. this->DefaultTargetFrameworkVersion = "v3.9"; this->DefaultTargetFrameworkIdentifier = "WindowsEmbeddedCompact"; @@ -1273,12 +1273,6 @@ const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const { switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: - return "4.0"; - - // in Visual Studio 2013 they detached the MSBuild tools version - // from the .Net Framework version and instead made it have it's own - // version number case cmGlobalVisualStudioGenerator::VSVersion::VS12: return "12.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS14:
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index 68b4d1a..35b69af 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -390,7 +390,7 @@ "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM")) { // If the value is some off/false value, then there is NO maximum set. - if (cmIsOff(value)) { + if (value.IsOff()) { return std::string(); } // If the value is something else, trust that it is a valid SDK value.
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 9739a09..7e04b9f 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -310,26 +310,6 @@ GetSLNFile(this->LocalGenerators[0].get())); } - if (this->Version == VSVersion::VS9 && - !this->CMakeInstance->GetIsInTryCompile()) { - std::string cmakeWarnVS9; - if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue( - "CMAKE_WARN_VS9")) { - this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS9"); - cmakeWarnVS9 = *cached; - } else { - cmSystemTools::GetEnv("CMAKE_WARN_VS9", cmakeWarnVS9); - } - if (cmakeWarnVS9.empty() || !cmIsOff(cmakeWarnVS9)) { - this->CMakeInstance->IssueMessage( - MessageType::WARNING, - "The \"Visual Studio 9 2008\" generator is deprecated " - "and will be removed in a future version of CMake." - "\n" - "Add CMAKE_WARN_VS9=OFF to the cache to disable this warning."); - } - } - if (this->Version == VSVersion::VS12 && !this->CMakeInstance->GetIsInTryCompile()) { std::string cmakeWarnVS12; @@ -403,8 +383,7 @@ std::string mapping; // On VS 19 and above, always map .NET SDK projects to "Any CPU". - if (target->IsDotNetSdkTarget() && - this->GetVersion() >= VSVersion::VS16 && + if (target->IsDotNetSdkTarget() && this->Version >= VSVersion::VS16 && !cmGlobalVisualStudio7Generator::IsReservedTarget( target->GetName())) { mapping = "Any CPU"; @@ -740,7 +719,7 @@ } // inspect EXCLUDE_FROM_DEFAULT_BUILD[_<CONFIG>] properties for (std::string const& i : configs) { - if (cmIsOff(target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i))) { + if (target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i).IsOff()) { activeConfigs.insert(i); } }
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 0d357ad..b1fba8f 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -225,11 +225,6 @@ return "generate.stamp.list"; } -void cmGlobalVisualStudio8Generator::Configure() -{ - this->cmGlobalVisualStudio7Generator::Configure(); -} - bool cmGlobalVisualStudio8Generator::UseFolderProperty() const { // NOLINTNEXTLINE(bugprone-parent-virtual-call)
diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index cb0ea76..931de40 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h
@@ -40,12 +40,6 @@ cm::optional<std::string> const& GetTargetFrameworkIdentifier() const; cm::optional<std::string> const& GetTargetFrameworkTargetsVersion() const; - /** - * Override Configure and Generate to add the build-system check - * target. - */ - void Configure() override; - /** Return true if the target project file should have the option LinkLibraryDependencies and link to .sln dependencies. */ bool NeedLinkLibraryDependencies(cmGeneratorTarget* target) override;
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx deleted file mode 100644 index de2153d..0000000 --- a/Source/cmGlobalVisualStudio9Generator.cxx +++ /dev/null
@@ -1,163 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmGlobalVisualStudio9Generator.h" - -#include <cstring> -#include <utility> -#include <vector> - -#include "cmGlobalGenerator.h" -#include "cmGlobalGeneratorFactory.h" -#include "cmGlobalVisualStudioGenerator.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" -#include "cmVisualStudioWCEPlatformParser.h" - -class cmake; - -static const char vs9generatorName[] = "Visual Studio 9 2008"; - -class cmGlobalVisualStudio9Generator::Factory : public cmGlobalGeneratorFactory -{ -public: - std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, bool allowArch, cmake* cm) const override - { - if (strncmp(name.c_str(), vs9generatorName, - sizeof(vs9generatorName) - 1) != 0) { - return std::unique_ptr<cmGlobalGenerator>(); - } - - const char* p = name.c_str() + sizeof(vs9generatorName) - 1; - if (p[0] == '\0') { - return std::unique_ptr<cmGlobalGenerator>( - new cmGlobalVisualStudio9Generator(cm, name, "")); - } - - if (!allowArch || p[0] != ' ') { - return std::unique_ptr<cmGlobalGenerator>(); - } - - ++p; - - if (!strcmp(p, "IA64")) { - return std::unique_ptr<cmGlobalGenerator>( - new cmGlobalVisualStudio9Generator(cm, name, "Itanium")); - } - - if (!strcmp(p, "Win64")) { - return std::unique_ptr<cmGlobalGenerator>( - new cmGlobalVisualStudio9Generator(cm, name, "x64")); - } - - cmVisualStudioWCEPlatformParser parser(p); - parser.ParseVersion("9.0"); - if (!parser.Found()) { - return std::unique_ptr<cmGlobalGenerator>(); - } - - auto ret = std::unique_ptr<cmGlobalVisualStudio9Generator>( - new cmGlobalVisualStudio9Generator(cm, name, p)); - ret->WindowsCEVersion = parser.GetOSVersion(); - return std::unique_ptr<cmGlobalGenerator>(std::move(ret)); - } - - cmDocumentationEntry GetDocumentation() const override - { - return { cmStrCat(vs9generatorName, " [arch]"), - "Deprecated. Generates Visual Studio 2008 project files. " - "Optional [arch] can be \"Win64\" or \"IA64\"." }; - } - - std::vector<std::string> GetGeneratorNames() const override - { - std::vector<std::string> names; - names.emplace_back(vs9generatorName); - return names; - } - - std::vector<std::string> GetGeneratorNamesWithPlatform() const override - { - std::vector<std::string> names; - names.emplace_back(cmStrCat(vs9generatorName, " Win64")); - names.emplace_back(cmStrCat(vs9generatorName, " IA64")); - cmVisualStudioWCEPlatformParser parser; - parser.ParseVersion("9.0"); - const std::vector<std::string>& availablePlatforms = - parser.GetAvailablePlatforms(); - for (std::string const& i : availablePlatforms) { - names.emplace_back(cmStrCat("Visual Studio 9 2008 ", i)); - } - return names; - } - - bool SupportsToolset() const override { return false; } - bool SupportsPlatform() const override { return true; } - - std::vector<std::string> GetKnownPlatforms() const override - { - std::vector<std::string> platforms; - platforms.emplace_back("x64"); - platforms.emplace_back("Win32"); - platforms.emplace_back("Itanium"); - cmVisualStudioWCEPlatformParser parser; - parser.ParseVersion("9.0"); - const std::vector<std::string>& availablePlatforms = - parser.GetAvailablePlatforms(); - for (std::string const& i : availablePlatforms) { - platforms.emplace_back(i); - } - return platforms; - } - - std::string GetDefaultPlatformName() const override { return "Win32"; } -}; - -std::unique_ptr<cmGlobalGeneratorFactory> -cmGlobalVisualStudio9Generator::NewFactory() -{ - return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory); -} - -cmGlobalVisualStudio9Generator::cmGlobalVisualStudio9Generator( - cmake* cm, const std::string& name, - std::string const& platformInGeneratorName) - : cmGlobalVisualStudio8Generator(cm, name, platformInGeneratorName) -{ - this->Version = VSVersion::VS9; - std::string vc9Express; - this->ExpressEdition = cmSystemTools::ReadRegistryValue( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\9.0\\Setup\\VC;" - "ProductDir", - vc9Express, cmSystemTools::KeyWOW64_32); -} - -std::string cmGlobalVisualStudio9Generator::GetUserMacrosDirectory() -{ - std::string base; - std::string path; - - // base begins with the VisualStudioProjectsLocation reg value... - if (cmSystemTools::ReadRegistryValue( - "HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\9.0;" - "VisualStudioProjectsLocation", - base)) { - cmSystemTools::ConvertToUnixSlashes(base); - - // 9.0 macros folder: - path = cmStrCat(base, "/VSMacros80"); - // *NOT* a typo; right now in Visual Studio 2008 beta the macros - // folder is VSMacros80... They may change it to 90 before final - // release of 2008 or they may not... we'll have to keep our eyes - // on it - } - - // path is (correctly) still empty if we did not read the base value from - // the Registry value - return path; -} - -std::string cmGlobalVisualStudio9Generator::GetUserMacrosRegKeyBase() -{ - return R"(Software\Microsoft\VisualStudio\9.0\vsmacros)"; -}
diff --git a/Source/cmGlobalVisualStudio9Generator.h b/Source/cmGlobalVisualStudio9Generator.h deleted file mode 100644 index 1c93d49..0000000 --- a/Source/cmGlobalVisualStudio9Generator.h +++ /dev/null
@@ -1,43 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include <memory> -#include <string> - -#include "cmGlobalVisualStudio8Generator.h" - -class cmGlobalGeneratorFactory; -class cmake; - -/** \class cmGlobalVisualStudio9Generator - * \brief Write a Unix makefiles. - * - * cmGlobalVisualStudio9Generator manages UNIX build process for a tree - */ -class cmGlobalVisualStudio9Generator : public cmGlobalVisualStudio8Generator -{ -public: - static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory(); - - /** - * Where does this version of Visual Studio look for macros for the - * current user? Returns the empty string if this version of Visual - * Studio does not implement support for VB macros. - */ - std::string GetUserMacrosDirectory() override; - - /** - * What is the reg key path to "vsmacros" for this version of Visual - * Studio? - */ - std::string GetUserMacrosRegKeyBase() override; - -protected: - cmGlobalVisualStudio9Generator(cmake* cm, const std::string& name, - std::string const& platformInGeneratorName); - -private: - class Factory; - friend class Factory; -};
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 5305fec..f96a84c 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -104,8 +104,6 @@ const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const { switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: - return "9.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS12: return "12.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -127,10 +125,6 @@ fout << '\n'; switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: - fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n"; - fout << "# Visual Studio 2008\n"; - break; case cmGlobalVisualStudioGenerator::VSVersion::VS12: fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; if (this->ExpressEdition) { @@ -491,7 +485,7 @@ // Visual Studio generators know how to lookup their build tool // directly instead of needing a helper module to do it, so we // do not actually need to put CMAKE_MAKE_PROGRAM into the cache. - if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { + if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) { mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram()); } return true;
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 76713fa..4746f13 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -34,7 +34,6 @@ /** Known versions of Visual Studio. */ enum class VSVersion : uint16_t { - VS9 = 90, VS12 = 120, /* VS13 = 130 was skipped */ VS14 = 140,
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index d816d7b..b4683aa 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -125,8 +125,6 @@ cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: - return 9; case cmGlobalVisualStudioGenerator::VSVersion::VS12: return 12; case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -145,8 +143,6 @@ cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: - return "v90"; case cmGlobalVisualStudioGenerator::VSVersion::VS12: return "v120"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -165,8 +161,6 @@ cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: - return "9"; case cmGlobalVisualStudioGenerator::VSVersion::VS12: return "12"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -185,7 +179,6 @@ cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: case cmGlobalVisualStudioGenerator::VSVersion::VS12: return ""; case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -485,7 +478,6 @@ { std::string genName; switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: case cmGlobalVisualStudioGenerator::VSVersion::VS12: case cmGlobalVisualStudioGenerator::VSVersion::VS14: break; @@ -752,7 +744,6 @@ const { switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS9: case cmGlobalVisualStudioGenerator::VSVersion::VS12: return ""; case cmGlobalVisualStudioGenerator::VSVersion::VS14:
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 98d77c6..aa948a5 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -264,12 +264,33 @@ #endif } +namespace { +std::string ConvertToMakefilePath(std::string const& path) +{ + std::string result; + result.reserve(path.size()); + for (char c : path) { + switch (c) { + case '\\': + case ' ': + case '#': + result.push_back('\\'); + CM_FALLTHROUGH; + default: + result.push_back(c); + break; + } + } + return result; +} +} + bool cmGlobalXCodeGenerator::FindMakeProgram(cmMakefile* mf) { // The Xcode generator knows how to lookup its build tool // directly instead of needing a helper module to do it, so we // do not actually need to put CMAKE_MAKE_PROGRAM into the cache. - if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { + if (mf->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) { mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetXcodeBuildCommand()); } return true; @@ -653,7 +674,7 @@ if (regenerate && isGenerateProject) { this->CreateReRunCMakeFile(root, gens); std::string file = - this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile); + cmSystemTools::ConvertToOutputPath(this->CurrentReRunCMakeMakefile); cmSystemTools::ReplaceString(file, "\\ ", " "); cc = cm::make_unique<cmCustomCommand>(); cc->SetCommandLines(cmMakeSingleCommandLine({ "make", "-f", file })); @@ -732,7 +753,7 @@ for (const auto& lfile : lfiles) { makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard " - << this->ConvertToRelativeForMake(lfile) << "))\n"; + << ConvertToMakefilePath(lfile) << "))\n"; } makefileStream << '\n'; @@ -743,25 +764,25 @@ makefileStream << ".NOTPARALLEL:\n\n" ".PHONY: all VERIFY_GLOBS\n\n" "all: VERIFY_GLOBS " - << this->ConvertToRelativeForMake(checkCache) + << ConvertToMakefilePath(checkCache) << "\n\n" "VERIFY_GLOBS:\n" "\t" - << this->ConvertToRelativeForMake( - cmSystemTools::GetCMakeCommand()) + << ConvertToMakefilePath(cmSystemTools::GetCMakeCommand()) << " -P " - << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript()) + << ConvertToMakefilePath(cm->GetGlobVerifyScript()) << "\n\n"; } - makefileStream << this->ConvertToRelativeForMake(checkCache) - << ": $(TARGETS)\n"; - makefileStream - << '\t' << this->ConvertToRelativeForMake(cmSystemTools::GetCMakeCommand()) - << " -S" << this->ConvertToRelativeForMake(root->GetSourceDirectory()) - << " -B" << this->ConvertToRelativeForMake(root->GetBinaryDirectory()) - << (cm->GetIgnoreWarningAsError() ? " --compile-no-warning-as-error" : "") - << '\n'; + makefileStream << ConvertToMakefilePath(checkCache) << ": $(TARGETS)\n"; + makefileStream << '\t' + << ConvertToMakefilePath(cmSystemTools::GetCMakeCommand()) + << " -S" << ConvertToMakefilePath(root->GetSourceDirectory()) + << " -B" << ConvertToMakefilePath(root->GetBinaryDirectory()) + << (cm->GetIgnoreWarningAsError() + ? " --compile-no-warning-as-error" + : "") + << '\n'; } static bool objectIdLessThan(const std::unique_ptr<cmXCodeObject>& l, @@ -1095,7 +1116,7 @@ bool& keepLastKnownFileType, const std::vector<std::string>& enabled_langs) { std::string ext = cmSystemTools::LowerCase(_ext); - std::string sourcecode = "sourcecode"; + std::string sourcecode = "default"; if (ext == "o"_s) { keepLastKnownFileType = true; @@ -1110,42 +1131,42 @@ sourcecode = "file.storyboard"; // NOLINTNEXTLINE(bugprone-branch-clone) } else if (ext == "mm"_s && !cm::contains(enabled_langs, "OBJCXX")) { - sourcecode += ".cpp.objcpp"; + sourcecode = "sourcecode.cpp.objcpp"; // NOLINTNEXTLINE(bugprone-branch-clone) } else if (ext == "m"_s && !cm::contains(enabled_langs, "OBJC")) { - sourcecode += ".c.objc"; + sourcecode = "sourcecode.c.objc"; } else if (ext == "swift"_s) { - sourcecode += ".swift"; + sourcecode = "sourcecode.swift"; } else if (ext == "plist"_s) { - sourcecode += ".text.plist"; + sourcecode = "sourcecode.text.plist"; } else if (ext == "h"_s) { - sourcecode += ".c.h"; + sourcecode = "sourcecode.c.h"; } else if (ext == "hxx"_s || ext == "hpp"_s || ext == "txx"_s || ext == "pch"_s || ext == "hh"_s || ext == "inl"_s) { - sourcecode += ".cpp.h"; + sourcecode = "sourcecode.cpp.h"; } else if (ext == "png"_s || ext == "gif"_s || ext == "jpg"_s) { keepLastKnownFileType = true; sourcecode = "image"; } else if (ext == "txt"_s) { - sourcecode += ".text"; + sourcecode = "sourcecode.text"; } else if (lang == "CXX"_s) { - sourcecode += ".cpp.cpp"; + sourcecode = "sourcecode.cpp.cpp"; } else if (lang == "C"_s) { - sourcecode += ".c.c"; + sourcecode = "sourcecode.c.c"; } else if (lang == "OBJCXX"_s) { - sourcecode += ".cpp.objcpp"; + sourcecode = "sourcecode.cpp.objcpp"; } else if (lang == "OBJC"_s) { - sourcecode += ".c.objc"; + sourcecode = "sourcecode.c.objc"; } else if (lang == "Fortran"_s) { - sourcecode += ".fortran.f90"; + sourcecode = "sourcecode.fortran.f90"; } else if (lang == "ASM"_s) { - sourcecode += ".asm"; + sourcecode = "sourcecode.asm"; } else if (ext == "metal"_s) { - sourcecode += ".metal"; + sourcecode = "sourcecode.metal"; } else if (ext == "mig"_s) { - sourcecode += ".mig"; + sourcecode = "sourcecode.mig"; } else if (ext == "tbd"_s) { - sourcecode += ".text-based-dylib-definition"; + sourcecode = "sourcecode.text-based-dylib-definition"; } else if (ext == "a"_s) { keepLastKnownFileType = true; sourcecode = "archive.ar"; @@ -1666,9 +1687,7 @@ // If the language is compiled as a source trust Xcode to link with it. for (auto const& Language : - gtgt - ->GetLinkImplementation("NOCONFIG", - cmGeneratorTarget::LinkInterfaceFor::Link) + gtgt->GetLinkImplementation("NOCONFIG", cmGeneratorTarget::UseTo::Link) ->Languages) { if (Language == llang) { return; @@ -2201,11 +2220,10 @@ } std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory(); - cdir = this->ConvertToRelativeForMake(cdir); + cdir = cmSystemTools::ConvertToOutputPath(cdir); std::string makecmd = cmStrCat( - "make -C ", cdir, " -f ", - this->ConvertToRelativeForMake(cmStrCat(makefile, "$CONFIGURATION")), - " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all"); + "make -C ", cdir, " -f ", cmSystemTools::ConvertToOutputPath(makefile), + "$CONFIGURATION", " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all"); buildphase->AddAttribute("shellScript", this->CreateString(makecmd)); buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0")); } @@ -2239,7 +2257,7 @@ const std::vector<std::string>& outputs = ccg.GetOutputs(); if (!outputs.empty()) { for (auto const& output : outputs) { - makefileStream << "\\\n\t" << this->ConvertToRelativeForMake(output); + makefileStream << "\\\n\t" << ConvertToMakefilePath(output); } } else { std::ostringstream str; @@ -2293,7 +2311,7 @@ // There is at least one output, start the rule for it const char* sep = ""; for (auto const& output : outputs) { - makefileStream << sep << this->ConvertToRelativeForMake(output); + makefileStream << sep << ConvertToMakefilePath(output); sep = " "; } makefileStream << ": "; @@ -2302,7 +2320,7 @@ makefileStream << tname[&ccg.GetCC()] << ": "; } for (auto const& dep : realDepends) { - makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep); + makefileStream << "\\\n" << ConvertToMakefilePath(dep); } makefileStream << '\n'; @@ -2319,12 +2337,12 @@ // Build the command line in a single string. std::string cmd2 = ccg.GetCommand(c); cmSystemTools::ReplaceString(cmd2, "/./", "/"); - cmd2 = this->ConvertToRelativeForMake(cmd2); + cmd2 = ConvertToMakefilePath(cmd2); std::string cmd; std::string wd = ccg.GetWorkingDirectory(); if (!wd.empty()) { cmd += "cd "; - cmd += this->ConvertToRelativeForMake(wd); + cmd += ConvertToMakefilePath(wd); cmd += " && "; } cmd += cmd2; @@ -2338,7 +2356,7 @@ target->GetLocalGenerator()->GetMakefile()->GetSource( dep, cmSourceFileLocationKind::Known)) { if (dsf->GetPropertyAsBool("SYMBOLIC")) { - makefileStream << this->ConvertToRelativeForMake(dep) << ":\n"; + makefileStream << ConvertToMakefilePath(dep) << ":\n"; } } } @@ -3115,8 +3133,12 @@ "shellScript", this->CreateString("# shell script goes here\nexit 0")); shellBuildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0")); - cmXCodeObject* target = - this->CreateObject(cmXCodeObject::PBXAggregateTarget); + std::string targetBinaryPath = cmStrCat( + gtgt->Makefile->GetCurrentBinaryDirectory(), '/', gtgt->GetName()); + + cmXCodeObject* target = this->CreateObject( + cmXCodeObject::PBXAggregateTarget, + cmStrCat("PBXAggregateTarget:", gtgt->GetName(), ":", targetBinaryPath)); target->SetComment(gtgt->GetName()); cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST); std::vector<cmXCodeObject*> emptyContentVector; @@ -3340,7 +3362,14 @@ if (!gtgt->IsInBuildSystem()) { return nullptr; } - cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXNativeTarget); + + std::string targetBinaryPath = this->RelativeToRootBinary(cmStrCat( + gtgt->Makefile->GetCurrentBinaryDirectory(), '/', gtgt->GetName())); + + cmXCodeObject* target = this->CreateObject( + cmXCodeObject::PBXNativeTarget, + cmStrCat("PBXNativeTarget:", gtgt->GetName(), ":", targetBinaryPath)); + target->AddAttribute("buildPhases", buildPhases); cmXCodeObject* buildRules = this->CreateObject(cmXCodeObject::OBJECT_LIST); target->AddAttribute("buildRules", buildRules); @@ -4223,18 +4252,18 @@ cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST); bool removeHeaders = actionsOnByDefault & RemoveHeadersOnCopyByDefault; - if (auto prop = gt->GetProperty( + if (cmValue prop = gt->GetProperty( cmStrCat(embedPropertyName, "_REMOVE_HEADERS_ON_COPY"))) { - removeHeaders = cmIsOn(*prop); + removeHeaders = prop.IsOn(); } if (removeHeaders) { attrs->AddObject(this->CreateString("RemoveHeadersOnCopy")); } bool codeSign = actionsOnByDefault & CodeSignOnCopyByDefault; - if (auto prop = + if (cmValue prop = gt->GetProperty(cmStrCat(embedPropertyName, "_CODE_SIGN_ON_COPY"))) { - codeSign = cmIsOn(*prop); + codeSign = prop.IsOn(); } if (codeSign) { attrs->AddObject(this->CreateString("CodeSignOnCopy")); @@ -4856,7 +4885,7 @@ gt->GetType() == cmStateEnums::SHARED_LIBRARY || gt->GetType() == cmStateEnums::MODULE_LIBRARY) { std::string tfull = gt->GetFullPath(configName); - std::string trel = this->ConvertToRelativeForMake(tfull); + std::string trel = ConvertToMakefilePath(tfull); // Add this target to the post-build phases of its dependencies. auto const y = target->GetDependTargets().find(configName); @@ -4882,7 +4911,7 @@ auto const x = target->GetDependLibraries().find(configName); if (x != target->GetDependLibraries().end()) { for (auto const& deplib : x->second) { - std::string file = this->ConvertToRelativeForMake(deplib); + std::string file = ConvertToMakefilePath(deplib); makefileStream << "\\\n\t" << file; dummyRules.insert(file); } @@ -4894,7 +4923,7 @@ std::string d = cmStrCat(this->GetTargetTempDir(gt, configName), "/lib", objLibName, ".a"); - std::string dependency = this->ConvertToRelativeForMake(d); + std::string dependency = ConvertToMakefilePath(d); makefileStream << "\\\n\t" << dependency; dummyRules.insert(dependency); } @@ -4902,7 +4931,7 @@ // Write the action to remove the target if it is out of date. makefileStream << "\n" "\t/bin/rm -f " - << this->ConvertToRelativeForMake(tfull) << '\n'; + << ConvertToMakefilePath(tfull) << '\n'; // if building for more than one architecture // then remove those executables as well if (this->Architectures.size() > 1) { @@ -4912,8 +4941,7 @@ std::string universalFile = cmStrCat(universal, architecture, '/', gt->GetFullName(configName)); makefileStream << "\t/bin/rm -f " - << this->ConvertToRelativeForMake(universalFile) - << '\n'; + << ConvertToMakefilePath(universalFile) << '\n'; } } makefileStream << "\n\n"; @@ -5114,12 +5142,6 @@ "Generate Xcode project files." }; } -std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake( - std::string const& p) -{ - return cmSystemTools::ConvertToOutputPath(p); -} - std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p) { std::string const& rootSrc = @@ -5130,6 +5152,17 @@ return p; } +std::string cmGlobalXCodeGenerator::RelativeToRootBinary(const std::string& p) +{ + std::string binaryDirectory = + this->CurrentRootGenerator->GetCurrentBinaryDirectory(); + if (cmSystemTools::IsSubDirectory(p, binaryDirectory)) { + binaryDirectory = cmSystemTools::ForceToRelativePath(binaryDirectory, p); + } + + return binaryDirectory; +} + std::string cmGlobalXCodeGenerator::RelativeToBinary(const std::string& p) { return this->CurrentRootGenerator->MaybeRelativeToCurBinDir(p); @@ -5298,7 +5331,7 @@ return mf->PlatformIsAppleEmbedded(); } - return cmIsOn(*epnValue); + return epnValue.IsOn(); } bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 12a5cad..14e9308 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h
@@ -161,8 +161,8 @@ bool CreateGroups(std::vector<cmLocalGenerator*>& generators); std::string XCodeEscapePath(const std::string& p); std::string RelativeToSource(const std::string& p); + std::string RelativeToRootBinary(const std::string& p); std::string RelativeToBinary(const std::string& p); - std::string ConvertToRelativeForMake(std::string const& p); void CreateCustomCommands( cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase, cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx index a8a7abb..6c3afef 100644 --- a/Source/cmGraphVizWriter.cxx +++ b/Source/cmGraphVizWriter.cxx
@@ -284,7 +284,8 @@ // Reserved targets have inconsistent names across platforms (e.g. 'all' // vs. 'ALL_BUILD'), which can disrupt the traversal ordering. // We don't need or want them anyway. - if (!cmGlobalGenerator::IsReservedTarget(gt->GetName())) { + if (!cmGlobalGenerator::IsReservedTarget(gt->GetName()) && + !cmHasLiteralPrefix(gt->GetName(), "__cmake_")) { sortedGeneratorTargets.insert(gt.get()); } }
diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx index 3695a9b..4956054 100644 --- a/Source/cmIncludeCommand.cxx +++ b/Source/cmIncludeCommand.cxx
@@ -22,6 +22,7 @@ if (DeprecatedModules.empty()) { DeprecatedModules["Dart"] = cmPolicies::CMP0145; DeprecatedModules["Documentation"] = cmPolicies::CMP0106; + DeprecatedModules["FindBoost"] = cmPolicies::CMP0167; DeprecatedModules["FindCUDA"] = cmPolicies::CMP0146; DeprecatedModules["FindDart"] = cmPolicies::CMP0145; DeprecatedModules["FindPythonInterp"] = cmPolicies::CMP0148;
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h index 1946c9b..4e356b7 100644 --- a/Source/cmLinkItem.h +++ b/Source/cmLinkItem.h
@@ -124,6 +124,7 @@ bool AllDone = false; bool Exists = false; bool Explicit = false; + bool CheckLinkLibraries = false; }; struct cmHeadToLinkInterfaceMap @@ -148,6 +149,7 @@ bool LibrariesDone = false; bool LanguagesDone = false; bool HadHeadSensitiveCondition = false; + bool CheckLinkLibraries = false; }; /** Compute the link type to use for the given configuration. */
diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx index a63b794..eabc8b9 100644 --- a/Source/cmLinkItemGraphVisitor.cxx +++ b/Source/cmLinkItemGraphVisitor.cxx
@@ -99,7 +99,7 @@ DependencyMap& dependencies) { const auto* implementationLibraries = target.GetLinkImplementationLibraries( - config, cmGeneratorTarget::LinkInterfaceFor::Link); + config, cmGeneratorTarget::UseTo::Link); if (implementationLibraries != nullptr) { for (auto const& lib : implementationLibraries->Libraries) { auto const& name = lib.AsStr(); @@ -108,7 +108,7 @@ } const auto* interfaceLibraries = target.GetLinkInterfaceLibraries( - config, &target, cmGeneratorTarget::LinkInterfaceFor::Usage); + config, &target, cmGeneratorTarget::UseTo::Compile); if (interfaceLibraries != nullptr) { for (auto const& lib : interfaceLibraries->Libraries) { auto const& name = lib.AsStr();
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 28aa5d9..018b517 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -156,8 +156,8 @@ linkLib.Value += " "; const cmLinkImplementation* linkImpl = - cli.GetTarget()->GetLinkImplementation( - cli.GetConfig(), cmGeneratorTarget::LinkInterfaceFor::Link); + cli.GetTarget()->GetLinkImplementation(cli.GetConfig(), + cmGeneratorTarget::UseTo::Link); for (const cmLinkImplItem& iter : linkImpl->Libraries) { if (iter.Target != nullptr && @@ -204,7 +204,7 @@ target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) { // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need // to honor the value no matter what it is. - return cmIsOn(*resolveDeviceSymbols); + return resolveDeviceSymbols.IsOn(); } // Determine if we have any dependencies that require @@ -213,7 +213,7 @@ target.GetLinkClosure(config); if (cm::contains(closure->Languages, "CUDA")) { - if (cmIsOn(target.GetProperty("CUDA_SEPARABLE_COMPILATION"))) { + if (target.GetProperty("CUDA_SEPARABLE_COMPILATION").IsOn()) { bool doDeviceLinking = false; switch (target.GetType()) { case cmStateEnums::SHARED_LIBRARY:
diff --git a/Source/cmList.h b/Source/cmList.h index c29aae7..e107096 100644 --- a/Source/cmList.h +++ b/Source/cmList.h
@@ -6,7 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <algorithm> -#include <cstdint> +#include <cstddef> #include <initializer_list> #include <iterator> #include <memory> @@ -47,7 +47,7 @@ using value_type = container_type::value_type; using allocator_type = container_type::allocator_type; - using index_type = std::intptr_t; + using index_type = std::ptrdiff_t; using size_type = container_type::size_type; using difference_type = container_type::difference_type; using reference = container_type::reference;
diff --git a/Source/cmLocalCommonGenerator.cxx b/Source/cmLocalCommonGenerator.cxx index aa953f4..14b3040 100644 --- a/Source/cmLocalCommonGenerator.cxx +++ b/Source/cmLocalCommonGenerator.cxx
@@ -2,10 +2,12 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmLocalCommonGenerator.h" +#include <memory> #include <utility> #include <vector> #include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmStateDirectory.h" @@ -13,14 +15,18 @@ #include "cmStringAlgorithms.h" #include "cmValue.h" -class cmGlobalGenerator; - cmLocalCommonGenerator::cmLocalCommonGenerator(cmGlobalGenerator* gg, cmMakefile* mf) : cmLocalGenerator(gg, mf) { - this->ConfigNames = - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + // Multi-config generators define one set of configurations at the top. + // Single-config generators nominally define one configuration at the top, + // but the implementation has never been strict about that, so look up the + // per-directory config to preserve behavior. + this->ConfigNames = (gg->IsMultiConfig() && !gg->GetMakefiles().empty() + ? gg->GetMakefiles().front().get() + : this->Makefile) + ->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); } cmLocalCommonGenerator::~cmLocalCommonGenerator() = default;
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index d38ed50..94e62ff 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx
@@ -595,7 +595,7 @@ "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) { /* clang-format off */ fout << - "# Set default install directory permissions.\n" + "# Set OS and executable format for runtime-dependencies.\n" "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM)\n" " set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \"" << *platform << "\")\n" @@ -611,7 +611,7 @@ this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) { /* clang-format off */ fout << - "# Set default install directory permissions.\n" + "# Set tool for dependency-resolution of runtime-dependencies.\n" "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL)\n" " set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \"" << *command << "\")\n" @@ -627,7 +627,7 @@ "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) { /* clang-format off */ fout << - "# Set default install directory permissions.\n" + "# Set path to tool for dependency-resolution of runtime-dependencies.\n" "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND)\n" " set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \"" << *command << "\")\n" @@ -644,7 +644,7 @@ if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) { /* clang-format off */ fout << - "# Set default install directory permissions.\n" + "# Set path to fallback-tool for dependency-resolution.\n" "if(NOT DEFINED CMAKE_OBJDUMP)\n" " set(CMAKE_OBJDUMP \"" << *command << "\")\n" @@ -717,16 +717,25 @@ /* clang-format off */ fout << "if(CMAKE_INSTALL_COMPONENT)\n" - " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" + " if(CMAKE_INSTALL_COMPONENT MATCHES \"^[a-zA-Z0-9_.+-]+$\")\n" + " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" "${CMAKE_INSTALL_COMPONENT}.txt\")\n" + " else()\n" + " string(MD5 CMAKE_INST_COMP_HASH \"${CMAKE_INSTALL_COMPONENT}\")\n" + " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" + "${CMAKE_INST_COMP_HASH}.txt\")\n" + " unset(CMAKE_INST_COMP_HASH)\n" + " endif()\n" "else()\n" " set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n" "endif()\n" "\n" - "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n" + "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n" + " string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n" " \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n" - "file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n" - " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n"; + " file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n" + " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" + "endif()\n"; /* clang-format on */ } } @@ -910,8 +919,7 @@ for (std::string const& i : includes) { if (cmNonempty(fwSearchFlag) && this->Makefile->IsOn("APPLE") && cmSystemTools::IsPathToFramework(i)) { - std::string const frameworkDir = - cmSystemTools::CollapseFullPath(cmStrCat(i, "/../")); + std::string const frameworkDir = cmSystemTools::GetFilenamePath(i); if (emitted.insert(frameworkDir).second) { if (sysFwSearchFlag && target && target->IsSystemIncludeDirectory(frameworkDir, config, lang)) { @@ -1479,7 +1487,7 @@ CM_FALLTHROUGH; case cmStateEnums::SHARED_LIBRARY: { std::string sharedLibFlags; - if (linkLanguage != "Swift") { + if (this->IsSplitSwiftBuild() || linkLanguage != "Swift") { sharedLibFlags = cmStrCat( this->Makefile->GetSafeDefinition(libraryLinkVariable), ' '); if (!configUpper.empty()) { @@ -1522,6 +1530,13 @@ } break; case cmStateEnums::EXECUTABLE: { std::string exeFlags; + if (linkLanguage.empty()) { + cmSystemTools::Error( + "CMake can not determine linker language for target: " + + target->GetName()); + return; + } + if (linkLanguage != "Swift") { exeFlags = this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS"); exeFlags += " "; @@ -1530,28 +1545,22 @@ cmStrCat("CMAKE_EXE_LINKER_FLAGS_", configUpper)); exeFlags += " "; } - if (linkLanguage.empty()) { - cmSystemTools::Error( - "CMake can not determine linker language for target: " + - target->GetName()); - return; - } + } - if (target->IsWin32Executable(config)) { - exeFlags += this->Makefile->GetSafeDefinition( - cmStrCat("CMAKE_", linkLanguage, "_CREATE_WIN32_EXE")); - exeFlags += " "; - } else { - exeFlags += this->Makefile->GetSafeDefinition( - cmStrCat("CMAKE_", linkLanguage, "_CREATE_CONSOLE_EXE")); - exeFlags += " "; - } + if (target->IsWin32Executable(config)) { + exeFlags += this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", linkLanguage, "_CREATE_WIN32_EXE")); + exeFlags += " "; + } else { + exeFlags += this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", linkLanguage, "_CREATE_CONSOLE_EXE")); + exeFlags += " "; + } - if (target->IsExecutableWithExports()) { - exeFlags += this->Makefile->GetSafeDefinition( - cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG")); - exeFlags += " "; - } + if (target->IsExecutableWithExports()) { + exeFlags += this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG")); + exeFlags += " "; } this->AddLanguageFlagsForLinking(flags, target, linkLanguage, config); @@ -2721,15 +2730,10 @@ continue; } - std::vector<std::string> architectures; - if (!this->GetGlobalGenerator()->IsXcode()) { - architectures = target->GetAppleArchs(config, lang); - } - if (architectures.empty()) { - architectures.emplace_back(); - } else { + std::vector<std::string> pchArchs = target->GetPchArchs(config, lang); + if (pchArchs.size() > 1) { std::string useMultiArchPch; - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { const std::string pchHeader = target->GetPchHeader(config, lang, arch); if (!pchHeader.empty()) { @@ -2746,7 +2750,7 @@ } } - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { const std::string pchSource = target->GetPchSource(config, lang, arch); const std::string pchHeader = target->GetPchHeader(config, lang, arch); @@ -3135,7 +3139,10 @@ unity_file << *beforeInclude << "\n"; } - unity_file << "/* NOLINTNEXTLINE(bugprone-suspicious-include) */\n"; + // clang-tidy-17 has new include checks that needs NOLINT too. + unity_file + << "/* NOLINTNEXTLINE(bugprone-suspicious-include,misc-include-cleaner) " + "*/\n"; unity_file << "#include \"" << sf_full_path << "\"\n"; if (afterInclude) { @@ -3425,7 +3432,7 @@ const std::string mode = cmIsOn(PICValue) ? "PIE" : "NO_PIE"; std::string supported = "CMAKE_" + lang + "_LINK_" + mode + "_SUPPORTED"; - if (cmIsOff(this->Makefile->GetDefinition(supported))) { + if (this->Makefile->GetDefinition(supported).IsOff()) { return; } @@ -4398,7 +4405,12 @@ } // Make sure the output file name has no invalid characters. - std::string::size_type pos = output.find_first_of("#<>"); + const bool hashNotAllowed = lg.GetState()->UseBorlandMake(); + std::string::size_type pos = output.find_first_of("<>"); + if (pos == std::string::npos && hashNotAllowed) { + pos = output.find_first_of('#'); + } + if (pos != std::string::npos) { lg.GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR,
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index bc3da6e..46a95af 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx
@@ -743,8 +743,6 @@ bool cmLocalNinjaGenerator::HasUniqueByproducts( std::vector<std::string> const& byproducts, cmListFileBacktrace const& bt) { - std::vector<std::string> configs = - this->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); cmGeneratorExpression ge(*this->GetCMakeInstance(), bt); for (std::string const& p : byproducts) { if (cmGeneratorExpression::Find(p) == std::string::npos) { @@ -752,7 +750,7 @@ } std::set<std::string> seen; std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p); - for (std::string const& config : configs) { + for (std::string const& config : this->GetConfigNames()) { for (std::string const& b : this->ExpandCustomCommandOutputPaths(*cge, config)) { if (!seen.insert(b).second) { @@ -800,8 +798,7 @@ std::string const base = cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", targetName, '-'); // The output is not actually created so mark it symbolic. - for (std::string const& config : - this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { + for (std::string const& config : this->GetConfigNames()) { std::string const force = cmStrCat(base, config); if (cmSourceFile* sf = this->Makefile->GetOrCreateGeneratedSource(force)) { sf->SetProperty("SYMBOLIC", "1");
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 7bce1d2..7def1fe 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -898,7 +898,7 @@ // Add a dependency on the rule file itself unless an option to skip // it is specifically enabled by the user or project. cmValue nodep = this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY"); - if (cmIsOff(nodep)) { + if (nodep.IsOff()) { depends.emplace_back(ruleFileName); } } @@ -1532,7 +1532,7 @@ if (haveDirectoryInfo) { // Test whether we need to force Unix paths. if (cmValue force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) { - if (!cmIsOff(force)) { + if (!force.IsOff()) { cmSystemTools::SetForceUnixPaths(true); } } @@ -1784,7 +1784,7 @@ depends.clear(); cmValue noall = this->Makefile->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"); - if (cmIsOff(noall)) { + if (noall.IsOff()) { // Drive the build before installing. depends.emplace_back("all"); } else if (regenerate) {
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index d315f0f..8577cc4 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -196,13 +196,6 @@ this->FortranProject = gg->TargetIsFortranOnly(target); this->WindowsCEProject = gg->TargetsWindowsCE(); - // Intel Fortran always uses VS9 format ".vfproj" files. - cmGlobalVisualStudioGenerator::VSVersion realVersion = gg->GetVersion(); - if (this->FortranProject && - gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS12) { - gg->SetVersion(cmGlobalVisualStudioGenerator::VSVersion::VS9); - } - // add to the list of projects target->Target->SetProperty("GENERATOR_FILE_NAME", lname); // create the dsp.cmake file @@ -224,7 +217,8 @@ this->GlobalGenerator->FileReplacedDuringGenerate(fname); } - gg->SetVersion(realVersion); + this->WindowsCEProject = false; + this->FortranProject = false; } cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() @@ -1135,16 +1129,11 @@ cmStrCat(target->GetPDBDirectory(configName), '/', targetNames.PDB); fout << "\t\t\t\tProgramDatabaseFile=\"" << this->ConvertToXMLOutputPathSingle(temp) << "\"\n"; - if (targetOptions.IsDebug()) { + if (targetOptions.UsingDebugInfo()) { fout << "\t\t\t\tGenerateDebugInformation=\"true\"\n"; } if (this->WindowsCEProject) { - if (this->GetVersion() < - cmGlobalVisualStudioGenerator::VSVersion::VS9) { - fout << "\t\t\t\tSubSystem=\"9\"\n"; - } else { - fout << "\t\t\t\tSubSystem=\"8\"\n"; - } + fout << "\t\t\t\tSubSystem=\"8\"\n"; } std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE"); cmValue stackVal = this->Makefile->GetDefinition(stackVar); @@ -1223,16 +1212,11 @@ target->GetPDBDirectory(configName)); fout << "\t\t\t\tProgramDatabaseFile=\"" << path << "/" << targetNames.PDB << "\"\n"; - if (targetOptions.IsDebug()) { + if (targetOptions.UsingDebugInfo()) { fout << "\t\t\t\tGenerateDebugInformation=\"true\"\n"; } if (this->WindowsCEProject) { - if (this->GetVersion() < - cmGlobalVisualStudioGenerator::VSVersion::VS9) { - fout << "\t\t\t\tSubSystem=\"9\"\n"; - } else { - fout << "\t\t\t\tSubSystem=\"8\"\n"; - } + fout << "\t\t\t\tSubSystem=\"8\"\n"; if (!linkOptions.GetFlag("EntryPointSymbol")) { const char* entryPointSymbol = targetOptions.UsingUnicode()
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index ce1156f..bb05226 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h
@@ -91,6 +91,8 @@ return this->SourcesVisited[target]; }; + bool IsVFProj() const override { return this->FortranProject; } + protected: virtual void GenerateTarget(cmGeneratorTarget* target); @@ -153,8 +155,8 @@ friend class EventWriter; - bool FortranProject; - bool WindowsCEProject; + bool FortranProject = false; + bool WindowsCEProject = false; std::unique_ptr<cmLocalVisualStudio7GeneratorInternals> Internal; std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>>
diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h index 8fed1bd..3e1eb5f 100644 --- a/Source/cmLocalVisualStudioGenerator.h +++ b/Source/cmLocalVisualStudioGenerator.h
@@ -31,6 +31,8 @@ cmLocalVisualStudioGenerator(cmGlobalGenerator* gg, cmMakefile* mf); ~cmLocalVisualStudioGenerator() override; + virtual bool IsVFProj() const = 0; + std::string ConstructScript(cmCustomCommandGenerator const& ccg, const std::string& newline = "\n"); std::string FinishConstructScript(VsProjectType projectType,
diff --git a/Source/cmMachO.cxx b/Source/cmMachO.cxx index 91a7b84..48d2453 100644 --- a/Source/cmMachO.cxx +++ b/Source/cmMachO.cxx
@@ -4,7 +4,6 @@ #include <cstddef> #include <string> -#include <vector> #include <cm/memory> @@ -13,8 +12,12 @@ #include "cmAlgorithms.h" // Include the Mach-O format information system header. +#include <mach-o/arch.h> #include <mach-o/fat.h> #include <mach-o/loader.h> +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000 +# include <mach-o/utils.h> +#endif /** @@ -115,12 +118,15 @@ return v; } + struct cmMachO::MachHeader mach_header() const { return MachHeader; } + protected: bool read_load_commands(uint32_t ncmds, uint32_t sizeofcmds, cmsys::ifstream& fin); bool Swap; std::vector<RawLoadCommand> LoadCommands; + struct cmMachO::MachHeader MachHeader; }; // Implementation for reading Mach-O header and load commands. @@ -138,9 +144,11 @@ if (!read(fin, this->Header)) { return false; } - this->Header.cputype = swap(this->Header.cputype); - this->Header.cpusubtype = swap(this->Header.cpusubtype); - this->Header.filetype = swap(this->Header.filetype); + // swap the header data and export a (potentially) useful subset via the + // parent class. + this->MachHeader.CpuType = swap(this->Header.cputype); + this->MachHeader.CpuSubType = swap(this->Header.cpusubtype); + this->MachHeader.FileType = swap(this->Header.filetype); this->Header.ncmds = swap(this->Header.ncmds); this->Header.sizeofcmds = swap(this->Header.sizeofcmds); this->Header.flags = swap(this->Header.flags); @@ -311,6 +319,9 @@ cmMachO::cmMachO(const char* fname) : Internal(cm::make_unique<cmMachOInternal>(fname)) { + for (const auto& m : this->Internal->MachOList) { + Headers.push_back(m->mach_header()); + } } cmMachO::~cmMachO() = default; @@ -355,3 +366,39 @@ void cmMachO::PrintInfo(std::ostream& /*os*/) const { } + +cmMachO::StringList cmMachO::GetArchitectures() const +{ + cmMachO::StringList archs; + if (Valid() && !this->Headers.empty()) { + for (const auto& header : this->Headers) { + const char* archName = "unknown"; +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000 + if (__builtin_available(macOS 13.0, *)) { + archName = (header.CpuType & CPU_TYPE_ARM) + ? macho_arch_name_for_cpu_type(header.CpuType, header.CpuSubType) + : macho_arch_name_for_cpu_type(header.CpuType, CPU_SUBTYPE_MULTIPLE); + } else +#endif + { +#if defined __clang__ +# define CM_MACOS_DEPRECATED_NXGetArchInfoFromCpuType +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + const NXArchInfo* archInfo = (header.CpuType & CPU_TYPE_ARM) + ? NXGetArchInfoFromCpuType(header.CpuType, header.CpuSubType) + : NXGetArchInfoFromCpuType(header.CpuType, CPU_SUBTYPE_MULTIPLE); +#ifdef CM_MACOS_DEPRECATED_NXGetArchInfoFromCpuType +# undef CM_MACOS_DEPRECATED_NXGetArchInfoFromCpuType +# pragma clang diagnostic pop +#endif + if (archInfo) { + archName = archInfo->name; + } + } + archs.push_back(archName); + } + } + return archs; +}
diff --git a/Source/cmMachO.h b/Source/cmMachO.h index ec7d54c..ddbfc28 100644 --- a/Source/cmMachO.h +++ b/Source/cmMachO.h
@@ -7,6 +7,9 @@ #include <iosfwd> #include <memory> #include <string> +#include <vector> + +#include <mach/machine.h> #if !defined(CMake_USE_MACH_PARSER) # error "This file may be included only if CMake_USE_MACH_PARSER is enabled." @@ -20,6 +23,16 @@ class cmMachO { public: + struct MachHeader + { + cpu_type_t CpuType; + cpu_subtype_t CpuSubType; + uint32_t FileType; + }; + class StringList : public std::vector<std::string> + { + }; + /** Construct with the name of the Mach-O input file to parse. */ cmMachO(const char* fname); @@ -38,8 +51,17 @@ /** Print human-readable information about the Mach-O file. */ void PrintInfo(std::ostream& os) const; + /** Get the architectural header(s) from the Mach-O file. */ + std::vector<struct MachHeader> GetHeaders() const { return this->Headers; } + + /** Get a list of the recognized architectures present in the Mach-O file + * in the order in which they are found. + */ + StringList GetArchitectures() const; + private: friend class cmMachOInternal; bool Valid() const; std::unique_ptr<cmMachOInternal> Internal; + std::vector<struct MachHeader> Headers; };
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 509f28b..ba9fab5 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx
@@ -2522,7 +2522,7 @@ bool cmMakefile::IsOn(const std::string& name) const { - return cmIsOn(this->GetDefinition(name)); + return this->GetDefinition(name).IsOn(); } bool cmMakefile::IsSet(const std::string& name) const @@ -3906,8 +3906,8 @@ #endif } -std::string cmMakefile::GetModulesFile(const std::string& filename, - bool& system, bool debug, +std::string cmMakefile::GetModulesFile(cm::string_view filename, bool& system, + bool debug, std::string& debugBuffer) const { std::string result; @@ -4025,7 +4025,7 @@ // Replace #cmakedefine instances. if (this->cmDefineRegex.find(line)) { cmValue def = this->GetDefinition(this->cmDefineRegex.match(2)); - if (!cmIsOff(def)) { + if (!def.IsOff()) { const std::string indentation = this->cmDefineRegex.match(1); cmSystemTools::ReplaceString(line, cmStrCat("#", indentation, "cmakedefine"), @@ -4043,7 +4043,7 @@ cmStrCat("#", indentation, "cmakedefine01"), cmStrCat("#", indentation, "define")); output += line; - if (!cmIsOff(def)) { + if (!def.IsOff()) { output += " 1"; } else { output += " 0"; @@ -4218,7 +4218,7 @@ bool cmMakefile::GetPropertyAsBool(const std::string& prop) const { - return cmIsOn(this->GetProperty(prop)); + return this->GetProperty(prop).IsOn(); } std::vector<std::string> cmMakefile::GetPropertyKeys() const @@ -4625,7 +4625,7 @@ { // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. if (cmValue val = this->GetDefinition(var)) { - return cmIsOn(val); + return val.IsOn(); } // Enable optional policy warnings with --debug-output, --trace, // or --trace-expand. @@ -4657,13 +4657,14 @@ } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0126 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0128 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 || id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104 || - id == cmPolicies::CMP0123 || id == cmPolicies::CMP0126)) && + id == cmPolicies::CMP0123 || id == cmPolicies::CMP0126 || + id == cmPolicies::CMP0128)) && (!this->IsSet("CMAKE_WARN_DEPRECATED") || this->IsOn("CMAKE_WARN_DEPRECATED"))) { this->IssueMessage(MessageType::DEPRECATION_WARNING,
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index e5edbae..ea0b4c6 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h
@@ -803,7 +803,7 @@ /** * Return a location of a file in cmake or custom modules directory */ - std::string GetModulesFile(const std::string& name) const + std::string GetModulesFile(cm::string_view name) const { bool system; std::string debugBuffer; @@ -813,13 +813,13 @@ /** * Return a location of a file in cmake or custom modules directory */ - std::string GetModulesFile(const std::string& name, bool& system) const + std::string GetModulesFile(cm::string_view name, bool& system) const { std::string debugBuffer; return this->GetModulesFile(name, system, false, debugBuffer); } - std::string GetModulesFile(const std::string& name, bool& system, bool debug, + std::string GetModulesFile(cm::string_view name, bool& system, bool debug, std::string& debugBuffer) const; //! Set/Get a property of this directory
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index ff509ce..d5c50bc 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx
@@ -65,7 +65,7 @@ this->NoRuleMessages = false; if (cmValue ruleStatus = cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) { - this->NoRuleMessages = cmIsOff(*ruleStatus); + this->NoRuleMessages = ruleStatus.IsOff(); } switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) { case cmPolicies::WARN: @@ -244,7 +244,7 @@ } // add custom commands to the clean rules? - bool const clean = cmIsOff(this->Makefile->GetProperty("CLEAN_NO_CUSTOM")); + bool const clean = this->Makefile->GetProperty("CLEAN_NO_CUSTOM").IsOff(); // First generate the object rule files. Save a list of all object // files for this target. @@ -613,8 +613,9 @@ // Use compiler to generate dependencies, if supported. bool const compilerGenerateDeps = this->GlobalGenerator->SupportsCompilerDependencies() && - cmIsOn(this->Makefile->GetDefinition( - cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER"))); + this->Makefile + ->GetDefinition(cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER")) + .IsOn(); auto const scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler : cmDependencyScannerKind::CMake; @@ -671,15 +672,12 @@ std::string const configUpper = cmSystemTools::UpperCase(config); // Add precompile headers dependencies - std::vector<std::string> architectures = - this->GeneratorTarget->GetAppleArchs(config, lang); - if (architectures.empty()) { - architectures.emplace_back(); - } + std::vector<std::string> pchArchs = + this->GeneratorTarget->GetPchArchs(config, lang); std::string filterArch; std::unordered_map<std::string, std::string> pchSources; - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { const std::string pchSource = this->GeneratorTarget->GetPchSource(config, lang, arch); if (pchSource == source.GetFullPath()) { @@ -691,7 +689,7 @@ } if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) { - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { std::string const& pchHeader = this->GeneratorTarget->GetPchHeader(config, lang, arch); depends.push_back(pchHeader); @@ -2082,7 +2080,7 @@ "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS"; if (cmValue val = this->Makefile->GetDefinition(responseVar)) { if (!val->empty()) { - return cmIsOn(val); + return val.IsOn(); } } @@ -2121,7 +2119,7 @@ "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES"; if (cmValue val = this->Makefile->GetDefinition(responseVar)) { if (!val->empty()) { - return cmIsOn(val); + return val.IsOn(); } }
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index d365ef6..77c3e6a 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -1078,37 +1078,6 @@ this->WriteNvidiaDeviceLinkRule(usedResponseFile, config); } -/// Get the target property if it exists, or return a default -static std::string GetTargetPropertyOrDefault(cmGeneratorTarget const* target, - std::string const& property, - std::string defaultValue) -{ - if (cmValue name = target->GetProperty(property)) { - return *name; - } - return defaultValue; -} - -/// Compute the swift module name for target -static std::string GetSwiftModuleName(cmGeneratorTarget const* target) -{ - return GetTargetPropertyOrDefault(target, "Swift_MODULE_NAME", - target->GetName()); -} - -/// Compute the swift module path for the target -/// The returned path will need to be converted to the generator path -static std::string GetSwiftModulePath(cmGeneratorTarget const* target) -{ - std::string moduleName = GetSwiftModuleName(target); - std::string moduleDirectory = GetTargetPropertyOrDefault( - target, "Swift_MODULE_DIRECTORY", - target->LocalGenerator->GetCurrentBinaryDirectory()); - std::string moduleFileName = GetTargetPropertyOrDefault( - target, "Swift_MODULE", moduleName + ".swiftmodule"); - return moduleDirectory + "/" + moduleFileName; -} - void cmNinjaNormalTargetGenerator::WriteLinkStatement( const std::string& config, const std::string& fileConfig, bool firstForConfig) @@ -1221,9 +1190,9 @@ return targetNames.Base; }(); - vars["SWIFT_MODULE_NAME"] = GetSwiftModuleName(gt); + vars["SWIFT_MODULE_NAME"] = gt->GetSwiftModuleName(); vars["SWIFT_MODULE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - this->ConvertToNinjaPath(GetSwiftModulePath(gt)), + this->ConvertToNinjaPath(gt->GetSwiftModulePath(config)), cmOutputConverter::SHELL); vars["SWIFT_SOURCES"] = [this, config]() -> std::string { @@ -1555,8 +1524,8 @@ // in order for there to be a swiftmodule to depend on if (dependency.Target && dependency.Target->GetLinkerLanguage(config) == "Swift") { - std::string swiftmodule = - this->ConvertToNinjaPath(GetSwiftModulePath(dependency.Target)); + std::string swiftmodule = this->ConvertToNinjaPath( + dependency.Target->GetSwiftModulePath(config)); linkBuild.ImplicitDeps.emplace_back(swiftmodule); } }
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index e2642b2..a10635a 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx
@@ -87,8 +87,7 @@ , LocalGenerator( static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator())) { - for (auto const& fileConfig : - target->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { + for (auto const& fileConfig : this->LocalGenerator->GetConfigNames()) { this->Configs[fileConfig].MacOSXContentGenerator = cm::make_unique<MacOSXContentGeneratorType>(this, fileConfig); } @@ -189,14 +188,11 @@ const std::string& config, const std::string& objectFileName) { std::unordered_map<std::string, std::string> pchSources; - std::vector<std::string> architectures = - this->GeneratorTarget->GetAppleArchs(config, language); - if (architectures.empty()) { - architectures.emplace_back(); - } + std::vector<std::string> pchArchs = + this->GeneratorTarget->GetPchArchs(config, language); std::string filterArch; - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { const std::string pchSource = this->GeneratorTarget->GetPchSource(config, language, arch); if (pchSource == source->GetFullPath()) { @@ -1500,14 +1496,11 @@ // Add precompile headers dependencies std::vector<std::string> depList; - std::vector<std::string> architectures = - this->GeneratorTarget->GetAppleArchs(config, language); - if (architectures.empty()) { - architectures.emplace_back(); - } + std::vector<std::string> pchArchs = + this->GeneratorTarget->GetPchArchs(config, language); std::unordered_set<std::string> pchSources; - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { const std::string pchSource = this->GeneratorTarget->GetPchSource(config, language, arch); @@ -1517,7 +1510,7 @@ } if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) { - for (const std::string& arch : architectures) { + for (const std::string& arch : pchArchs) { depList.push_back( this->GeneratorTarget->GetPchHeader(config, language, arch)); if (pchSources.find(source->GetFullPath()) == pchSources.end()) { @@ -1841,12 +1834,6 @@ std::vector<std::string> depList; - std::vector<std::string> architectures = - this->GeneratorTarget->GetAppleArchs(config, language); - if (architectures.empty()) { - architectures.emplace_back(); - } - bmiBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); // For some cases we scan to dynamically discover dependencies. @@ -1959,15 +1946,6 @@ return; } - auto getTargetPropertyOrDefault = - [](cmGeneratorTarget const& target, std::string const& property, - std::string defaultValue) -> std::string { - if (cmValue value = target.GetProperty(property)) { - return *value; - } - return defaultValue; - }; - std::string const language = "Swift"; std::string const objectDir = this->ConvertToNinjaPath( cmStrCat(this->GeneratorTarget->GetSupportDirectory(), @@ -1982,15 +1960,14 @@ // changes to input files (e.g. addition of a comment). vars.emplace("restat", "1"); - std::string const moduleName = - getTargetPropertyOrDefault(target, "Swift_MODULE_NAME", target.GetName()); - std::string const moduleDirectory = getTargetPropertyOrDefault( - target, "Swift_MODULE_DIRECTORY", - target.LocalGenerator->GetCurrentBinaryDirectory()); - std::string const moduleFilename = getTargetPropertyOrDefault( - target, "Swift_MODULE", cmStrCat(moduleName, ".swiftmodule")); + std::string const moduleName = target.GetSwiftModuleName(); std::string const moduleFilepath = - this->ConvertToNinjaPath(cmStrCat(moduleDirectory, '/', moduleFilename)); + this->ConvertToNinjaPath(target.GetSwiftModulePath(config)); + + vars.emplace("description", + cmStrCat("Building Swift Module '", moduleName, "' with ", + sources.size(), + sources.size() == 1 ? " source" : " sources")); bool const isSingleOutput = [this, compileMode]() -> bool { bool isMultiThread = false; @@ -2103,15 +2080,8 @@ // If the dependency emits a swiftmodule, add a dependency edge on that // swiftmodule to the ninja build graph. if (isImportableTarget(*dep)) { - std::string const depModuleName = - getTargetPropertyOrDefault(*dep, "Swift_MODULE_NAME", dep->GetName()); - std::string const depModuleDir = getTargetPropertyOrDefault( - *dep, "Swift_MODULE_DIRECTORY", - dep->LocalGenerator->GetCurrentBinaryDirectory()); - std::string const depModuleFilename = getTargetPropertyOrDefault( - *dep, "Swift_MODULE", cmStrCat(depModuleName, ".swiftmodule")); - std::string const depModuleFilepath = this->ConvertToNinjaPath( - cmStrCat(depModuleDir, '/', depModuleFilename)); + std::string const depModuleFilepath = + this->ConvertToNinjaPath(dep->GetSwiftModulePath(config)); objBuild.ImplicitDeps.push_back(depModuleFilepath); } }
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 705314f..d893c44 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h
@@ -294,8 +294,8 @@ "FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing.", 3, \ 17, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0099, \ - "Link properties are transitive over private dependency on static " \ - "libraries.", \ + "Link properties are transitive over private dependencies of " \ + "static libraries.", \ 3, 17, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0100, "Let AUTOMOC and AUTOUIC process .hh files.", 3, \ 17, 0, cmPolicies::WARN) \ @@ -349,10 +349,10 @@ SELECT(POLICY, CMP0117, \ "MSVC RTTI flag /GR is not added to CMAKE_CXX_FLAGS by default.", 3, \ 20, 0, cmPolicies::WARN) \ - SELECT( \ - POLICY, CMP0118, \ - "The GENERATED source file property is now visible in all directories.", \ - 3, 20, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0118, \ + "GENERATED sources may be used across directories without manual " \ + "marking.", \ + 3, 20, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0119, \ "LANGUAGE source file property explicitly compiles as specified " \ "language.", \ @@ -406,8 +406,8 @@ "is not usable.", \ 3, 24, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0135, \ - "ExternalProject ignores timestamps in archives by default for the " \ - "URL download method", \ + "ExternalProject and FetchContent ignore timestamps in archives by " \ + "default for the URL download method", \ 3, 24, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0136, \ "Watcom runtime library flags are selected by an abstraction.", 3, \ @@ -493,7 +493,39 @@ "More read-only target properties now error when trying to set them.", 3, \ 29, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0161, "CPACK_PRODUCTBUILD_DOMAINS defaults to true.", 3, \ - 29, 0, cmPolicies::WARN) + 29, 0, cmPolicies::WARN) \ + SELECT( \ + POLICY, CMP0162, \ + "Visual Studio generators add UseDebugLibraries indicators by default.", \ + 3, 30, 0, cmPolicies::WARN) \ + SELECT( \ + POLICY, CMP0163, \ + "The GENERATED source file property is now visible in all directories.", \ + 3, 30, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0164, \ + "add_library() rejects SHARED libraries when not supported by the " \ + "platform.", \ + 3, 30, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0165, \ + "enable_language() must not be called before project().", 3, 30, 0, \ + cmPolicies::WARN) \ + SELECT(POLICY, CMP0166, \ + "TARGET_PROPERTY evaluates link properties transitively over " \ + "private dependencies of static libraries.", \ + 3, 30, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0167, "The FindBoost module is removed.", 3, 30, 0, \ + cmPolicies::WARN) \ + SELECT(POLICY, CMP0168, \ + "FetchContent implements steps directly instead of through a " \ + "sub-build.", \ + 3, 30, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0169, \ + "FetchContent_Populate(depName) single-argument signature is " \ + "deprecated.", \ + 3, 30, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0170, \ + "FETCHCONTENT_FULLY_DISCONNECTED requirements are enforced.", 3, 30, \ + 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -536,7 +568,8 @@ F(CMP0155) \ F(CMP0156) \ F(CMP0157) \ - F(CMP0160) + F(CMP0160) \ + F(CMP0162) #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \ F(CMP0116) \
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 53166c1..90c1042 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx
@@ -8,6 +8,8 @@ #include <limits> #include <utility> +#include <cmext/string_view> + #include "cmsys/RegularExpression.hxx" #include "cmExecutionStatus.h" @@ -56,12 +58,21 @@ mf.SetProjectName(projectName); - mf.AddCacheDefinition(projectName + "_BINARY_DIR", - mf.GetCurrentBinaryDirectory(), + std::string varName = cmStrCat(projectName, "_BINARY_DIR"_s); + bool nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName); + mf.AddCacheDefinition(varName, mf.GetCurrentBinaryDirectory(), "Value Computed by CMake", cmStateEnums::STATIC); - mf.AddCacheDefinition(projectName + "_SOURCE_DIR", - mf.GetCurrentSourceDirectory(), + if (nonCacheVarAlreadySet) { + mf.AddDefinition(varName, mf.GetCurrentBinaryDirectory()); + } + + varName = cmStrCat(projectName, "_SOURCE_DIR"_s); + nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName); + mf.AddCacheDefinition(varName, mf.GetCurrentSourceDirectory(), "Value Computed by CMake", cmStateEnums::STATIC); + if (nonCacheVarAlreadySet) { + mf.AddDefinition(varName, mf.GetCurrentSourceDirectory()); + } mf.AddDefinition("PROJECT_BINARY_DIR", mf.GetCurrentBinaryDirectory()); mf.AddDefinition("PROJECT_SOURCE_DIR", mf.GetCurrentSourceDirectory()); @@ -69,9 +80,14 @@ mf.AddDefinition("PROJECT_NAME", projectName); mf.AddDefinitionBool("PROJECT_IS_TOP_LEVEL", mf.IsRootMakefile()); - mf.AddCacheDefinition(projectName + "_IS_TOP_LEVEL", - mf.IsRootMakefile() ? "ON" : "OFF", + + varName = cmStrCat(projectName, "_IS_TOP_LEVEL"_s); + nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName); + mf.AddCacheDefinition(varName, mf.IsRootMakefile() ? "ON" : "OFF", "Value Computed by CMake", cmStateEnums::STATIC); + if (nonCacheVarAlreadySet) { + mf.AddDefinition(varName, mf.IsRootMakefile() ? "ON" : "OFF"); + } // Set the CMAKE_PROJECT_NAME variable to be the highest-level // project name in the tree. If there are two project commands
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 3d2373d..ee53a8e 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx
@@ -122,7 +122,7 @@ // Collect all static_library dependencies from the test target cmLinkImplementationLibraries const* libs = testTarget->GetLinkImplementationLibraries( - config, cmGeneratorTarget::LinkInterfaceFor::Link); + config, cmGeneratorTarget::UseTo::Link); if (libs) { for (cmLinkItem const& item : libs->Libraries) { cmGeneratorTarget const* depTarget = item.Target; @@ -601,7 +601,7 @@ if (this->Moc.Enabled) { // Path prefix - if (cmIsOn(this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX"))) { + if (this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX").IsOn()) { this->Moc.PathPrefix = true; } @@ -657,7 +657,7 @@ auto const& value = this->GenTarget->GetProperty("AUTOGEN_USE_SYSTEM_INCLUDE"); if (value.IsSet()) { - if (cmIsOn(value)) { + if (value.IsOn()) { this->GenTarget->AddSystemIncludeDirectory(this->Dir.IncludeGenExp, "CXX"); } else { @@ -1461,7 +1461,7 @@ for (std::string const& config : this->ConfigsList) { cmLinkImplementationLibraries const* libs = this->GenTarget->GetLinkImplementationLibraries( - config, cmGeneratorTarget::LinkInterfaceFor::Link); + config, cmGeneratorTarget::UseTo::Link); if (libs) { for (cmLinkItem const& item : libs->Libraries) { cmGeneratorTarget const* libTarget = item.Target; @@ -1918,13 +1918,14 @@ info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode); info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix); - cmGeneratorExpressionDAGChecker dagChecker( - this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr); EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries; if (this->MultiConfig) { for (auto const& cfg : this->ConfigsList) { if (!cfg.empty()) { + cmGeneratorExpressionDAGChecker dagChecker( + this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr, + this->LocalGen, cfg); AddInterfaceEntries(this->GenTarget, cfg, "INTERFACE_AUTOMOC_MACRO_NAMES", "CXX", &dagChecker, InterfaceAutoMocMacroNamesEntries, @@ -1932,6 +1933,9 @@ } } } else { + cmGeneratorExpressionDAGChecker dagChecker( + this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr, + this->LocalGen, this->ConfigDefault); AddInterfaceEntries(this->GenTarget, this->ConfigDefault, "INTERFACE_AUTOMOC_MACRO_NAMES", "CXX", &dagChecker, InterfaceAutoMocMacroNamesEntries,
diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 60610a4..a229ea2 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx
@@ -6,6 +6,8 @@ #include <sstream> #include <unordered_set> +#include <cm/string_view> + #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmInstalledFile.h" @@ -281,12 +283,60 @@ cmSourceFile* sf, std::string const& propertyValue, PropertyOp op) { const auto& mf = *sf->GetLocation().GetMakefile(); - auto policyStatus = mf.GetPolicyStatus(cmPolicies::CMP0118); - const bool policyWARN = policyStatus == cmPolicies::WARN; - const bool policyNEW = policyStatus != cmPolicies::OLD && !policyWARN; + auto isProblematic = [&mf, &propertyValue, + op](cm::string_view policy) -> bool { + if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { + mf.IssueMessage( + MessageType::AUTHOR_ERROR, + cmStrCat("Policy ", policy, + " is set to NEW and the following non-boolean value given " + "for property 'GENERATED' is therefore not allowed:\n", + propertyValue, "\nReplace it with a boolean value!\n")); + return true; + } + if (cmIsOff(propertyValue)) { + mf.IssueMessage( + MessageType::AUTHOR_ERROR, + 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, + cmStrCat( + "Policy ", policy, + " is set to NEW and appending to the 'GENERATED' property is " + "therefore not allowed. Only setting it to \"1\" is allowed!\n")); + return true; + } + return false; + }; - if (policyWARN) { + const auto cmp0163PolicyStatus = mf.GetPolicyStatus(cmPolicies::CMP0163); + const bool cmp0163PolicyNEW = cmp0163PolicyStatus != cmPolicies::OLD && + cmp0163PolicyStatus != cmPolicies::WARN; + if (cmp0163PolicyNEW) { + if (!isProblematic("CMP0163")) { + sf->MarkAsGenerated(); + } + return true; + } + + const auto cmp0118PolicyStatus = mf.GetPolicyStatus(cmPolicies::CMP0118); + const bool cmp0118PolicyWARN = cmp0118PolicyStatus == cmPolicies::WARN; + const bool cmp0118PolicyNEW = + cmp0118PolicyStatus != cmPolicies::OLD && !cmp0118PolicyWARN; + + if (cmp0118PolicyNEW) { + if (!isProblematic("CMP0118")) { + sf->MarkAsGenerated(); + } + return true; + } + + if (cmp0118PolicyWARN) { if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { mf.IssueMessage( MessageType::AUTHOR_WARNING, @@ -312,50 +362,22 @@ "\nAppending to property 'GENERATED' will not be allowed " "under policy CMP0118!\n")); } - } else if (policyNEW) { - if (!cmIsOn(propertyValue) && !cmIsOff(propertyValue)) { - mf.IssueMessage( - MessageType::AUTHOR_ERROR, - cmStrCat( - "Policy CMP0118 is set to NEW and the following non-boolean value " - "given for property 'GENERATED' is therefore not allowed:\n", - propertyValue, "\nReplace it with a boolean value!\n")); - return true; - } - if (cmIsOff(propertyValue)) { - mf.IssueMessage( - MessageType::AUTHOR_ERROR, - "Unsetting the 'GENERATED' property is not allowed under CMP0118!\n"); - return true; - } - if (op == PropertyOp::Append || op == PropertyOp::AppendAsString) { - mf.IssueMessage(MessageType::AUTHOR_ERROR, - "Policy CMP0118 is set to NEW and appending to the " - "'GENERATED' property is therefore not allowed. Only " - "setting it to \"1\" is allowed!\n"); - return true; - } } - // Set property. - if (!policyNEW) { - // Do it the traditional way. - switch (op) { - case PropertyOp::Append: - sf->AppendProperty("GENERATED", propertyValue, false); - break; - case PropertyOp::AppendAsString: - sf->AppendProperty("GENERATED", propertyValue, true); - break; - case PropertyOp::Remove: - sf->RemoveProperty("GENERATED"); - break; - case PropertyOp::Set: - sf->SetProperty("GENERATED", propertyValue); - break; - } - } else { - sf->MarkAsGenerated(); + // Set property the traditional way. + switch (op) { + case PropertyOp::Append: + sf->AppendProperty("GENERATED", propertyValue, false); + break; + case PropertyOp::AppendAsString: + sf->AppendProperty("GENERATED", propertyValue, true); + break; + case PropertyOp::Remove: + sf->RemoveProperty("GENERATED"); + break; + case PropertyOp::Set: + sf->SetProperty("GENERATED", propertyValue); + break; } return true; }
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index 1be680a..30ee369 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx
@@ -123,7 +123,8 @@ { // If the file is generated compute the location without checking on disk. // Note: We also check for a locally set GENERATED property, because - // it might have been set before policy CMP0118 was set to NEW. + // it might have been set before policy CMP0118 (or CMP0163) was set + // to NEW. if (this->GetIsGenerated(CheckScope::GlobalAndLocal)) { // The file is either already a full path or is relative to the // build directory for the target. @@ -146,9 +147,12 @@ std::vector<std::string> exts = makefile->GetCMakeInstance()->GetAllExtensions(); auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115); + auto cmp0163 = makefile->GetPolicyStatus(cmPolicies::CMP0163); auto cmp0118 = makefile->GetPolicyStatus(cmPolicies::CMP0118); + bool const cmp0163new = + cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN; bool const cmp0118new = - cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN; + cmp0163new || (cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN); // Tries to find the file in a given directory auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning, cmp0118new, @@ -156,6 +160,7 @@ // Compute full path std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir); // Try full path + // Is this file globally marked as generated? Then mark so locally. if (cmp0118new && makefile->GetGlobalGenerator()->IsGeneratedFile(fullPath)) { this->IsGenerated = true; @@ -172,6 +177,7 @@ for (std::string const& ext : exts) { if (!ext.empty()) { std::string extPath = cmStrCat(fullPath, '.', ext); + // Is this file globally marked as generated? Then mark so locally. if (cmp0118new && makefile->GetGlobalGenerator()->IsGeneratedFile(extPath)) { this->IsGenerated = true; @@ -357,14 +363,19 @@ // Special handling for GENERATED property. if (prop == propGENERATED) { - // We need to check policy CMP0118 in order to determine if we need to - // possibly consider the value of a locally set GENERATED property, too. - auto policyStatus = + // We need to check policy CMP0163 and CMP0118 in order to determine if we + // need to possibly consider the value of a locally set GENERATED property, + // too. + auto cmp0163 = + this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0163); + auto cmp0118 = this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0118); - if (this->GetIsGenerated( - (policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD) - ? CheckScope::GlobalAndLocal - : CheckScope::Global)) { + bool const cmp0163new = + cmp0163 != cmPolicies::OLD && cmp0163 != cmPolicies::WARN; + bool const cmp0118new = cmp0163new || + (cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN); + if (this->GetIsGenerated((!cmp0118new) ? CheckScope::GlobalAndLocal + : CheckScope::Global)) { return cmValue(propTRUE); } return cmValue(propFALSE); @@ -442,7 +453,7 @@ bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const { - return cmIsOn(this->GetProperty(prop)); + return this->GetProperty(prop).IsOn(); } void cmSourceFile::MarkAsGenerated()
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index e814690..44c4ae1 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx
@@ -88,8 +88,10 @@ cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus( cmPolicies::CMP0128) }; - bool const defaultExt{ cmIsOn(*makefile->GetDefinition( - cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) }; + bool const defaultExt{ makefile + ->GetDefinition(cmStrCat("CMAKE_", this->Language, + "_EXTENSIONS_DEFAULT")) + .IsOn() }; bool ext = true; if (cmp0128 == cmPolicies::NEW) { @@ -97,7 +99,7 @@ } if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) { - ext = cmIsOn(*extPropValue); + ext = extPropValue.IsOn(); } std::string const type{ ext ? "EXTENSION" : "STANDARD" }; @@ -252,8 +254,10 @@ cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus( cmPolicies::CMP0128) }; - bool const defaultExt{ cmIsOn(*makefile->GetDefinition( - cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) }; + bool const defaultExt{ makefile + ->GetDefinition(cmStrCat("CMAKE_", this->Language, + "_EXTENSIONS_DEFAULT")) + .IsOn() }; bool ext = true; if (cmp0128 == cmPolicies::NEW) { @@ -261,7 +265,7 @@ } if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) { - ext = cmIsOn(*extPropValue); + ext = extPropValue.IsOn(); } std::string const type{ ext ? "EXTENSION" : "STANDARD" }; @@ -537,6 +541,21 @@ return mapping->second.GetEffectiveStandard(this->Makefile, target, config); } +std::string cmStandardLevelResolver::GetLevelString( + std::string const& lang, cmStandardLevel const& level) const +{ + auto mapping = StandardComputerMapping.find(lang); + if (mapping == StandardComputerMapping.end()) { + return {}; + } + + if (mapping->second.LevelsAsStrings.size() <= level.Index()) { + return {}; + } + + return mapping->second.LevelsAsStrings[level.Index()]; +} + bool cmStandardLevelResolver::AddRequiredTargetFeature( cmTarget* target, const std::string& feature, std::string* error) const {
diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h index 29cab55..523aa73 100644 --- a/Source/cmStandardLevelResolver.h +++ b/Source/cmStandardLevelResolver.h
@@ -29,6 +29,9 @@ std::string const& lang, std::string const& config) const; + std::string GetLevelString(std::string const& lang, + cmStandardLevel const& level) const; + bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature, std::string* error = nullptr) const;
diff --git a/Source/cmState.cxx b/Source/cmState.cxx index d104268..b716dc7 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx
@@ -16,6 +16,7 @@ #include "cmCommand.h" #include "cmDefinitions.h" #include "cmExecutionStatus.h" +#include "cmGlobCacheEntry.h" #include "cmGlobVerificationManager.h" #include "cmList.h" #include "cmListFileCache.h" @@ -238,15 +239,18 @@ messenger); } -void cmState::AddGlobCacheEntry( - bool recurse, bool listDirectories, bool followSymlinks, - const std::string& relative, const std::string& expression, - const std::vector<std::string>& files, const std::string& variable, - cmListFileBacktrace const& backtrace, cmMessenger* messenger) +void cmState::AddGlobCacheEntry(const cmGlobCacheEntry& entry, + const std::string& variable, + cmListFileBacktrace const& backtrace, + cmMessenger* messenger) { - this->GlobVerificationManager->AddCacheEntry( - recurse, listDirectories, followSymlinks, relative, expression, files, - variable, backtrace, messenger); + this->GlobVerificationManager->AddCacheEntry(entry, variable, backtrace, + messenger); +} + +std::vector<cmGlobCacheEntry> cmState::GetGlobCacheEntries() const +{ + return this->GlobVerificationManager->GetCacheEntries(); } void cmState::RemoveCacheEntry(std::string const& key) @@ -655,6 +659,11 @@ &FOR_EACH_CUDA_FEATURE(STRING_LIST_ELEMENT)[1]); return cmValue(s_out); } + if (prop == "CMAKE_HIP_KNOWN_FEATURES") { + static const std::string s_out( + &FOR_EACH_HIP_FEATURE(STRING_LIST_ELEMENT)[1]); + return cmValue(s_out); + } #undef STRING_LIST_ELEMENT return this->GlobalProperties.GetPropertyValue(prop); @@ -662,7 +671,7 @@ bool cmState::GetGlobalPropertyAsBool(const std::string& prop) { - return cmIsOn(this->GetGlobalProperty(prop)); + return this->GetGlobalProperty(prop).IsOn(); } void cmState::SetSourceDirectory(std::string const& sourceDirectory) @@ -712,6 +721,16 @@ return this->GhsMultiIDE; } +void cmState::SetBorlandMake(bool borlandMake) +{ + this->BorlandMake = borlandMake; +} + +bool cmState::UseBorlandMake() const +{ + return this->BorlandMake; +} + void cmState::SetWatcomWMake(bool watcomWMake) { this->WatcomWMake = watcomWMake;
diff --git a/Source/cmState.h b/Source/cmState.h index ae0d52b..4dc982f 100644 --- a/Source/cmState.h +++ b/Source/cmState.h
@@ -34,6 +34,7 @@ class cmMessenger; class cmExecutionStatus; class cmListFileBacktrace; +struct cmGlobCacheEntry; struct cmListFileArgument; template <typename T> @@ -212,6 +213,8 @@ bool UseWindowsVSIDE() const; void SetGhsMultiIDE(bool ghsMultiIDE); bool UseGhsMultiIDE() const; + void SetBorlandMake(bool borlandMake); + bool UseBorlandMake() const; void SetWatcomWMake(bool watcomWMake); bool UseWatcomWMake() const; void SetMinGWMake(bool minGWMake); @@ -263,13 +266,11 @@ std::string const& GetGlobVerifyScript() const; std::string const& GetGlobVerifyStamp() const; bool SaveVerificationScript(const std::string& path, cmMessenger* messenger); - void AddGlobCacheEntry(bool recurse, bool listDirectories, - bool followSymlinks, const std::string& relative, - const std::string& expression, - const std::vector<std::string>& files, + void AddGlobCacheEntry(const cmGlobCacheEntry& entry, const std::string& variable, cmListFileBacktrace const& bt, cmMessenger* messenger); + std::vector<cmGlobCacheEntry> GetGlobCacheEntries() const; cmPropertyDefinitionMap PropertyDefinitions; std::vector<std::string> EnabledLanguages; @@ -295,6 +296,7 @@ bool WindowsShell = false; bool WindowsVSIDE = false; bool GhsMultiIDE = false; + bool BorlandMake = false; bool WatcomWMake = false; bool MinGWMake = false; bool NMake = false;
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 39353f3..343bee5 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx
@@ -451,7 +451,7 @@ bool cmStateDirectory::GetPropertyAsBool(const std::string& prop) const { - return cmIsOn(this->GetProperty(prop)); + return this->GetProperty(prop).IsOn(); } std::vector<std::string> cmStateDirectory::GetPropertyKeys() const
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 76cca21..1284130 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx
@@ -404,6 +404,7 @@ { "VS_DEBUGGER_COMMAND_ARGUMENTS"_s, IC::ExecutableTarget }, { "VS_DEBUGGER_ENVIRONMENT"_s, IC::ExecutableTarget }, { "VS_DEBUGGER_WORKING_DIRECTORY"_s, IC::ExecutableTarget }, + { "VS_USE_DEBUG_LIBRARIES"_s, IC::NonImportedTarget }, // ---- OpenWatcom { "WATCOM_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, // -- Language @@ -411,6 +412,7 @@ COMMON_LANGUAGE_PROPERTIES(C), // ---- C++ COMMON_LANGUAGE_PROPERTIES(CXX), + { "CXX_MODULE_STD"_s, IC::CanCompileSources }, // ---- CSharp { "DOTNET_SDK"_s, IC::NonImportedTarget }, { "DOTNET_TARGET_FRAMEWORK"_s, IC::TargetWithCommands }, @@ -1841,6 +1843,7 @@ "CXX_STANDARD_REQUIRED", "CXX_EXTENSIONS", "CXX_VISIBILITY_PRESET", + "CXX_MODULE_STD", // Static analysis "CXX_CLANG_TIDY", @@ -2150,7 +2153,7 @@ } if (prop == propIMPORTED_GLOBAL) { - if (!cmIsOn(value)) { + if (!value.IsOn()) { std::ostringstream e; e << "IMPORTED_GLOBAL property can't be set to FALSE on targets (\"" << this->impl->Name << "\")\n"; @@ -2844,7 +2847,7 @@ bool cmTarget::GetPropertyAsBool(const std::string& prop) const { - return cmIsOn(this->GetProperty(prop)); + return this->GetProperty(prop).IsOn(); } cmPropertyMap const& cmTarget::GetProperties() const
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index bfd3972..51dce76 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -85,8 +85,7 @@ } // Lookup the target for which libraries are specified. - cmTarget* target = - mf.GetCMakeInstance()->GetGlobalGenerator()->FindTarget(args[0]); + cmTarget* target = mf.GetGlobalGenerator()->FindTarget(args[0]); if (!target) { for (const auto& importedTarget : mf.GetOwnedImportedTargets()) { if (importedTarget->GetName() == args[0]) {
diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx index 8d2ff71..de2012f 100644 --- a/Source/cmTargetPropCommandBase.cxx +++ b/Source/cmTargetPropCommandBase.cxx
@@ -8,7 +8,6 @@ #include "cmStateTypes.h" #include "cmTarget.h" #include "cmValue.h" -#include "cmake.h" cmTargetPropCommandBase::cmTargetPropCommandBase(cmExecutionStatus& status) : Makefile(&status.GetMakefile()) @@ -35,9 +34,7 @@ return false; } // Lookup the target for which property-values are specified. - this->Target = - this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->FindTarget( - args[0]); + this->Target = this->Makefile->GetGlobalGenerator()->FindTarget(args[0]); if (!this->Target) { this->Target = this->Makefile->FindTargetToUse(args[0]); }
diff --git a/Source/cmTargetTraceDependencies.cxx b/Source/cmTargetTraceDependencies.cxx new file mode 100644 index 0000000..cc91a42 --- /dev/null +++ b/Source/cmTargetTraceDependencies.cxx
@@ -0,0 +1,239 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTargetTraceDependencies.h" + +#include <sstream> +#include <utility> + +#include <cmext/algorithm> + +#include "cmCustomCommandGenerator.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmSourceFile.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" + +cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) + : GeneratorTarget(target) +{ + // Convenience. + this->Makefile = target->Target->GetMakefile(); + this->LocalGenerator = target->GetLocalGenerator(); + this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); + this->CurrentEntry = nullptr; + + // Queue all the source files already specified for the target. + std::set<cmSourceFile*> emitted; + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& c : configs) { + std::vector<cmSourceFile*> sources; + this->GeneratorTarget->GetSourceFiles(sources, c); + for (cmSourceFile* sf : sources) { + const std::set<cmGeneratorTarget const*> tgts = + this->GlobalGenerator->GetFilenameTargetDepends(sf); + if (cm::contains(tgts, this->GeneratorTarget)) { + std::ostringstream e; + e << "Evaluation output file\n \"" << sf->ResolveFullPath() + << "\"\ndepends on the sources of a target it is used in. This " + "is a dependency loop and is not allowed."; + this->GeneratorTarget->LocalGenerator->IssueMessage( + MessageType::FATAL_ERROR, e.str()); + return; + } + if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); + } + } + } + + // Queue pre-build, pre-link, and post-build rule dependencies. + this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); + this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); + this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); +} + +void cmTargetTraceDependencies::Trace() +{ + // Process one dependency at a time until the queue is empty. + while (!this->SourceQueue.empty()) { + // Get the next source from the queue. + cmSourceFile* sf = this->SourceQueue.front(); + this->SourceQueue.pop(); + this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; + + // Queue dependencies added explicitly by the user. + if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { + cmList objDeps{ *additionalDeps }; + for (auto& objDep : objDeps) { + if (cmSystemTools::FileIsFullPath(objDep)) { + objDep = cmSystemTools::CollapseFullPath(objDep); + } + } + this->FollowNames(objDeps); + } + + // Queue the source needed to generate this file, if any. + this->FollowName(sf->ResolveFullPath()); + + // Queue dependencies added programmatically by commands. + this->FollowNames(sf->GetDepends()); + + // Queue custom command dependencies. + if (cmCustomCommand const* cc = sf->GetCustomCommand()) { + this->CheckCustomCommand(*cc); + } + } + this->CurrentEntry = nullptr; + + this->GeneratorTarget->AddTracedSources(this->NewSources); +} + +void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) +{ + if (this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); + + // Make sure this file is in the target at the end. + this->NewSources.push_back(sf->ResolveFullPath()); + } +} + +void cmTargetTraceDependencies::FollowName(std::string const& name) +{ + // Use lower bound with key comparison to not repeat the search for the + // insert position if the name could not be found (which is the common case). + auto i = this->NameMap.lower_bound(name); + if (i == this->NameMap.end() || i->first != name) { + // Check if we know how to generate this file. + cmSourcesWithOutput sources = + this->LocalGenerator->GetSourcesWithOutput(name); + // If we failed to find a target or source and we have a relative path, it + // might be a valid source if made relative to the current binary + // directory. + if (!sources.Target && !sources.Source && + !cmSystemTools::FileIsFullPath(name)) { + auto fullname = + cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); + fullname = cmSystemTools::CollapseFullPath( + fullname, this->Makefile->GetHomeOutputDirectory()); + sources = this->LocalGenerator->GetSourcesWithOutput(fullname); + } + i = this->NameMap.emplace_hint(i, name, sources); + } + if (cmTarget* t = i->second.Target) { + // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or + // POST_BUILD command. + this->GeneratorTarget->Target->AddUtility(t->GetName(), false); + } + if (cmSourceFile* sf = i->second.Source) { + // For now only follow the dependency if the source file is not a + // byproduct. Semantics of byproducts in a non-Ninja context will have to + // be defined first. + if (!i->second.SourceIsByproduct) { + // Record the dependency we just followed. + if (this->CurrentEntry) { + this->CurrentEntry->Depends.push_back(sf); + } + this->QueueSource(sf); + } + } +} + +void cmTargetTraceDependencies::FollowNames( + std::vector<std::string> const& names) +{ + for (std::string const& name : names) { + this->FollowName(name); + } +} + +bool cmTargetTraceDependencies::IsUtility(std::string const& dep) +{ + // Dependencies on targets (utilities) are supposed to be named by + // just the target name. However for compatibility we support + // naming the output file generated by the target (assuming there is + // no output-name property which old code would not have set). In + // that case the target name will be the file basename of the + // dependency. + std::string util = cmSystemTools::GetFilenameName(dep); + if (cmSystemTools::GetFilenameLastExtension(util) == ".exe") { + util = cmSystemTools::GetFilenameWithoutLastExtension(util); + } + + // Check for a target with this name. + if (cmGeneratorTarget* t = + this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( + util)) { + // If we find the target and the dep was given as a full path, + // then make sure it was not a full path to something else, and + // the fact that the name matched a target was just a coincidence. + if (cmSystemTools::FileIsFullPath(dep)) { + if (t->GetType() >= cmStateEnums::EXECUTABLE && + t->GetType() <= cmStateEnums::MODULE_LIBRARY) { + // This is really only for compatibility so we do not need to + // worry about configuration names and output names. + std::string tLocation = t->GetLocationForBuild(); + tLocation = cmSystemTools::GetFilenamePath(tLocation); + std::string depLocation = cmSystemTools::GetFilenamePath(dep); + depLocation = cmSystemTools::CollapseFullPath(depLocation); + tLocation = cmSystemTools::CollapseFullPath(tLocation); + if (depLocation == tLocation) { + this->GeneratorTarget->Target->AddUtility(util, false); + return true; + } + } + } else { + // The original name of the dependency was not a full path. It + // must name a target, so add the target-level dependency. + this->GeneratorTarget->Target->AddUtility(util, true); + return true; + } + } + + // The dependency does not name a target built in this project. + return false; +} + +void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) +{ + // Collect dependencies referenced by all configurations. + std::set<std::string> depends; + for (std::string const& config : + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { + for (cmCustomCommandGenerator const& ccg : + this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { + // Collect target-level dependencies referenced in command lines. + for (auto const& util : ccg.GetUtilities()) { + this->GeneratorTarget->Target->AddUtility(util); + } + + // Collect file-level dependencies referenced in DEPENDS. + depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); + } + } + + // Queue file-level dependencies. + for (std::string const& dep : depends) { + if (!this->IsUtility(dep)) { + // The dependency does not name a target and may be a file we + // know how to generate. Queue it. + this->FollowName(dep); + } + } +} + +void cmTargetTraceDependencies::CheckCustomCommands( + const std::vector<cmCustomCommand>& commands) +{ + for (cmCustomCommand const& command : commands) { + this->CheckCustomCommand(command); + } +}
diff --git a/Source/cmTargetTraceDependencies.h b/Source/cmTargetTraceDependencies.h new file mode 100644 index 0000000..5bbe8df --- /dev/null +++ b/Source/cmTargetTraceDependencies.h
@@ -0,0 +1,46 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <queue> +#include <set> +#include <string> +#include <vector> + +#include "cmGeneratorTarget.h" +#include "cmLocalGenerator.h" + +class cmGlobalGenerator; +class cmSourceFile; +class cmCustomCommand; +class cmMakefile; + +class cmTargetTraceDependencies +{ +public: + cmTargetTraceDependencies(cmGeneratorTarget* target); + void Trace(); + +private: + cmGeneratorTarget* GeneratorTarget; + cmMakefile* Makefile; + cmLocalGenerator* LocalGenerator; + cmGlobalGenerator const* GlobalGenerator; + using SourceEntry = cmGeneratorTarget::SourceEntry; + SourceEntry* CurrentEntry; + std::queue<cmSourceFile*> SourceQueue; + std::set<cmSourceFile*> SourcesQueued; + using NameMapType = std::map<std::string, cmSourcesWithOutput>; + NameMapType NameMap; + std::vector<std::string> NewSources; + + void QueueSource(cmSourceFile* sf); + void FollowName(std::string const& name); + void FollowNames(std::vector<std::string> const& names); + bool IsUtility(std::string const& dep); + void CheckCustomCommand(cmCustomCommand const& cc); + void CheckCustomCommands(const std::vector<cmCustomCommand>& commands); +};
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index 7c9969c..8a39144 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx
@@ -50,7 +50,7 @@ bool cmTest::GetPropertyAsBool(const std::string& prop) const { - return cmIsOn(this->GetProperty(prop)); + return this->GetProperty(prop).IsOn(); } void cmTest::SetProperty(const std::string& prop, cmValue value)
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index fbd676d..a3d5244 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -1177,7 +1177,7 @@ const char* privateReference = "True"; if (cmValue value = this->GeneratorTarget->GetProperty( "VS_DOTNET_REFERENCES_COPY_LOCAL")) { - if (cmIsOff(*value)) { + if (value.IsOff()) { privateReference = "False"; } } @@ -1522,7 +1522,6 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( Elem& e1, std::string const& config) { - cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; cmValue mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG"); if (mfcFlag) { std::string const mfcFlagValue = @@ -1553,12 +1552,9 @@ } else { e1.Element("CharacterSet", "MultiByte"); } - if (cmValue projectToolsetOverride = - this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) { - e1.Element("PlatformToolset", *projectToolsetOverride); - } else if (const char* toolset = gg->GetPlatformToolset()) { - e1.Element("PlatformToolset", toolset); - } + + this->WriteMSToolConfigurationValuesCommon(e1, config); + if (this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_COMPONENT") || this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_EXTENSIONS")) { e1.Element("WindowsAppContainer", "true"); @@ -1589,11 +1585,9 @@ return; } - cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; - Options& o = *(this->ClOptions[config]); - if (o.IsDebug()) { + if (o.UsingDebugInfo()) { e1.Element("DebugSymbols", "true"); e1.Element("DefineDebug", "true"); } @@ -1608,12 +1602,7 @@ o.RemoveFlag("Platform"); } - if (cmValue projectToolsetOverride = - this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) { - e1.Element("PlatformToolset", *projectToolsetOverride); - } else if (const char* toolset = gg->GetPlatformToolset()) { - e1.Element("PlatformToolset", toolset); - } + this->WriteMSToolConfigurationValuesCommon(e1, config); std::string postfixName = cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX"); @@ -1633,6 +1622,50 @@ oh.OutputFlagMap(); } +void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValuesCommon( + Elem& e1, std::string const& config) +{ + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; + if (cmValue projectToolsetOverride = + this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) { + e1.Element("PlatformToolset", *projectToolsetOverride); + } else if (const char* toolset = gg->GetPlatformToolset()) { + e1.Element("PlatformToolset", toolset); + } + + cm::optional<bool> maybeUseDebugLibraries; + if (cmValue useDebugLibrariesProp = + this->GeneratorTarget->GetProperty("VS_USE_DEBUG_LIBRARIES")) { + // The project explicitly specified a value for this target. + // An empty string suppresses generation of the setting altogether. + std::string const useDebugLibraries = cmGeneratorExpression::Evaluate( + *useDebugLibrariesProp, this->LocalGenerator, config); + if (!useDebugLibraries.empty()) { + maybeUseDebugLibraries = cmIsOn(useDebugLibraries); + } + } else if (this->GeneratorTarget->GetPolicyStatusCMP0162() == + cmPolicies::NEW) { + // The project did not explicitly specify a value for this target. + // If the target compiles sources for a known MSVC runtime library, + // base our default value on that. + if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { + maybeUseDebugLibraries = this->ClOptions[config]->UsingDebugRuntime(); + } + // For other targets, such as UTILITY targets, base our default + // on the configuration name. + if (!maybeUseDebugLibraries) { + maybeUseDebugLibraries = cmSystemTools::UpperCase(config) == "DEBUG"_s; + } + } + if (maybeUseDebugLibraries) { + if (*maybeUseDebugLibraries) { + e1.Element("UseDebugLibraries", "true"); + } else { + e1.Element("UseDebugLibraries", "false"); + } + } +} + //---------------------------------------------------------------------------- void cmVisualStudio10TargetGenerator::WriteNsightTegraConfigurationValues( Elem& e1, std::string const&) @@ -2051,6 +2084,18 @@ "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms"); } } + { + if (cmValue p = this->GeneratorTarget->GetProperty("VS_FILTER_PROPS")) { + auto props = *p; + if (!props.empty()) { + ConvertToWindowsSlash(props); + Elem(e0, "Import") + .Attribute("Project", props) + .Attribute("Condition", cmStrCat("exists('", props, "')")) + .Attribute("Label", "LocalAppDataPlatform"); + } + } + } } fout << '\n'; @@ -3168,7 +3213,10 @@ Options& linkOptions = *(this->LinkOptions[configName]); const std::string cond = this->CalcCondition(configName); - if (this->IPOEnabledConfigurations.count(configName) == 0) { + if (this->IPOEnabledConfigurations.count(configName) > 0) { + // Suppress LinkIncremental in favor of WholeProgramOptimization. + e1.WritePlatformConfigTag("LinkIncremental", cond, ""); + } else { const char* incremental = linkOptions.GetFlag("LinkIncremental"); e1.WritePlatformConfigTag("LinkIncremental", cond, (incremental ? incremental : "true")); @@ -3513,6 +3561,26 @@ clOptions.RemoveFlag("CompileAs"); } + if (this->ProjectType == VsProjectType::vcxproj && this->MSTools) { + // Suppress Microsoft.Cl.Common.props default settings for which the + // project specifies no flags. Do not let UseDebugLibraries affect them. + if (!clOptions.HasFlag("BasicRuntimeChecks")) { + clOptions.AddFlag("BasicRuntimeChecks", "Default"); + } + if (!clOptions.HasFlag("MinimalRebuild")) { + clOptions.AddFlag("MinimalRebuild", ""); + } + if (!clOptions.HasFlag("Optimization")) { + clOptions.AddFlag("Optimization", ""); + } + if (!clOptions.HasFlag("RuntimeLibrary")) { + clOptions.AddFlag("RuntimeLibrary", ""); + } + if (!clOptions.HasFlag("SupportJustMyCode")) { + clOptions.AddFlag("SupportJustMyCode", ""); + } + } + this->ClOptions[configName] = std::move(pOptions); return true; } @@ -3555,7 +3623,7 @@ // without value so PDBs don't get generated uselessly. Each tag // goes on its own line because Visual Studio corrects it this // way when saving the project after CMake generates it. - if (!clOptions.IsDebug()) { + if (!clOptions.UsingDebugInfo()) { Elem e3(e2, "DebugInformationFormat"); e3.SetHasElements(); } @@ -3812,6 +3880,14 @@ cudaOptions.AddFlag("CudaRuntime", "None"); } + if (this->ProjectType == VsProjectType::vcxproj && this->MSTools) { + // Suppress inheritance of host compiler optimization flags + // when the project does not specify any optimization flags for CUDA. + if (!cudaOptions.HasFlag("Optimization")) { + cudaOptions.AddFlag("Optimization", ""); + } + } + this->CudaOptions[configName] = std::move(pOptions); return true; } @@ -4176,9 +4252,9 @@ if (dpiAware) { if (*dpiAware == "PerMonitor"_s) { e2.Element("EnableDpiAwareness", "PerMonitorHighDPIAware"); - } else if (cmIsOn(*dpiAware)) { + } else if (dpiAware.IsOn()) { e2.Element("EnableDpiAwareness", "true"); - } else if (cmIsOff(*dpiAware)) { + } else if (dpiAware.IsOff()) { e2.Element("EnableDpiAwareness", "false"); } else { cmSystemTools::Error(
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 1f8b2eb..056f426 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -72,6 +72,8 @@ void WriteCEDebugProjectConfigurationValues(Elem& e0); void WriteMSToolConfigurationValuesManaged(Elem& e1, std::string const& config); + void WriteMSToolConfigurationValuesCommon(Elem& e1, + std::string const& config); void WriteHeaderSource(Elem& e1, cmSourceFile const* sf, ConfigToSettings const& toolSettings); void WriteExtraSource(Elem& e1, cmSourceFile const* sf,
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 9dd2e6c..a5f2cdf 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -26,7 +26,6 @@ cmVS7FlagTable const* extraTable) : cmIDEOptions() , LocalGenerator(lg) - , Version(lg->GetVersion()) , CurrentTool(tool) { // Store the given flag tables. @@ -75,21 +74,14 @@ // initialization to off, but the user has the option of removing // the flag to disable exception handling. When the user does // remove the flag we need to override the IDE default of on. - switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - case cmGlobalVisualStudioGenerator::VSVersion::VS14: - case cmGlobalVisualStudioGenerator::VSVersion::VS15: - case cmGlobalVisualStudioGenerator::VSVersion::VS16: - case cmGlobalVisualStudioGenerator::VSVersion::VS17: - // by default VS puts <ExceptionHandling></ExceptionHandling> empty - // for a project, to make our projects look the same put a new line - // and space over for the closing </ExceptionHandling> as the default - // value - this->FlagMap["ExceptionHandling"] = "\n "; - break; - default: - this->FlagMap["ExceptionHandling"] = "0"; - break; + if (!this->LocalGenerator->IsVFProj()) { + // by default VS puts <ExceptionHandling></ExceptionHandling> empty + // for a project, to make our projects look the same put a new line + // and space over for the closing </ExceptionHandling> as the default + // value + this->FlagMap["ExceptionHandling"] = "\n "; + } else { + this->FlagMap["ExceptionHandling"] = "0"; } } @@ -100,17 +92,16 @@ // to the generated project to disable logo suppression. Otherwise // the GUI default is to enable suppression. // - // On Visual Studio 9, the value of this attribute should be + // In '.vfproj' files, the value of this attribute should be // "FALSE", instead of an empty string. if (verbose && this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) { this->FlagMap["SuppressStartupBanner"] = - this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9 ? "FALSE" - : ""; + !this->LocalGenerator->IsVFProj() ? "" : "FALSE"; } } -bool cmVisualStudioGeneratorOptions::IsDebug() const +bool cmVisualStudioGeneratorOptions::UsingDebugInfo() const { if (this->CurrentTool != CSharpCompiler) { return this->FlagMap.find("DebugInformationFormat") != this->FlagMap.end(); @@ -124,6 +115,15 @@ return false; } +cm::optional<bool> cmVisualStudioGeneratorOptions::UsingDebugRuntime() const +{ + cm::optional<bool> result; + if (const char* rtl = this->GetFlag("RuntimeLibrary")) { + result = strstr(rtl, "Debug") != nullptr; + } + return result; +} + bool cmVisualStudioGeneratorOptions::IsWinRt() const { return this->FlagMap.find("CompileAsWinRT") != this->FlagMap.end(); @@ -364,24 +364,22 @@ } std::ostringstream oss; - if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { + if (!this->LocalGenerator->IsVFProj()) { oss << "%(" << tag << ')'; } auto de = cmRemoveDuplicates(this->Defines); for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) { - // Escape the definition for the compiler. std::string define; - if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9) { - define = this->LocalGenerator->EscapeForShell(di, true); - } else { + if (!this->LocalGenerator->IsVFProj()) { + // Escape the definition for MSBuild. define = di; - } - // Escape this flag for the MSBuild. - if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { cmVS10EscapeForMSBuild(define); if (lang == "RC"_s) { cmSystemTools::ReplaceString(define, "\"", "\\\""); } + } else { + // Escape the definition for the compiler. + define = this->LocalGenerator->EscapeForShell(di, true); } // Store the flag in the project file. oss << ';' << define; @@ -419,7 +417,7 @@ } // Escape this include for the MSBuild. - if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { + if (!this->LocalGenerator->IsVFProj()) { cmVS10EscapeForMSBuild(include); } oss << sep << include; @@ -431,7 +429,7 @@ } } - if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { + if (!this->LocalGenerator->IsVFProj()) { oss << sep << "%(" << tag << ')'; } @@ -445,7 +443,7 @@ std::ostringstream oss; const char* sep = ""; for (std::string i : m.second) { - if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { + if (!this->LocalGenerator->IsVFProj()) { cmVS10EscapeForMSBuild(i); } oss << sep << i;
diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h index 20e2d22..409fd62 100644 --- a/Source/cmVisualStudioGeneratorOptions.h +++ b/Source/cmVisualStudioGeneratorOptions.h
@@ -7,6 +7,8 @@ #include <iosfwd> #include <string> +#include <cm/optional> + #include "cmGlobalVisualStudioGenerator.h" #include "cmIDEFlagTable.h" #include "cmIDEOptions.h" @@ -65,7 +67,8 @@ void FixManifestUACFlags(); - bool IsDebug() const; + bool UsingDebugInfo() const; + cm::optional<bool> UsingDebugRuntime() const; bool IsWinRt() const; bool IsManaged() const; // Write options to output. @@ -84,7 +87,6 @@ private: cmLocalVisualStudioGenerator* LocalGenerator; - cmGlobalVisualStudioGenerator::VSVersion Version; std::string Configuration; Tool CurrentTool;
diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx index 80327e4..12272d1 100644 --- a/Source/cmXCodeScheme.cxx +++ b/Source/cmXCodeScheme.cxx
@@ -77,7 +77,7 @@ WriteBuildAction(xout, container); WriteTestAction(xout, FindConfiguration("Debug"), container); WriteLaunchAction(xout, FindConfiguration(launchConfiguration), container); - WriteProfileAction(xout, FindConfiguration("Release")); + WriteProfileAction(xout, FindConfiguration("Release"), container); WriteAnalyzeAction(xout, FindConfiguration("Debug")); WriteArchiveAction(xout, FindConfiguration("Release")); @@ -240,18 +240,13 @@ // Diagnostics tab end if (IsExecutable(this->Target)) { - xout.StartElement("BuildableProductRunnable"); - xout.BreakAttributes(); - xout.Attribute("runnableDebuggingMode", "0"); - + WriteBuildableProductRunnable(xout, this->Target, container); } else { xout.StartElement("MacroExpansion"); + WriteBuildableReference(xout, this->Target, container); + xout.EndElement(); } - WriteBuildableReference(xout, this->Target, container); - - xout.EndElement(); // MacroExpansion - // Info tab begin if (cmValue exe = @@ -374,7 +369,7 @@ bool defaultValue) { cmValue property = Target->GetTarget()->GetProperty(varName); - bool isOn = (!property && defaultValue) || cmIsOn(property); + bool isOn = (!property && defaultValue) || property.IsOn(); if (isOn) { xout.Attribute(attrName.c_str(), "YES"); @@ -404,7 +399,8 @@ } void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout, - const std::string& configuration) + const std::string& configuration, + const std::string& container) { xout.StartElement("ProfileAction"); xout.BreakAttributes(); @@ -415,6 +411,11 @@ WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning", "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING", true); + + if (IsExecutable(this->Target)) { + WriteBuildableProductRunnable(xout, this->Target, container); + } + xout.EndElement(); } @@ -437,6 +438,17 @@ xout.EndElement(); } +void cmXCodeScheme::WriteBuildableProductRunnable(cmXMLWriter& xout, + const cmXCodeObject* xcObj, + const std::string& container) +{ + xout.StartElement("BuildableProductRunnable"); + xout.BreakAttributes(); + xout.Attribute("runnableDebuggingMode", "0"); + WriteBuildableReference(xout, xcObj, container); + xout.EndElement(); +} + void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout, const cmXCodeObject* xcObj, const std::string& container)
diff --git a/Source/cmXCodeScheme.h b/Source/cmXCodeScheme.h index 07fdedb..c933a6e 100644 --- a/Source/cmXCodeScheme.h +++ b/Source/cmXCodeScheme.h
@@ -57,10 +57,14 @@ const std::string& value, const std::string& varName); - void WriteProfileAction(cmXMLWriter& xout, const std::string& configuration); + void WriteProfileAction(cmXMLWriter& xout, const std::string& configuration, + const std::string& container); void WriteAnalyzeAction(cmXMLWriter& xout, const std::string& configuration); void WriteArchiveAction(cmXMLWriter& xout, const std::string& configuration); + void WriteBuildableProductRunnable(cmXMLWriter& xout, + const cmXCodeObject* xcObj, + const std::string& container); void WriteBuildableReference(cmXMLWriter& xout, const cmXCodeObject* xcObj, const std::string& container);
diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 88fac8d..775265f 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx
@@ -4,10 +4,12 @@ #include <algorithm> #include <array> +#include <chrono> #include <cstdio> #include <cstdlib> #include <cstring> #include <initializer_list> +#include <iomanip> #include <iostream> #include <sstream> #include <stdexcept> @@ -48,6 +50,7 @@ #include "cmExternalMakefileProjectGenerator.h" #include "cmFileTimeCache.h" #include "cmGeneratorTarget.h" +#include "cmGlobCacheEntry.h" #include "cmGlobalGenerator.h" #include "cmGlobalGeneratorFactory.h" #include "cmLinkLineComputer.h" @@ -96,7 +99,6 @@ # include "cmGlobalNMakeMakefileGenerator.h" # include "cmGlobalVisualStudio12Generator.h" # include "cmGlobalVisualStudio14Generator.h" -# include "cmGlobalVisualStudio9Generator.h" # include "cmGlobalVisualStudioVersionedGenerator.h" # include "cmVSSetupHelper.h" @@ -2346,16 +2348,16 @@ // 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 && cmIsOff(*value)); + this->Messenger->SetSuppressDeprecatedWarnings(value && value.IsOff()); value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED"); - this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value)); + this->Messenger->SetDeprecatedWarningsAsErrors(value.IsOn()); value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_WARNINGS"); - this->Messenger->SetSuppressDevWarnings(cmIsOn(value)); + this->Messenger->SetSuppressDevWarnings(value.IsOn()); value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_ERRORS"); - this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(*value)); + this->Messenger->SetDevWarningsAsErrors(value && value.IsOff()); int ret = this->ActualConfigure(); cmValue delCacheVars = @@ -2557,7 +2559,22 @@ #endif // actually do the configure + auto startTime = std::chrono::steady_clock::now(); this->GlobalGenerator->Configure(); + auto endTime = std::chrono::steady_clock::now(); + + if (this->GetWorkingMode() == cmake::NORMAL_MODE) { + std::ostringstream msg; + if (cmSystemTools::GetErrorOccurredFlag()) { + msg << "Configuring incomplete, errors occurred!"; + } else { + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( + endTime - startTime); + msg << "Configuring done (" << std::fixed << std::setprecision(1) + << ms.count() / 1000.0L << "s)"; + } + this->UpdateProgress(msg.str(), -1); + } #if !defined(CMAKE_BOOTSTRAP) this->ConfigureLog.reset(); @@ -2635,7 +2652,6 @@ static VSVersionedGenerator const vsGenerators[] = { { "14.0", "Visual Studio 14 2015" }, // { "12.0", "Visual Studio 12 2013" }, // - { "9.0", "Visual Studio 9 2008" } }; static const char* const vsEntries[] = { "\\Setup\\VC;ProductDir", // @@ -2890,10 +2906,20 @@ auto profilingRAII = this->CreateProfilingEntry("project", "generate"); #endif + auto startTime = std::chrono::steady_clock::now(); if (!this->GlobalGenerator->Compute()) { return -1; } this->GlobalGenerator->Generate(); + auto endTime = std::chrono::steady_clock::now(); + { + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - + startTime); + std::ostringstream msg; + msg << "Generating done (" << std::fixed << std::setprecision(1) + << ms.count() / 1000.0L << "s)"; + this->UpdateProgress(msg.str(), -1); + } if (!this->GraphVizFile.empty()) { std::cout << "Generate graphviz: " << this->GraphVizFile << '\n'; this->GenerateGraphViz(this->GraphVizFile); @@ -2924,13 +2950,13 @@ this->UnwatchUnusedCli(key); if (key == "CMAKE_WARN_DEPRECATED"_s) { - this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(value)); + this->Messenger->SetSuppressDeprecatedWarnings(value && value.IsOff()); } else if (key == "CMAKE_ERROR_DEPRECATED"_s) { - this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value)); + this->Messenger->SetDeprecatedWarningsAsErrors(value.IsOn()); } else if (key == "CMAKE_SUPPRESS_DEVELOPER_WARNINGS"_s) { - this->Messenger->SetSuppressDevWarnings(cmIsOn(value)); + this->Messenger->SetSuppressDevWarnings(value.IsOn()); } else if (key == "CMAKE_SUPPRESS_DEVELOPER_ERRORS"_s) { - this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(value)); + this->Messenger->SetDevWarningsAsErrors(value && value.IsOff()); } } @@ -2949,16 +2975,17 @@ return this->State->GetGlobVerifyStamp(); } -void cmake::AddGlobCacheEntry(bool recurse, bool listDirectories, - bool followSymlinks, const std::string& relative, - const std::string& expression, - const std::vector<std::string>& files, +void cmake::AddGlobCacheEntry(const cmGlobCacheEntry& entry, const std::string& variable, cmListFileBacktrace const& backtrace) { - this->State->AddGlobCacheEntry(recurse, listDirectories, followSymlinks, - relative, expression, files, variable, - backtrace, this->Messenger.get()); + this->State->AddGlobCacheEntry(entry, variable, backtrace, + this->Messenger.get()); +} + +std::vector<cmGlobCacheEntry> cmake::GetGlobCacheEntries() const +{ + return this->State->GetGlobCacheEntries(); } std::vector<std::string> cmake::GetAllExtensions() const @@ -3019,7 +3046,6 @@ cmGlobalVisualStudioVersionedGenerator::NewFactory15()); this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory()); this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory()); - this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory()); this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory()); @@ -3769,7 +3795,7 @@ } projName = *cachedProjectName; - if (cmIsOn(this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE"))) { + if (this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE").IsOn()) { verbose = true; } @@ -3779,22 +3805,12 @@ // itself, there is the risk of building an out-of-date solution file due // to limitations of the underlying build system. std::string const stampList = cachePath + "/" + "CMakeFiles/" + - cmGlobalVisualStudio9Generator::GetGenerateStampList(); + cmGlobalVisualStudio12Generator::GetGenerateStampList(); // Note that the stampList file only exists for VS generators. if (cmSystemTools::FileExists(stampList)) { - // Check if running for Visual Studio 9 - we need to explicitly run - // the glob verification script before starting the build this->AddScriptingCommands(); - if (this->GlobalGenerator->MatchesGeneratorName("Visual Studio 9 2008")) { - std::string const globVerifyScript = - cachePath + "/" + "CMakeFiles/" + "VerifyGlobs.cmake"; - if (cmSystemTools::FileExists(globVerifyScript)) { - std::vector<std::string> args; - this->ReadListFile(args, globVerifyScript); - } - } if (!cmakeCheckStampList(stampList)) { // Correctly initialize the home (=source) and home output (=binary)
diff --git a/Source/cmake.h b/Source/cmake.h index ccf5154..2d17ed2 100644 --- a/Source/cmake.h +++ b/Source/cmake.h
@@ -53,6 +53,7 @@ class cmMessenger; class cmVariableWatch; struct cmBuildOptions; +struct cmGlobCacheEntry; /** \brief Represents a cmake invocation. * @@ -351,12 +352,10 @@ bool DoWriteGlobVerifyTarget() const; std::string const& GetGlobVerifyScript() const; std::string const& GetGlobVerifyStamp() const; - void AddGlobCacheEntry(bool recurse, bool listDirectories, - bool followSymlinks, const std::string& relative, - const std::string& expression, - const std::vector<std::string>& files, + void AddGlobCacheEntry(const cmGlobCacheEntry& entry, const std::string& variable, cmListFileBacktrace const& bt); + std::vector<cmGlobCacheEntry> GetGlobCacheEntries() const; /** * Get the system information and write it to the file specified
diff --git a/Source/kwsys/CONTRIBUTING.rst b/Source/kwsys/CONTRIBUTING.rst index ebd3ed3..d62afa5 100644 --- a/Source/kwsys/CONTRIBUTING.rst +++ b/Source/kwsys/CONTRIBUTING.rst
@@ -34,7 +34,7 @@ for which we enforce style. The script also has options to format only a subset of files, such as those that are locally modified. -.. _`clang-format`: http://clang.llvm.org/docs/ClangFormat.html +.. _`clang-format`: https://clang.llvm.org/docs/ClangFormat.html .. _`.clang-format`: .clang-format .. _`clang-format.bash`: clang-format.bash
diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx index 50171dd..61703ad 100644 --- a/Source/kwsys/CommandLineArguments.cxx +++ b/Source/kwsys/CommandLineArguments.cxx
@@ -132,7 +132,7 @@ // Does the argument match to any we know about? for (it = this->Internals->Callbacks.begin(); - it != this->Internals->Callbacks.end(); it++) { + it != this->Internals->Callbacks.end(); ++it) { const CommandLineArguments::Internal::String& parg = it->first; CommandLineArgumentsCallbackStructure* cs = &it->second; if (cs->ArgumentType == CommandLineArguments::NO_ARGUMENT || @@ -467,7 +467,7 @@ MapArgs mp; MapArgs::iterator mpit, smpit; for (it = this->Internals->Callbacks.begin(); - it != this->Internals->Callbacks.end(); it++) { + it != this->Internals->Callbacks.end(); ++it) { CommandLineArgumentsCallbackStructure* cs = &(it->second); mpit = mp.find(cs->Help); if (mpit != mp.end()) { @@ -478,14 +478,14 @@ } } for (it = this->Internals->Callbacks.begin(); - it != this->Internals->Callbacks.end(); it++) { + it != this->Internals->Callbacks.end(); ++it) { CommandLineArgumentsCallbackStructure* cs = &(it->second); mpit = mp.find(cs->Help); if (mpit != mp.end()) { mpit->second.insert(it->first); smpit = mp.find(it->first); CommandLineArguments::Internal::SetOfStrings::iterator sit; - for (sit = smpit->second.begin(); sit != smpit->second.end(); sit++) { + for (sit = smpit->second.begin(); sit != smpit->second.end(); ++sit) { mpit->second.insert(*sit); } mp.erase(smpit); @@ -496,9 +496,9 @@ // Find the length of the longest string CommandLineArguments::Internal::String::size_type maxlen = 0; - for (mpit = mp.begin(); mpit != mp.end(); mpit++) { + for (mpit = mp.begin(); mpit != mp.end(); ++mpit) { CommandLineArguments::Internal::SetOfStrings::iterator sit; - for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) { + for (sit = mpit->second.begin(); sit != mpit->second.end(); ++sit) { CommandLineArguments::Internal::String::size_type clen = sit->size(); switch (this->Internals->Callbacks[*sit].ArgumentType) { case CommandLineArguments::NO_ARGUMENT: @@ -524,9 +524,9 @@ maxlen += 4; // For the space before and after the option // Print help for each option - for (mpit = mp.begin(); mpit != mp.end(); mpit++) { + for (mpit = mp.begin(); mpit != mp.end(); ++mpit) { CommandLineArguments::Internal::SetOfStrings::iterator sit; - for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) { + for (sit = mpit->second.begin(); sit != mpit->second.end(); ++sit) { str << std::endl; std::string argument = *sit; switch (this->Internals->Callbacks[*sit].ArgumentType) {
diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index f239576..f152ca4 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx
@@ -269,7 +269,7 @@ # include <string.h> // PGI with glibc has trouble with dirent and large file support: -// http://www.pgroup.com/userforum/viewtopic.php? +// https://www.pgroup.com/userforum/viewtopic.php? // p=1992&sid=f16167f51964f1a68fe5041b8eb213b6 // Work around the problem by mapping dirent the same way as readdir. # if defined(__PGI) && defined(__GLIBC__)
diff --git a/Source/kwsys/MD5.c b/Source/kwsys/MD5.c index 76995e2..b0faa0e 100644 --- a/Source/kwsys/MD5.c +++ b/Source/kwsys/MD5.c
@@ -52,7 +52,7 @@ This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at - http://www.ietf.org/rfc/rfc1321.txt + https://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index 369ff9a..428d649 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx
@@ -22,10 +22,10 @@ // Consider using these on Win32/Win64 for some of them: // // IsProcessorFeaturePresent -// http://msdn.microsoft.com/en-us/library/ms724482(VS.85).aspx +// https://msdn.microsoft.com/en-us/library/ms724482(VS.85).aspx // // GetProcessMemoryInfo -// http://msdn.microsoft.com/en-us/library/ms683219(VS.85).aspx +// https://msdn.microsoft.com/en-us/library/ms683219(VS.85).aspx #include "kwsysPrivate.h" #include KWSYS_HEADER(SystemInformation.hxx) @@ -4265,7 +4265,7 @@ { #ifdef __CYGWIN__ // _SC_PAGE_SIZE does return the mmap() granularity on Cygwin, - // see http://cygwin.com/ml/cygwin/2006-06/msg00350.html + // see https://sourceware.org/legacy-ml/cygwin/2006-06/msg00350.html // Therefore just use 4096 as the page size of Windows. long m = sysconf(_SC_PHYS_PAGES); if (m < 0) { @@ -4646,7 +4646,7 @@ err = sysctlbyname("hw.machine", &tempBuff, &len, nullptr, 0); if (err == 0) { std::string machineBuf(tempBuff); - if (machineBuf.find_first_of("Power") != std::string::npos) { + if (machineBuf.find("Power") != std::string::npos) { this->ChipID.Vendor = "IBM"; err = kw_sysctlbyname_int32("hw.cputype", &tempInt32); @@ -4660,10 +4660,15 @@ } this->FindManufacturer(); - } else if (machineBuf.find_first_of("arm64") != std::string::npos) { + } else if (machineBuf.find("arm64") != std::string::npos) { this->ChipID.Vendor = "Apple"; this->FindManufacturer(); + + err = kw_sysctlbyname_int32("hw.optional.floatingpoint", &tempInt32); + if (err == 0) { + this->Features.HasFPU = static_cast<bool>(tempInt32); + } } } } else { @@ -4890,7 +4895,7 @@ args.reserve(3 + args_string.size()); args.push_back("kstat"); args.push_back("-p"); - for (auto& i : args_string) { + for (const auto& i : args_string) { args.push_back(i.c_str()); } args.push_back(nullptr);
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 53b55f6..6356616 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx
@@ -1356,6 +1356,75 @@ } #endif +#ifdef _WIN32 +SystemTools::WindowsFileId::WindowsFileId(unsigned long volumeSerialNumber, + unsigned long fileIndexHigh, + unsigned long fileIndexLow) + : m_volumeSerialNumber(volumeSerialNumber) + , m_fileIndexHigh(fileIndexHigh) + , m_fileIndexLow(fileIndexLow) +{ +} + +bool SystemTools::WindowsFileId::operator==(const WindowsFileId& o) const +{ + return (m_volumeSerialNumber == o.m_volumeSerialNumber && + m_fileIndexHigh == o.m_fileIndexHigh && + m_fileIndexLow == o.m_fileIndexLow); +} + +bool SystemTools::WindowsFileId::operator!=(const WindowsFileId& o) const +{ + return !(*this == o); +} +#else +SystemTools::UnixFileId::UnixFileId(dev_t volumeSerialNumber, + ino_t fileSerialNumber, off_t fileSize) + : m_volumeSerialNumber(volumeSerialNumber) + , m_fileSerialNumber(fileSerialNumber) + , m_fileSize(fileSize) +{ +} + +bool SystemTools::UnixFileId::operator==(const UnixFileId& o) const +{ + return (m_volumeSerialNumber == o.m_volumeSerialNumber && + m_fileSerialNumber == o.m_fileSerialNumber && + m_fileSize == o.m_fileSize); +} + +bool SystemTools::UnixFileId::operator!=(const UnixFileId& o) const +{ + return !(*this == o); +} +#endif + +bool SystemTools::GetFileId(const std::string& file, FileId& id) +{ +#ifdef _WIN32 + HANDLE hFile = + CreateFileW(Encoding::ToWide(file).c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + BY_HANDLE_FILE_INFORMATION fiBuf; + GetFileInformationByHandle(hFile, &fiBuf); + CloseHandle(hFile); + id = FileId(fiBuf.dwVolumeSerialNumber, fiBuf.nFileIndexHigh, + fiBuf.nFileIndexLow); + return true; + } else { + return false; + } +#else + struct stat fileStat; + if (stat(file.c_str(), &fileStat) == 0) { + id = FileId(fileStat.st_dev, fileStat.st_ino, fileStat.st_size); + return true; + } + return false; +#endif +} + bool SystemTools::SameFile(const std::string& file1, const std::string& file2) { #ifdef _WIN32 @@ -2867,9 +2936,8 @@ path.reserve(path.size() + userPaths.size()); path.insert(path.end(), userPaths.begin(), userPaths.end()); // now look for the file - std::string tryPath; for (std::string const& p : path) { - tryPath = p; + std::string tryPath = p; if (tryPath.empty() || tryPath.back() != '/') { tryPath += '/'; } @@ -2938,8 +3006,6 @@ const std::vector<std::string>& userPaths, bool no_system_path) { - std::string tryPath; - #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) std::vector<std::string> extensions; // check to see if the name already has a .xxx at @@ -2951,7 +3017,7 @@ // first try with extensions if the os supports them for (std::string const& ext : extensions) { - tryPath = name; + std::string tryPath = name; tryPath += ext; if (SystemTools::FileIsExecutable(tryPath)) { return SystemTools::CollapseFullPath(tryPath); @@ -2988,7 +3054,7 @@ #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) // first try with extensions for (std::string const& ext : extensions) { - tryPath = p; + std::string tryPath = p; tryPath += name; tryPath += ext; if (SystemTools::FileIsExecutable(tryPath)) { @@ -2997,7 +3063,7 @@ } #endif // now try it without them - tryPath = p; + std::string tryPath = p; tryPath += name; if (SystemTools::FileIsExecutable(tryPath)) { return SystemTools::CollapseFullPath(tryPath); @@ -3152,16 +3218,13 @@ #if defined(_WIN32) DWORD attr = GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str()); - if (attr != INVALID_FILE_ATTRIBUTES) { - return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; + return (attr != INVALID_FILE_ATTRIBUTES) && + (attr & FILE_ATTRIBUTE_DIRECTORY); #else struct stat fs; - if (stat(name, &fs) == 0) { - return S_ISDIR(fs.st_mode); + + return (stat(name, &fs) == 0) && S_ISDIR(fs.st_mode); #endif - } else { - return false; - } } bool SystemTools::FileIsExecutable(const std::string& inName) @@ -3226,11 +3289,7 @@ return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str())); #else struct stat fs; - if (lstat(name.c_str(), &fs) == 0) { - return S_ISLNK(fs.st_mode); - } else { - return false; - } + return (lstat(name.c_str(), &fs) == 0) && S_ISLNK(fs.st_mode); #endif } @@ -3248,11 +3307,7 @@ return type == FILE_TYPE_PIPE; #else struct stat fs; - if (lstat(name.c_str(), &fs) == 0) { - return S_ISFIFO(fs.st_mode); - } else { - return false; - } + return (lstat(name.c_str(), &fs) == 0) && S_ISFIFO(fs.st_mode); #endif } @@ -4889,7 +4944,7 @@ #if defined(__VMS) // On VMS we configure the run time C library to be more UNIX like. -// http://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html +// https://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html extern "C" int decc$feature_get_index(char* name); extern "C" int decc$feature_set_value(int index, int mode, int value); static int SetVMSFeature(char* name, int value)
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index 729928e..294ffca 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in
@@ -617,6 +617,52 @@ std::string const& destination); /** + * Object encapsulating a unique identifier for a file + * or directory + */ +#ifdef _WIN32 + class WindowsFileId + { + public: + WindowsFileId() = default; + WindowsFileId(unsigned long volumeSerialNumber, + unsigned long fileIndexHigh, unsigned long fileIndexLow); + + bool operator==(const WindowsFileId& o) const; + bool operator!=(const WindowsFileId& o) const; + + private: + unsigned long m_volumeSerialNumber; + unsigned long m_fileIndexHigh; + unsigned long m_fileIndexLow; + }; + using FileId = WindowsFileId; +#else + class UnixFileId + { + public: + UnixFileId() = default; + UnixFileId(dev_t volumeSerialNumber, ino_t fileSerialNumber, + off_t fileSize); + + bool operator==(const UnixFileId& o) const; + bool operator!=(const UnixFileId& o) const; + + private: + dev_t m_volumeSerialNumber; + ino_t m_fileSerialNumber; + off_t m_fileSize; + }; + using FileId = UnixFileId; +#endif + + /** + * Outputs a FileId for the given file or directory. + * Returns true on success, false on failure + */ + static bool GetFileId(const std::string& file, FileId& id); + + /** * Return true if the two files are the same file */ static bool SameFile(const std::string& file1, const std::string& file2);
diff --git a/Source/kwsys/Terminal.c b/Source/kwsys/Terminal.c index 39081a7..eb6e01b 100644 --- a/Source/kwsys/Terminal.c +++ b/Source/kwsys/Terminal.c
@@ -168,7 +168,7 @@ static int kwsysTerminalStreamIsVT100(FILE* stream, int default_vt100, int default_tty) { - /* Force color according to http://bixense.com/clicolors/ convention. */ + /* Force color according to https://bixense.com/clicolors/ convention. */ { const char* clicolor_force = getenv("CLICOLOR_FORCE"); if (clicolor_force && *clicolor_force && @@ -177,7 +177,7 @@ } } - /* Disable color according to http://bixense.com/clicolors/ convention. */ + /* Disable color according to https://bixense.com/clicolors/ convention. */ { const char* clicolor = getenv("CLICOLOR"); if (clicolor && strcmp(clicolor, "0") == 0) {
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx index 8afcb68..9275043 100644 --- a/Source/kwsys/testSystemTools.cxx +++ b/Source/kwsys/testSystemTools.cxx
@@ -1148,12 +1148,12 @@ static bool CheckURLParsing() { bool ret = true; - std::string url = "http://user:pw@hostname:42/full/url.com"; + std::string url = "https://user:pw@hostname:42/full/url.com"; std::string protocol, username, password, hostname, dataport, database; kwsys::SystemTools::ParseURL(url, protocol, username, password, hostname, dataport, database); - if (protocol != "http" || username != "user" || password != "pw" || + if (protocol != "https" || username != "user" || password != "pw" || hostname != "hostname" || dataport != "42" || database != "full/url.com") { std::cerr << "Incorrect URL parsing" << std::endl;
diff --git a/Tests/Architecture/CMakeLists.txt b/Tests/Architecture/CMakeLists.txt index 3d10ee0..8b3d0af 100644 --- a/Tests/Architecture/CMakeLists.txt +++ b/Tests/Architecture/CMakeLists.txt
@@ -1,30 +1,21 @@ cmake_minimum_required(VERSION 3.5) project(Architecture C) -function(test_for_xcode4 result_var) - set(${result_var} 0 PARENT_SCOPE) - if(APPLE) - execute_process(COMMAND xcodebuild -version - OUTPUT_VARIABLE ov RESULT_VARIABLE rv - ) - if("${rv}" STREQUAL "0" AND ov MATCHES "^Xcode ([0-9]+)\\.") - if(NOT CMAKE_MATCH_1 VERSION_LESS 4) - set(${result_var} 1 PARENT_SCOPE) - endif() - endif() - endif() -endfunction() - -test_for_xcode4(is_xcode4) - -set(arch0 i386) -set(arch1 ppc) - -if(is_xcode4) - # Xcode 4, use modern architectures as defaults - # Arch 'ppc' no longer works: tools no longer available starting with Xcode 4 +if (CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12) + # Since Xcode 12 we have two architectures again: arm64 and x86_64. + set(arch0 x86_64) + set(arch1 arm64) +elseif(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 10) + # Xcode 10 / 11 do not have two supported architectures for the host. + message(STATUS "Skip test x86_64 only") + return() +elseif(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 4) + # Xcode 4 no longer supports ppc, but does support x86_64. set(arch0 i386) set(arch1 x86_64) +else() + set(arch0 i386) + set(arch1 ppc) endif() add_library(foo foo.c) @@ -43,7 +34,6 @@ endif() endif() -message("is_xcode4='${is_xcode4}'") message("archs='${archs}'") message("arch0='${arch0}'") message("arch1='${arch1}'")
diff --git a/Tests/BuildDepends/CMakeLists.txt b/Tests/BuildDepends/CMakeLists.txt index 5ddae83..2fa2bc5 100644 --- a/Tests/BuildDepends/CMakeLists.txt +++ b/Tests/BuildDepends/CMakeLists.txt
@@ -12,6 +12,15 @@ # project is built. set(CMAKE_SUPPRESS_REGENERATION 1) +if(CMAKE_GENERATOR MATCHES "Visual Studio" AND CMAKE_VS_PLATFORM_TOOLSET MATCHES "^(v90|v100|v110|v120|v140)$") + # These toolsets update 'link_depends_no_shared_lib.lib' during rebuild in Release mode. + set(config Debug) +else() + # Some toolsets update 'link_depends_no_shared_lib.pdb' during rebuild in Debug mode. + set(config Release) +endif() +set(CMAKE_TRY_COMPILE_CONFIGURATION "${config}") + # Xcode needs some help with the fancy dependencies in this test. if(XCODE AND XCODE_VERSION VERSION_LESS 5) set(HELP_XCODE 1) @@ -42,11 +51,15 @@ list(APPEND _cmake_options "-DCMAKE_FORCE_DEPFILES=1") -if(NOT CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])") +if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") set(TEST_MULTI3 1) list(APPEND _cmake_options "-DTEST_MULTI3=1") endif() +if (APPLE) + list(APPEND _cmake_options "-DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION}") +endif() + file(MAKE_DIRECTORY ${BuildDepends_BINARY_DIR}/Project) message("Creating Project/foo.cxx") write_file(${BuildDepends_BINARY_DIR}/Project/foo.cxx @@ -113,10 +126,10 @@ # find and save the ninjadep executable set(ninjadep ${BuildDepends_BINARY_DIR}/Project/ninjadep${CMAKE_EXECUTABLE_SUFFIX}) if(EXISTS - "${BuildDepends_BINARY_DIR}/Project/Debug/ninjadep${CMAKE_EXECUTABLE_SUFFIX}" ) - message("found debug") + "${BuildDepends_BINARY_DIR}/Project/${config}/ninjadep${CMAKE_EXECUTABLE_SUFFIX}" ) + message("found ${config}/ninjadep${CMAKE_EXECUTABLE_SUFFIX}") set(ninjadep - "${BuildDepends_BINARY_DIR}/Project/Debug/ninjadep${CMAKE_EXECUTABLE_SUFFIX}") + "${BuildDepends_BINARY_DIR}/Project/${config}/ninjadep${CMAKE_EXECUTABLE_SUFFIX}") endif() message("Running ${ninjadep} ") execute_process(COMMAND ${ninjadep} OUTPUT_VARIABLE out RESULT_VARIABLE runResult) @@ -132,17 +145,17 @@ set(bar ${BuildDepends_BINARY_DIR}/Project/bar${CMAKE_EXECUTABLE_SUFFIX}) if(EXISTS - "${BuildDepends_BINARY_DIR}/Project/Debug/bar${CMAKE_EXECUTABLE_SUFFIX}" ) - message("found debug") + "${BuildDepends_BINARY_DIR}/Project/${config}/bar${CMAKE_EXECUTABLE_SUFFIX}" ) + message("found ${config}/bar${CMAKE_EXECUTABLE_SUFFIX}") set(bar - "${BuildDepends_BINARY_DIR}/Project/Debug/bar${CMAKE_EXECUTABLE_SUFFIX}") + "${BuildDepends_BINARY_DIR}/Project/${config}/bar${CMAKE_EXECUTABLE_SUFFIX}") endif() set(zot ${BuildDepends_BINARY_DIR}/Project/zot${CMAKE_EXECUTABLE_SUFFIX}) if(EXISTS - "${BuildDepends_BINARY_DIR}/Project/Debug/zot${CMAKE_EXECUTABLE_SUFFIX}" ) - message("found debug") + "${BuildDepends_BINARY_DIR}/Project/${config}/zot${CMAKE_EXECUTABLE_SUFFIX}" ) + message("found ${config}/zot${CMAKE_EXECUTABLE_SUFFIX}") set(zot - "${BuildDepends_BINARY_DIR}/Project/Debug/zot${CMAKE_EXECUTABLE_SUFFIX}") + "${BuildDepends_BINARY_DIR}/Project/${config}/zot${CMAKE_EXECUTABLE_SUFFIX}") endif() message("Running ${bar} ") @@ -301,12 +314,12 @@ message(SEND_ERROR "Could not build test project (2)!") endif() if(EXISTS - "${BuildDepends_BINARY_DIR}/Project/Debug/bar${CMAKE_EXECUTABLE_SUFFIX}" ) - message("found debug") + "${BuildDepends_BINARY_DIR}/Project/${config}/bar${CMAKE_EXECUTABLE_SUFFIX}" ) + message("found ${config}/bar${CMAKE_EXECUTABLE_SUFFIX}") endif() if(EXISTS - "${BuildDepends_BINARY_DIR}/Project/Debug/zot${CMAKE_EXECUTABLE_SUFFIX}" ) - message("found debug") + "${BuildDepends_BINARY_DIR}/Project/${config}/zot${CMAKE_EXECUTABLE_SUFFIX}" ) + message("found ${config}/zot${CMAKE_EXECUTABLE_SUFFIX}") endif() message("Running ${ninjadep} ")
diff --git a/Tests/BuildDepends/Project/CMakeLists.txt b/Tests/BuildDepends/Project/CMakeLists.txt index a320d53..2abda2e 100644 --- a/Tests/BuildDepends/Project/CMakeLists.txt +++ b/Tests/BuildDepends/Project/CMakeLists.txt
@@ -1,19 +1,7 @@ cmake_minimum_required(VERSION 2.6) project(testRebuild) -if(APPLE) - set(CMake_TEST_XCODE_VERSION 0) - if(XCODE_VERSION) - set(CMake_TEST_XCODE_VERSION "${XCODE_VERSION}") - else() - execute_process( - COMMAND xcodebuild -version - OUTPUT_VARIABLE _version ERROR_VARIABLE _version_err - ) - if(_version MATCHES "^Xcode ([0-9]+(\\.[0-9]+)*)") - set(CMake_TEST_XCODE_VERSION "${CMAKE_MATCH_1}") - endif() - endif() +if(APPLE AND CMake_TEST_XCODE_VERSION) # only use multi-arch if the sysroot exists on this machine # Ninja needs -M which could not be used with multiple -arch flags if(EXISTS "${CMAKE_OSX_SYSROOT}" AND NOT "${CMAKE_GENERATOR}" MATCHES "Ninja")
diff --git a/Tests/CMakeGUI/CMakeLists.txt b/Tests/CMakeGUI/CMakeLists.txt index 4e8609b..c9f44e9 100644 --- a/Tests/CMakeGUI/CMakeLists.txt +++ b/Tests/CMakeGUI/CMakeLists.txt
@@ -2,6 +2,11 @@ find_package(Qt5Test REQUIRED) +if(MSVC) + # QTBUG-118993: Qt uses deprecated stdext::checked_array_iterator + add_compile_definitions(_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING) +endif() + include_directories( ${CMake_SOURCE_DIR}/Source ${CMake_SOURCE_DIR}/Source/QtDialog
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 2c1d22b..4d43e08 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt
@@ -53,6 +53,7 @@ add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx) target_link_libraries(testUVProcessChainHelper CMakeLib) +target_compile_definitions(testUVProcessChainHelper PRIVATE WIN32_LEAN_AND_MEAN) set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR}) set(testUVProcessChain_ARGS $<TARGET_FILE:testUVProcessChainHelper>)
diff --git a/Tests/CMakeLib/testDebuggerNamedPipe.cxx b/Tests/CMakeLib/testDebuggerNamedPipe.cxx index 1ae3f64..67e1372 100644 --- a/Tests/CMakeLib/testDebuggerNamedPipe.cxx +++ b/Tests/CMakeLib/testDebuggerNamedPipe.cxx
@@ -194,7 +194,7 @@ R"("command" *: *"disconnect".*"success" *: *true.*"type" *: *"response")" }; - for (auto& regexString : expectedResponses) { + for (const auto& regexString : expectedResponses) { cmsys::RegularExpression regex(regexString); if (!regex.find(debuggerResponse)) { std::cout << "Expected response not found: " << regexString << std::endl;
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 72b55e0..730a7b4 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt
@@ -1,5 +1,13 @@ -# a macro for tests that have a simple format where the name matches the -# directory and project +# Creates a test named <NAME> that runs ctest --build-and-test on the +# subdirectory of this one named by replacing "." with "/" in +# <NAME>. Any arguments following NAME are passed to ctest as a +# --test-command. See the implementation for additional details. +# +# The subdirectory is added to TEST_BUILD_DIRS in the caller's scope. +# +# Requires: the name of the project in the subdirectory is the part of +# <NAME> following the last dot ("."), or all of <NAME> if there is no +# "." in <NAME>. macro(ADD_TEST_MACRO NAME) if(${ARGC} GREATER 1) set(_test_command --test-command ${ARGN}) @@ -40,9 +48,7 @@ endif() # Suppress generator deprecation warnings in test suite. -if(CMAKE_GENERATOR MATCHES "^Visual Studio 9 2008") - set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS9} OFF)") -elseif(CMAKE_GENERATOR MATCHES "^Visual Studio 12 2013") +if(CMAKE_GENERATOR MATCHES "^Visual Studio 12 2013") set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS12} OFF)") else() set(TEST_WARN_VS_CODE "") @@ -74,9 +80,6 @@ set(CMake_TEST_DEVENV "") if(CMAKE_VS_DEVENV_COMMAND) set(CMake_TEST_DEVENV "${CMAKE_VS_DEVENV_COMMAND}") - elseif(CMAKE_GENERATOR MATCHES "Visual Studio 9 " AND - NOT CMAKE_MAKE_PROGRAM MATCHES "[mM][sS][bB][uU][iI][lL][dD]\\.[eE][xX][eE]") - set(CMake_TEST_DEVENV "${CMAKE_MAKE_PROGRAM}") endif() if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") @@ -172,10 +175,7 @@ set(CPACK_BINARY_DEB OFF) endif() - # Look for NuGet to use for tests. - find_program(NUGET_EXECUTABLE NAMES NuGet nuget) - - if(NUGET_EXECUTABLE) + if(CMake_TEST_CPACK_NUGET) set(CPACK_BINARY_NUGET ON) else() set(CPACK_BINARY_NUGET OFF) @@ -299,25 +299,21 @@ "Should the tests that use '--build-target package' be run?" ON) mark_as_advanced(CTEST_TEST_CPACK) - set(CTEST_TEST_OSX_ARCH 0) set(CMake_TEST_XCODE_VERSION 0) if(APPLE) - set(CTEST_TEST_OSX_ARCH 1) if(XCODE_VERSION) set(CMake_TEST_XCODE_VERSION "${XCODE_VERSION}") else() execute_process( COMMAND xcodebuild -version - OUTPUT_VARIABLE _version ERROR_VARIABLE _version_err + OUTPUT_VARIABLE _version + ERROR_VARIABLE _stderr + RESULT_VARIABLE _failed ) - if(_version MATCHES "^Xcode ([0-9]+(\\.[0-9]+)*)") + if(NOT _failed AND _version MATCHES "^Xcode ([0-9]+(\\.[0-9]+)*)") set(CMake_TEST_XCODE_VERSION "${CMAKE_MATCH_1}") endif() endif() - if(NOT CMake_TEST_XCODE_VERSION VERSION_LESS 10) - # Since Xcode 10 we do not have two supported architectures for the host. - set(CTEST_TEST_OSX_ARCH 0) - endif() if(CMAKE_OSX_SYSROOT) execute_process( COMMAND xcodebuild -sdk ${CMAKE_OSX_SYSROOT} -version ProductName @@ -392,7 +388,22 @@ ADD_TEST_MACRO(TryCompile TryCompile) ADD_TEST_MACRO(SystemInformation SystemInformation) ADD_TEST_MACRO(MathTest MathTest) + + string(REPLACE ";" "$<SEMICOLON>" TEST_STDS_C "${CMake_TEST_C_STANDARDS}") + string(REPLACE ";" "$<SEMICOLON>" TEST_STDS_CXX "${CMake_TEST_CXX_STANDARDS}") + 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_C_STANDARDS=${TEST_STDS_C} + -DCMake_TEST_CXX_STANDARDS=${TEST_STDS_CXX} + -DCMake_TEST_CUDA=${CMake_TEST_CUDA} + -DCMake_TEST_CUDA_STANDARDS=${TEST_STDS_CUDA} + -DCMake_TEST_HIP=${CMake_TEST_HIP} + -DCMake_TEST_HIP_STANDARDS=${TEST_STDS_HIP} + ) ADD_TEST_MACRO(CompileFeatures CompileFeatures) + set_property(TEST CompileFeatures APPEND PROPERTY LABELS "CUDA" "HIP") + ADD_TEST_MACRO(CMakeCommands.target_compile_features) if(CMake_TEST_RESOURCES) @@ -465,7 +476,7 @@ add_subdirectory(ObjCXX) endif() - if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])") + if(${CMAKE_GENERATOR} MATCHES "Visual Studio") ADD_TEST_MACRO(CSharpOnly CSharpOnly) if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") ADD_TEST_MACRO(CSharpLinkToCxx CSharpLinkToCxx) @@ -498,6 +509,10 @@ ADD_TEST_MACRO(ReturnTest ReturnTest) ADD_TEST_MACRO(Properties Properties) ADD_TEST_MACRO(Assembler HelloAsm) + # relies on Linux syscall interface + if(CMake_TEST_ASM_NASM AND CMAKE_SYSTEM_NAME MATCHES "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + ADD_TEST_MACRO(NasmOnly NasmOnly) + endif() ADD_TEST_MACRO(SourceGroups SourceGroups) ADD_TEST_MACRO(Preprocess Preprocess) set(ExportImport_BUILD_OPTIONS -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM} @@ -524,6 +539,7 @@ set_property(TEST CompileOptions APPEND PROPERTY LABELS "Fortran") ADD_TEST_MACRO(CompatibleInterface CompatibleInterface) + ADD_TEST_MACRO(CustomTransitiveProperties CustomTransitiveProperties) ADD_TEST_MACRO(AliasTarget AliasTarget) ADD_TEST_MACRO(StagingPrefix StagingPrefix) ADD_TEST_MACRO(ImportedSameName ImportedSameName) @@ -562,10 +578,13 @@ if("${CMAKE_GENERATOR}" MATCHES "Make") ADD_TEST_MACRO(Policy0002 Policy0002) endif() - if(CTEST_TEST_OSX_ARCH) + if(CMake_TEST_XCODE_VERSION) + set(Architecture_BUILD_OPTIONS -DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION}) ADD_TEST_MACRO(Architecture Architecture) set_tests_properties(Architecture PROPERTIES PASS_REGULAR_EXPRESSION "(file is not of required architecture|does not match cputype|not the architecture being linked|but attempting to link with file built for)") + set_property(TEST Architecture APPEND PROPERTY + PASS_REGULAR_EXPRESSION "Skip test x86_64 only|found architecture '.*', required architecture '.*'") endif() list(APPEND TEST_BUILD_DIRS ${CMake_TEST_INSTALL_PREFIX}) @@ -933,6 +952,8 @@ "${CMake_BINARY_DIR}/Tests/BuildDepends" ${build_generator_args} --build-project BuildDepends + --build-options + "-DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION}" ) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BuildDepends") @@ -974,36 +995,6 @@ endif() endif() - # On Windows run the CPackWiXGenerator test - # if the WiX Toolset seems to be available - if(WIN32) - file(TO_CMAKE_PATH "$ENV{WIX}" WIX_ROOT) - - find_program(WIX_LIGHT_EXECUTABLE light - PATHS "${WIX_ROOT}/bin" - DOC "WiX Toolset light.exe location") - - if(WIX_LIGHT_EXECUTABLE) - add_test(CPackWiXGenerator ${CMAKE_CTEST_COMMAND} - -C \${CTEST_CONFIGURATION_TYPE} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/CPackWiXGenerator" - "${CMake_BINARY_DIR}/Tests/CPackWiXGenerator" - ${build_generator_args} - --build-project CPackWiXGenerator - --build-options - --test-command ${CMAKE_CMAKE_COMMAND} - "-DCPackWiXGenerator_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackWiXGenerator" - "-Dno_verify:BOOL=${CMake_TEST_WIX_NO_VERIFY}" - "-Dconfig=\${CTEST_CONFIGURATION_TYPE}" - -P "${CMake_SOURCE_DIR}/Tests/CPackWiXGenerator/RunCPackVerifyResult.cmake") - - set_property(TEST CPackWiXGenerator PROPERTY - ATTACHED_FILES_ON_FAIL - "${CMake_BINARY_DIR}/Tests/CPackWiXGenerator/_CPack_Packages/win32/WIX/wix.log") - endif() - endif() - # On Windows run the CPackInnoSetupGenerator test if(WIN32 AND CMake_TEST_CPACK_INNOSETUP) add_test(CPackInnoSetupGenerator ${CMAKE_CTEST_COMMAND} @@ -1134,9 +1125,7 @@ if(CPACK_BINARY_DEB) list(APPEND ACTIVE_CPACK_GENERATORS DEB) endif() - # Check whether if NuGet command is found - # before adding NuGet tests - if(CPACK_BINARY_NUGET) + if(CMake_TEST_CPACK_NUGET) list(APPEND ACTIVE_CPACK_GENERATORS NUGET) set(CPACK_GENERATOR_STRING_NUGET NuGet) endif() @@ -1211,13 +1200,17 @@ else() unset(SHLIBDEPS_EXECUTABLE_VERSION) endif() + # Check if distro has symbols or shlibs data + file(GLOB SHLIBS_FILES_EXIST "/var/lib/dpkg/info/*.shlibs" "/var/lib/dpkg/info/*.symbols") if(NOT SHLIBDEPS_EXECUTABLE_VERSION VERSION_LESS 1.19 OR (NOT SHLIBDEPS_EXECUTABLE_VERSION VERSION_LESS 1.17 AND NOT CMAKE_BINARY_DIR MATCHES ".*[ ].*")) list(APPEND DEB_CONFIGURATIONS_TO_TEST "shlibdeps-with-private-lib-failure" - "shlibdeps-with-private-lib-success") + "shlibdeps-with-private-lib-success" + "shlibdeps-with-ORIGIN-RPATH-failure") + if(SHLIBS_FILES_EXIST) + list(APPEND DEB_CONFIGURATIONS_TO_TEST "shlibdeps-with-ORIGIN-RPATH-success") + endif() endif() - # Check if distro has symbols or shlibs data - file(GLOB SHLIBS_FILES_EXIST "/var/lib/dpkg/info/*.shlibs" "/var/lib/dpkg/info/*.symbols") if(SHLIBS_FILES_EXIST) list(APPEND DEB_CONFIGURATIONS_TO_TEST "components-depend2") endif() @@ -1473,6 +1466,7 @@ _mod IN ITEMS ALSA + Backtrace BLAS Boost BZip2 @@ -1559,7 +1553,7 @@ set(CMake_TEST_FindPython2 TRUE) set(CMake_TEST_FindPython3 TRUE) endif() - if(CMake_TEST_FindPython_SABIMOdule) + if(CMake_TEST_FindPython_SABIModule) set(CMake_TEST_FindPython2_SABIModule TRUE) set(CMake_TEST_FindPython3_SABIModule TRUE) endif() @@ -2222,8 +2216,6 @@ endif() if(MSVC AND NOT MSVC_VERSION LESS 1310 - AND (NOT CMAKE_GENERATOR MATCHES "Visual Studio 9 " - OR CMAKE_SIZEOF_VOID_P EQUAL 4) AND (NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64") ) ADD_TEST_MACRO(VSMASM VSMASM) @@ -2234,8 +2226,7 @@ ADD_TEST_MACRO(SBCS SBCS) endif() - if(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio 9 " - AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90" + if(NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90" AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") ADD_TEST_MACRO(VSWindowsFormsResx VSWindowsFormsResx) ADD_TEST_MACRO(VSManagedCustomCommand) @@ -2386,7 +2377,7 @@ endif() endif() - if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])" AND nasm) + if(CMAKE_GENERATOR MATCHES "Visual Studio" AND nasm) ADD_TEST_MACRO(VSNASM VSNASM) endif()
diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt index ea06464..727c7fc 100644 --- a/Tests/CMakeOnly/CMakeLists.txt +++ b/Tests/CMakeOnly/CMakeLists.txt
@@ -54,7 +54,7 @@ add_CMakeOnly_test(CompilerIdFortran) set_property(TEST CMakeOnly.CompilerIdFortran APPEND PROPERTY LABELS "Fortran") endif() -if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])") +if(CMAKE_GENERATOR MATCHES "Visual Studio") add_CMakeOnly_test(CompilerIdCSharp) endif()
diff --git a/Tests/CMakeTests/FileTest.cmake.in b/Tests/CMakeTests/FileTest.cmake.in index 71cb3db..7b76700 100644 --- a/Tests/CMakeTests/FileTest.cmake.in +++ b/Tests/CMakeTests/FileTest.cmake.in
@@ -13,13 +13,13 @@ set(Copy-NoFile-RESULT 1) set(Copy-NoFile-STDERR "COPY cannot find.*/does_not_exist\\.txt") set(Glob-NoArg-RESULT 1) -set(Glob-NoArg-STDERR "file must be called with at least two arguments") +set(Glob-NoArg-STDERR "file GLOB must be called with at least one additional argument\\.") set(Make_Directory-NoArg-RESULT 1) set(Make-Directory-NoArg-STDERR "file must be called with at least two arguments") set(MD5-NoFile-RESULT 1) set(MD5-NoFile-STDERR "file MD5 failed to read file") set(MD5-BadArg1-RESULT 1) -set(MD5-BadArg1-STDERR "file must be called with at least two arguments") +set(MD5-BadArg1-STDERR "file MD5 requires a file name and output variable") set(MD5-BadArg2-RESULT 1) set(MD5-BadArg2-STDERR "file MD5 requires a file name and output variable") set(MD5-BadArg4-RESULT 1)
diff --git a/Tests/CPackComponentsDEB/CMakeLists.txt b/Tests/CPackComponentsDEB/CMakeLists.txt index b2e2106..a3eb337 100644 --- a/Tests/CPackComponentsDEB/CMakeLists.txt +++ b/Tests/CPackComponentsDEB/CMakeLists.txt
@@ -5,6 +5,10 @@ # which supports CPack components. cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +# Make sure to properly escape RPATH/RUNPATH entries. +if (POLICY CMP0095) + cmake_policy(SET CMP0095 NEW) +endif() project(CPackComponentsDEB VERSION 1.0.3) # Use GNUInstallDirs in order to enforce lib64 if needed @@ -29,6 +33,14 @@ target_link_libraries(mylibapp3 myprivatelib) endif() +if (CPackDEBConfiguration MATCHES "shlibdeps-with-ORIGIN-RPATH") + add_subdirectory("subdir") + add_executable(mylibapp4 mylibapp.cpp) + target_compile_definitions(mylibapp4 PRIVATE -DSHLIBDEPS_OTHER) + target_link_libraries(mylibapp4 PUBLIC myotherlib) + set_target_properties(mylibapp4 PROPERTIES INSTALL_RPATH "\${ORIGIN};$ORIGIN/../lib") +endif() + # Create installation targets. Note that we put each kind of file # into a different component via COMPONENT. These components will # be used to create the installation components. @@ -39,17 +51,28 @@ install(TARGETS mylibapp RUNTIME - DESTINATION bin + DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT applications) install(FILES mylib.h - DESTINATION include - COMPONENT headers) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT headers) if (CPackDEBConfiguration MATCHES "shlibdeps-with-private-lib") install(TARGETS mylibapp3 RUNTIME - DESTINATION bin + DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT applications) +endif() + +if (CPackDEBConfiguration MATCHES "shlibdeps-with-ORIGIN-RPATH") + install(TARGETS myotherlib + LIBRARY + DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT libraries) + install(TARGETS mylibapp4 + RUNTIME + DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT applications) endif()
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in new file mode 100644 index 0000000..0794fbd --- /dev/null +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in
@@ -0,0 +1,23 @@ +# +# Activate component packaging +# + +if(CPACK_GENERATOR MATCHES "DEB") + set(CPACK_DEB_COMPONENT_INSTALL "ON") + set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS "ON") +endif() + +# +# Choose grouping way +# +#set(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE) +#set(CPACK_COMPONENTS_GROUPING) +set(CPACK_COMPONENTS_IGNORE_GROUPS 1) +#set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) + +# we set shlibdeps to on +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + +# setting dependencies +# Note: We explicitly do NOT declare dependency to "libraries" component! +#set(CPACK_COMPONENT_APPLICATIONS_DEPENDS "libraries")
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in new file mode 100644 index 0000000..81d6c7b --- /dev/null +++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in
@@ -0,0 +1,22 @@ +# +# Activate component packaging +# + +if(CPACK_GENERATOR MATCHES "DEB") + set(CPACK_DEB_COMPONENT_INSTALL "ON") + set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS "ON") +endif() + +# +# Choose grouping way +# +#set(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE) +#set(CPACK_COMPONENTS_GROUPING) +set(CPACK_COMPONENTS_IGNORE_GROUPS 1) +#set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) + +# we set shlibdeps to on +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + +# setting dependencies +set(CPACK_COMPONENT_APPLICATIONS_DEPENDS "libraries")
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake new file mode 100644 index 0000000..dddcb4a --- /dev/null +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake
@@ -0,0 +1,19 @@ +if(NOT CPackComponentsDEB_SOURCE_DIR) + message(FATAL_ERROR "CPackComponentsDEB_SOURCE_DIR not set") +endif() + +include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) + + +set(actual_output) +run_cpack(actual_output + CPack_output + CPack_error + EXPECT_FAILURE + CONFIG_ARGS ${config_args} + CONFIG_VERBOSE ${config_verbose}) + +string(REGEX MATCH "dpkg-shlibdeps: error: (cannot|couldn't) find[ \n\t]+library[ \n\t]+libmyotherlib.so.1[ \n\t]+needed[ \n\t]+by[ \n\t]+./usr/bin/mylibapp4" expected_error ${CPack_error}) +if(NOT expected_error) + message(FATAL_ERROR "Did not get the expected error-message!") +endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake new file mode 100644 index 0000000..6eff3db --- /dev/null +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake
@@ -0,0 +1,75 @@ +if(NOT CPackComponentsDEB_SOURCE_DIR) + message(FATAL_ERROR "CPackComponentsDEB_SOURCE_DIR not set") +endif() + +include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) + + + +# requirements + +# debian now produces lower case names +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb") +set(expected_count 3) + + +set(actual_output) +run_cpack(actual_output + CPack_output + CPack_error + EXPECTED_FILE_MASK "${expected_file_mask}" + CONFIG_ARGS ${config_args} + CONFIG_VERBOSE ${config_verbose}) + +message(STATUS "expected_count='${expected_count}'") +message(STATUS "expected_file_mask='${expected_file_mask}'") +message(STATUS "actual_output_files='${actual_output}'") + +if(NOT actual_output) + message(FATAL_ERROR "error: expected_files do not exist: CPackComponentsDEB test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error}") +endif() + +list(LENGTH actual_output actual_count) +message(STATUS "actual_count='${actual_count}'") +if(NOT actual_count EQUAL expected_count) + message(FATAL_ERROR "error: expected_count=${expected_count} does not match actual_count=${actual_count}: CPackComponents test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error})") +endif() + + +# dpkg-deb checks for the summary of the packages +find_program(DPKGDEB_EXECUTABLE dpkg-deb) +if(DPKGDEB_EXECUTABLE) + set(dpkgdeb_output_errors_all "") + foreach(_f IN LISTS actual_output) + + # extracts the metadata from the package + run_dpkgdeb(dpkg_output + FILENAME ${_f} + ) + + dpkgdeb_return_specific_metaentry(dpkg_package_name + DPKGDEB_OUTPUT "${dpkg_output}" + METAENTRY "Package:") + + message(STATUS "package='${dpkg_package_name}'") + + if(dpkg_package_name STREQUAL "mylib-applications") + # pass + elseif(dpkg_package_name STREQUAL "mylib-headers") + # pass + elseif(dpkg_package_name STREQUAL "mylib-libraries") + # pass + else() + set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all} + "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n") + endif() + + endforeach() + + + if(NOT dpkgdeb_output_errors_all STREQUAL "") + message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}") + endif() +else() + message("dpkg-deb executable not found - skipping dpkg-deb test") +endif()
diff --git a/Tests/CPackComponentsDEB/mylibapp.cpp b/Tests/CPackComponentsDEB/mylibapp.cpp index bb45831..6d63803 100644 --- a/Tests/CPackComponentsDEB/mylibapp.cpp +++ b/Tests/CPackComponentsDEB/mylibapp.cpp
@@ -1,13 +1,13 @@ -#ifndef SHLIBDEPS_PRIVATE +#if defined SHLIBDEPS_OTHER -# include "mylib.h" +# include "subdir/myotherlib.h" int main() { - mylib_function(); + myotherlib_function(); } -#else +#elif defined SHLIBDEPS_PRIVATE # include "shlibdeps-with-private-lib/myprivatelib.h" @@ -16,4 +16,13 @@ myprivatelib_function(); } +#else + +# include "mylib.h" + +int main() +{ + mylib_function(); +} + #endif
diff --git a/Tests/CPackComponentsDEB/subdir/CMakeLists.txt b/Tests/CPackComponentsDEB/subdir/CMakeLists.txt new file mode 100644 index 0000000..71b3fdd --- /dev/null +++ b/Tests/CPackComponentsDEB/subdir/CMakeLists.txt
@@ -0,0 +1,5 @@ +add_library(myotherlib SHARED myotherlib.cpp) +set_target_properties( myotherlib PROPERTIES + VERSION 1.2.3 + SOVERSION 1 +)
diff --git a/Tests/CPackComponentsDEB/subdir/myotherlib.cpp b/Tests/CPackComponentsDEB/subdir/myotherlib.cpp new file mode 100644 index 0000000..eec270c --- /dev/null +++ b/Tests/CPackComponentsDEB/subdir/myotherlib.cpp
@@ -0,0 +1,8 @@ +#include "myotherlib.h" + +#include "stdio.h" + +void myotherlib_function() +{ + printf("This is myotherlib"); +}
diff --git a/Tests/CPackComponentsDEB/subdir/myotherlib.h b/Tests/CPackComponentsDEB/subdir/myotherlib.h new file mode 100644 index 0000000..71848a0 --- /dev/null +++ b/Tests/CPackComponentsDEB/subdir/myotherlib.h
@@ -0,0 +1 @@ +void myotherlib_function();
diff --git a/Tests/CPackNSISGenerator/CMakeLists.txt b/Tests/CPackNSISGenerator/CMakeLists.txt index 5d6320b..cb0b13d 100644 --- a/Tests/CPackNSISGenerator/CMakeLists.txt +++ b/Tests/CPackNSISGenerator/CMakeLists.txt
@@ -10,6 +10,26 @@ LIBRARY DESTINATION . BUNDLE DESTINATION .) +# Component that is a reserved name on Windows. +# See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file +install( + DIRECTORY . + DESTINATION txt + COMPONENT CON + FILES_MATCHING PATTERN *.txt) +# Component name that is similar to a reserved name on Windows. +install( + DIRECTORY . + DESTINATION txt + COMPONENT Console + FILES_MATCHING PATTERN *.txt) +# Component name that is strongly discouraged on Windows. +install( + DIRECTORY . + DESTINATION txt + COMPONENT EndsWithDot. + FILES_MATCHING PATTERN *.txt) + set(CPACK_NSIS_MUI_HEADERIMAGE "${PROJECT_SOURCE_DIR}\\\\header-image.bmp") set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}\\\\header-icon.bmp") set(CPACK_NSIS_MUI_ICON "${PROJECT_SOURCE_DIR}\\\\install.ico")
diff --git a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake index 31a2560..3e4d7b4 100644 --- a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake +++ b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
@@ -69,3 +69,24 @@ else() message(FATAL_ERROR "License 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") +if("${output_index}" EQUAL "-1") + message(FATAL_ERROR "CON component reserved directory name not found as _CON in the project") +endif() + +file(STRINGS "${project_file}" line REGEX [[\\Console\\]]) +string(FIND "${line}" [[\Console\]] output_index) +message(STATUS "Found Console component directory name as Console") +if("${output_index}" EQUAL "-1") + message(FATAL_ERROR "Console component directory name not found as Console in the project") +endif() + +file(STRINGS "${project_file}" line REGEX [[\\EndsWithDot._\\]]) +string(FIND "${line}" [[\EndsWithDot._\]] output_index) +message(STATUS "Found EndsWithDot. component directory name as EndsWithDot._") +if("${output_index}" EQUAL "-1") + message(FATAL_ERROR "EndsWithDot. component directory name not found as EndsWithDot._ in the project") +endif()
diff --git a/Tests/CPackWiXGenerator/CMakeLists.txt b/Tests/CPackWiXGenerator/CMakeLists.txt deleted file mode 100644 index 33fdc5e..0000000 --- a/Tests/CPackWiXGenerator/CMakeLists.txt +++ /dev/null
@@ -1,119 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(CPackWiXGenerator) - -add_library(mylib mylib.cpp) - -add_executable(my-libapp mylibapp.cpp) -target_link_libraries(my-libapp mylib) - -add_executable(my-other-app myotherapp.cpp) - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/empty) -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/empty - DESTINATION extras - COMPONENT extras) - -install(TARGETS mylib - ARCHIVE - DESTINATION lib - COMPONENT libraries) - -install(TARGETS my-libapp - RUNTIME - DESTINATION bin - COMPONENT applications) - -install(TARGETS my-other-app - RUNTIME - DESTINATION bin - COMPONENT applications2) - -install(FILES mylib.h "file with spaces.h" - DESTINATION include - COMPONENT headers) - -set(CPACK_GENERATOR "WIX") - -set(CPACK_PACKAGE_NAME "MyLib") -set(CPACK_PACKAGE_VENDOR "CMake.org") -set(CPACK_PACKAGE_CONTACT "somebody@cmake.org") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY - "MyLib - CPack Component Installation Example") - -set(CPACK_PACKAGE_VERSION_MAJOR "1") -set(CPACK_PACKAGE_VERSION_MINOR "0") -set(CPACK_PACKAGE_VERSION_PATCH "0") -set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example") - -set(CPACK_WIX_UPGRADE_GUID "BF20CE5E-7F7C-401D-8F7C-AB45E8D170E6") -set(CPACK_WIX_UNINSTALL "1") - -# Support non-interactive sessions (like CI). -set(CPACK_WIX_LIGHT_EXTRA_FLAGS "-sval") - -set(CPACK_PACKAGE_EXECUTABLES - "my-libapp" "CPack WiX Test" - "my-other-app" "Second CPack WiX Test" -) - -set(CPACK_CREATE_DESKTOP_LINKS - "my-libapp" - "my-other-app" -) - -set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/patch.xml") - -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.txt") - -set(CPACK_WIX_PROPERTY_ARPCOMMENTS "My Custom ARPCOMMENTS") -set(CPACK_WIX_PROPERTY_ARPHELPLINK "https://cmake.org") - -include(CPack) - -cpack_add_install_type(Full DISPLAY_NAME "Everything") -cpack_add_install_type(Developer) - -cpack_add_component_group(Runtime) - -cpack_add_component_group(Development - EXPANDED - DESCRIPTION "All of the tools you'll ever need to develop software") - -cpack_add_component(extras - DISPLAY_NAME "Extras" - DESCRIPTION "Extras" - GROUP Runtime - INSTALL_TYPES Full) - -cpack_add_component(applications - REQUIRED - DISPLAY_NAME "MyLib Application" - DESCRIPTION "An extremely useful application that makes use of MyLib" - GROUP Runtime - INSTALL_TYPES Full) - -cpack_add_component(applications2 - DISPLAY_NAME "MyLib Extra Application" - DESCRIPTION "Another extremely useful application that makes use of MyLib" - GROUP Runtime - INSTALL_TYPES Full) - -cpack_add_component(documentation - DISPLAY_NAME "MyLib Documentation" - DESCRIPTION "The extensive suite of MyLib Application documentation files" - GROUP Runtime - INSTALL_TYPES Full) - -cpack_add_component(libraries - DISPLAY_NAME "Libraries" - DESCRIPTION "Static libraries used to build programs with MyLib" - GROUP Development - INSTALL_TYPES Developer Full) - -cpack_add_component(headers - DISPLAY_NAME "C++ Headers" - DESCRIPTION "C/C++ header files for use with MyLib" - GROUP Development - DEPENDS libraries - INSTALL_TYPES Developer Full)
diff --git a/Tests/CPackWiXGenerator/RunCPackVerifyResult.cmake b/Tests/CPackWiXGenerator/RunCPackVerifyResult.cmake deleted file mode 100644 index c549e61..0000000 --- a/Tests/CPackWiXGenerator/RunCPackVerifyResult.cmake +++ /dev/null
@@ -1,79 +0,0 @@ -message(STATUS "=============================================================") -message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)") -message(STATUS "") - -if(NOT CPackWiXGenerator_BINARY_DIR) - message(FATAL_ERROR "CPackWiXGenerator_BINARY_DIR not set") -endif() - -message(STATUS "CMAKE_COMMAND: ${CMAKE_COMMAND}") -message(STATUS "CMAKE_CPACK_COMMAND: ${CMAKE_CPACK_COMMAND}") -message(STATUS "CPackWiXGenerator_BINARY_DIR: ${CPackWiXGenerator_BINARY_DIR}") - -if(config) - set(_C_config -C ${config}) -endif() - -execute_process(COMMAND "${CMAKE_CPACK_COMMAND}" - ${_C_config} - RESULT_VARIABLE CPack_result - OUTPUT_VARIABLE CPack_output - ERROR_VARIABLE CPack_error - WORKING_DIRECTORY "${CPackWiXGenerator_BINARY_DIR}") - -if(CPack_result) - message(FATAL_ERROR "CPack execution went wrong!, CPack_output=${CPack_output}, CPack_error=${CPack_error}") -else () - message(STATUS "CPack_output=${CPack_output}") -endif() - -set(expected_file_mask "*.msi") -file(GLOB installer_file "${expected_file_mask}") - -message(STATUS "installer_file='${installer_file}'") -message(STATUS "expected_file_mask='${expected_file_mask}'") - -if(NOT installer_file) - message(FATAL_ERROR "installer_file does not exist.") -endif() - -function(run_wix_command command) - file(TO_CMAKE_PATH "$ENV{WIX}" WIX_ROOT) - set(WIX_PROGRAM "${WIX_ROOT}/bin/${command}.exe") - - if(NOT EXISTS "${WIX_PROGRAM}") - message(FATAL_ERROR "Failed to find WiX Tool: ${WIX_PROGRAM}") - endif() - - message(STATUS "Running WiX Tool: ${command} ${ARGN}") - - execute_process(COMMAND "${WIX_PROGRAM}" ${ARGN} - RESULT_VARIABLE WIX_result - OUTPUT_VARIABLE WIX_output - ERROR_VARIABLE WIX_output - WORKING_DIRECTORY "${CPackWiXGenerator_BINARY_DIR}") - - message(STATUS "${command} Output: \n${WIX_output}") - - if(WIX_result) - message(FATAL_ERROR "WiX ${command} failed: ${WIX_result}") - endif() -endfunction() - -file(GLOB WXS_SOURCE_FILES - "${CPackWiXGenerator_BINARY_DIR}/_CPack_Packages/*/WIX/*.wxs") - -if(NOT WXS_SOURCE_FILES) - message(FATAL_ERROR "Failed finding WiX source files to validate.") -endif() - -foreach(WXS_SOURCE_FILE IN LISTS WXS_SOURCE_FILES) - run_wix_command(wixcop "${WXS_SOURCE_FILE}") -endforeach() - -# error SMOK1076 : ICE61: This product should remove only older -# versions of itself. The Maximum version is not less -# than the current product. (1.0.0 1.0.0) -if (NOT no_verify) - run_wix_command(smoke -nologo -wx -sw1076 "${installer_file}") -endif ()
diff --git a/Tests/CompatibleInterface/CMakeLists.txt b/Tests/CompatibleInterface/CMakeLists.txt index 668a97b..5d57ce4 100644 --- a/Tests/CompatibleInterface/CMakeLists.txt +++ b/Tests/CompatibleInterface/CMakeLists.txt
@@ -13,12 +13,14 @@ BOOL_PROP2 BOOL_PROP3 BOOL_PROP4 + BOOL_PROP5 ) set_property(TARGET iface1 APPEND PROPERTY COMPATIBLE_INTERFACE_STRING STRING_PROP1 STRING_PROP2 STRING_PROP3 + STRING_PROP4 ) set_property(TARGET iface1 APPEND PROPERTY COMPATIBLE_INTERFACE_NUMBER_MIN @@ -26,30 +28,40 @@ NUMBER_MIN_PROP2 NUMBER_MIN_PROP3 NUMBER_MIN_PROP4 + NUMBER_MIN_PROP5 + NUMBER_MIN_PROP6 ) set_property(TARGET iface1 APPEND PROPERTY COMPATIBLE_INTERFACE_NUMBER_MAX NUMBER_MAX_PROP1 NUMBER_MAX_PROP2 + NUMBER_MAX_PROP3 + NUMBER_MAX_PROP4 ) set(CMAKE_DEBUG_TARGET_PROPERTIES - BOOL_PROP1 BOOL_PROP2 BOOL_PROP3 BOOL_PROP4 - STRING_PROP1 STRING_PROP2 STRING_PROP3 - NUMBER_MIN_PROP1 NUMBER_MIN_PROP2 NUMBER_MIN_PROP3 NUMBER_MIN_PROP4 - NUMBER_MAX_PROP1 NUMBER_MAX_PROP2 + BOOL_PROP1 BOOL_PROP2 BOOL_PROP3 BOOL_PROP4 BOOL_PROP5 + STRING_PROP1 STRING_PROP2 STRING_PROP3 STRING_PROP4 + NUMBER_MIN_PROP1 NUMBER_MIN_PROP2 NUMBER_MIN_PROP3 NUMBER_MIN_PROP4 NUMBER_MIN_PROP5 NUMBER_MIN_PROP6 + NUMBER_MAX_PROP1 NUMBER_MAX_PROP2 NUMBER_MAX_PROP3 NUMBER_MAX_PROP4 ) set_property(TARGET iface1 PROPERTY INTERFACE_BOOL_PROP1 ON) set_property(TARGET iface1 PROPERTY INTERFACE_BOOL_PROP2 ON) +set_property(TARGET iface1 PROPERTY INTERFACE_BOOL_PROP5 ON) set_property(TARGET iface1 PROPERTY INTERFACE_STRING_PROP1 prop1) set_property(TARGET iface1 PROPERTY INTERFACE_STRING_PROP2 prop2) +set_property(TARGET iface1 PROPERTY INTERFACE_STRING_PROP4 prop4) set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MIN_PROP1 100) set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MIN_PROP2 200) set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MIN_PROP3 0x10) set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MIN_PROP4 0x10) +set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MIN_PROP5 5) +set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MIN_PROP6 6) set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MAX_PROP1 100) set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MAX_PROP2 200) +set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MAX_PROP3 3) +set_property(TARGET iface1 PROPERTY INTERFACE_NUMBER_MAX_PROP4 4) add_executable(CompatibleInterface main.cpp) target_link_libraries(CompatibleInterface iface1) @@ -86,8 +98,48 @@ $<$<STREQUAL:$<TARGET_PROPERTY:NUMBER_MIN_PROP2>,200>:NUMBER_MIN_PROP2=200> $<$<EQUAL:$<TARGET_PROPERTY:NUMBER_MIN_PROP3>,0xA>:NUMBER_MIN_PROP3=0xA> $<$<STREQUAL:$<TARGET_PROPERTY:NUMBER_MIN_PROP4>,0x10>:NUMBER_MIN_PROP4=0x10> + $<$<STREQUAL:$<TARGET_PROPERTY:NUMBER_MIN_PROP5>,5>:NUMBER_MIN_PROP5=5> $<$<STREQUAL:$<TARGET_PROPERTY:NUMBER_MAX_PROP1>,100>:NUMBER_MAX_PROP1=100> $<$<STREQUAL:$<TARGET_PROPERTY:NUMBER_MAX_PROP2>,250>:NUMBER_MAX_PROP2=250> + $<$<STREQUAL:$<TARGET_PROPERTY:NUMBER_MAX_PROP3>,3>:NUMBER_MAX_PROP3=3> + + # Static libraries compute COMPATIBLE_INTERFACE_ properties transitively. + $<$<BOOL:$<TARGET_PROPERTY:static1,BOOL_PROP1>>:STATIC1_BOOL_PROP1> + $<$<STREQUAL:$<TARGET_PROPERTY:static1,STRING_PROP1>,prop1>:STATIC1_STRING_PROP1> + $<$<EQUAL:$<TARGET_PROPERTY:static1,NUMBER_MAX_PROP3>,3>:STATIC1_NUMBER_MAX_PROP3> + $<$<EQUAL:$<TARGET_PROPERTY:static1,NUMBER_MIN_PROP5>,5>:STATIC1_NUMBER_MIN_PROP5> + + # Object libraries do not compute COMPATIBLE_INTERFACE_ properties transitively. + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:object1,BOOL_PROP1>,>>:OBJECT1_BOOL_PROP1> + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:object1,STRING_PROP1>,>>:OBJECT1_STRING_PROP1> + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:object1,NUMBER_MAX_PROP3>,>>:OBJECT1_NUMBER_MAX_PROP3> + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:object1,NUMBER_MIN_PROP5>,>>:OBJECT1_NUMBER_MIN_PROP5> + + # Interface libraries do not compute COMPATIBLE_INTERFACE_ properties transitively. + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:iface3,BOOL_PROP1>,>>:IFACE3_BOOL_PROP1> + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:iface3,STRING_PROP1>,>>:IFACE3_STRING_PROP1> + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:iface3,NUMBER_MAX_PROP3>,>>:IFACE3_NUMBER_MAX_PROP3> + $<$<NOT:$<STREQUAL:$<TARGET_PROPERTY:iface3,NUMBER_MIN_PROP5>,>>:IFACE3_NUMBER_MIN_PROP5> + + # Static libraries compute COMPATIBLE_INTERFACE_ properties transitively. + $<$<BOOL:$<TARGET_PROPERTY:static1,BOOL_PROP5>>:STATIC1_BOOL_PROP5> + $<$<STREQUAL:$<TARGET_PROPERTY:static1,STRING_PROP4>,prop4>:STATIC1_STRING_PROP4> + $<$<EQUAL:$<TARGET_PROPERTY:static1,NUMBER_MIN_PROP6>,6>:STATIC1_NUMBER_MIN_PROP6> + $<$<EQUAL:$<TARGET_PROPERTY:static1,NUMBER_MAX_PROP4>,4>:STATIC1_NUMBER_MAX_PROP4> + + # Object libraries do not compute COMPATIBLE_INTERFACE_ properties transitively, + # but can have properties set on them. + $<$<BOOL:$<TARGET_PROPERTY:object1,BOOL_PROP5>>:OBJECT1_BOOL_PROP5> + $<$<STREQUAL:$<TARGET_PROPERTY:object1,STRING_PROP4>,prop4>:OBJECT1_STRING_PROP4> + $<$<EQUAL:$<TARGET_PROPERTY:object1,NUMBER_MIN_PROP6>,7>:OBJECT1_NUMBER_MIN_PROP6> + $<$<EQUAL:$<TARGET_PROPERTY:object1,NUMBER_MAX_PROP4>,1>:OBJECT1_NUMBER_MAX_PROP4> + + # Interface libraries do not compute COMPATIBLE_INTERFACE_ properties transitively, + # but can have properties set on them. + $<$<BOOL:$<TARGET_PROPERTY:iface3,BOOL_PROP5>>:IFACE3_BOOL_PROP5> + $<$<STREQUAL:$<TARGET_PROPERTY:iface3,STRING_PROP4>,prop4>:IFACE3_STRING_PROP4> + $<$<EQUAL:$<TARGET_PROPERTY:iface3,NUMBER_MIN_PROP6>,7>:IFACE3_NUMBER_MIN_PROP6> + $<$<EQUAL:$<TARGET_PROPERTY:iface3,NUMBER_MAX_PROP4>,1>:IFACE3_NUMBER_MAX_PROP4> ) @@ -128,3 +180,64 @@ COMPATIBLE_INTERFACE_BOOL NON_RELEVANT_PROP ) + +add_library(static1 STATIC foo.cpp) +set_property(TARGET static1 PROPERTY BOOL_PROP5 ON) +set_property(TARGET static1 PROPERTY STRING_PROP4 prop4) +set_property(TARGET static1 PROPERTY NUMBER_MIN_PROP6 7) +set_property(TARGET static1 PROPERTY NUMBER_MAX_PROP4 1) +target_link_libraries(static1 PUBLIC iface1) + +add_library(object1 OBJECT foo.cpp) +set_property(TARGET object1 PROPERTY BOOL_PROP5 ON) +set_property(TARGET object1 PROPERTY STRING_PROP4 prop4) +set_property(TARGET object1 PROPERTY NUMBER_MIN_PROP6 7) +set_property(TARGET object1 PROPERTY NUMBER_MAX_PROP4 1) +target_link_libraries(object1 PUBLIC iface1) + +add_library(iface3 INTERFACE) +set_property(TARGET iface3 PROPERTY BOOL_PROP5 ON) +set_property(TARGET iface3 PROPERTY STRING_PROP4 prop4) +set_property(TARGET iface3 PROPERTY NUMBER_MIN_PROP6 7) +set_property(TARGET iface3 PROPERTY NUMBER_MAX_PROP4 1) +target_link_libraries(iface3 INTERFACE iface1) + +# Test COMPATIBLE_INTERFACE_* property evaluation outside of usage requirements. +add_custom_target(check ALL VERBATIM + COMMAND CompatibleInterface + # expect actual + "1" "$<TARGET_PROPERTY:CompatibleInterface,BOOL_PROP1>" + "prop1" "$<TARGET_PROPERTY:CompatibleInterface,STRING_PROP1>" + "3" "$<TARGET_PROPERTY:CompatibleInterface,NUMBER_MAX_PROP3>" + "5" "$<TARGET_PROPERTY:CompatibleInterface,NUMBER_MIN_PROP5>" + + "1" "$<TARGET_PROPERTY:static1,BOOL_PROP1>" + "prop1" "$<TARGET_PROPERTY:static1,STRING_PROP1>" + "3" "$<TARGET_PROPERTY:static1,NUMBER_MAX_PROP3>" + "5" "$<TARGET_PROPERTY:static1,NUMBER_MIN_PROP5>" + + "" "$<TARGET_PROPERTY:object1,BOOL_PROP1>" + "" "$<TARGET_PROPERTY:object1,STRING_PROP1>" + "" "$<TARGET_PROPERTY:object1,NUMBER_MAX_PROP3>" + "" "$<TARGET_PROPERTY:object1,NUMBER_MIN_PROP5>" + + "" "$<TARGET_PROPERTY:iface3,BOOL_PROP1>" + "" "$<TARGET_PROPERTY:iface3,STRING_PROP1>" + "" "$<TARGET_PROPERTY:iface3,NUMBER_MAX_PROP3>" + "" "$<TARGET_PROPERTY:iface3,NUMBER_MIN_PROP5>" + + "ON" "$<TARGET_PROPERTY:static1,BOOL_PROP5>" + "prop4" "$<TARGET_PROPERTY:static1,STRING_PROP4>" + "6" "$<TARGET_PROPERTY:static1,NUMBER_MIN_PROP6>" + "4" "$<TARGET_PROPERTY:static1,NUMBER_MAX_PROP4>" + + "ON" "$<TARGET_PROPERTY:object1,BOOL_PROP5>" + "prop4" "$<TARGET_PROPERTY:object1,STRING_PROP4>" + "7" "$<TARGET_PROPERTY:object1,NUMBER_MIN_PROP6>" + "1" "$<TARGET_PROPERTY:object1,NUMBER_MAX_PROP4>" + + "ON" "$<TARGET_PROPERTY:iface3,BOOL_PROP5>" + "prop4" "$<TARGET_PROPERTY:iface3,STRING_PROP4>" + "7" "$<TARGET_PROPERTY:iface3,NUMBER_MIN_PROP6>" + "1" "$<TARGET_PROPERTY:iface3,NUMBER_MAX_PROP4>" + )
diff --git a/Tests/CompatibleInterface/main.cpp b/Tests/CompatibleInterface/main.cpp index 0bccb82..1205cca 100644 --- a/Tests/CompatibleInterface/main.cpp +++ b/Tests/CompatibleInterface/main.cpp
@@ -1,3 +1,5 @@ +#include <stdio.h> +#include <string.h> #ifndef BOOL_PROP1 # error Expected BOOL_PROP1 @@ -23,6 +25,102 @@ # error Expected STRING_PROP3 #endif +#ifndef STATIC1_BOOL_PROP1 +# error Expected STATIC1_BOOL_PROP1 +#endif + +#ifndef STATIC1_STRING_PROP1 +# error Expected STATIC1_STRING_PROP1 +#endif + +#ifndef STATIC1_NUMBER_MAX_PROP3 +# error Expected STATIC1_NUMBER_MAX_PROP3 +#endif + +#ifndef STATIC1_NUMBER_MIN_PROP5 +# error Expected STATIC1_NUMBER_MIN_PROP5 +#endif + +#ifdef OBJECT1_BOOL_PROP1 +# error Unexpected OBJECT1_BOOL_PROP1 +#endif + +#ifdef OBJECT1_STRING_PROP1 +# error Unexpected OBJECT1_STRING_PROP1 +#endif + +#ifdef OBJECT1_NUMBER_MAX_PROP3 +# error Unexpected OBJECT1_NUMBER_MAX_PROP3 +#endif + +#ifdef OBJECT1_NUMBER_MIN_PROP5 +# error Unexpected OBJECT1_NUMBER_MIN_PROP5 +#endif + +#ifdef IFACE3_BOOL_PROP1 +# error Unexpected IFACE3_BOOL_PROP1 +#endif + +#ifdef IFACE3_STRING_PROP1 +# error Unexpected IFACE3_STRING_PROP1 +#endif + +#ifdef IFACE3_NUMBER_MAX_PROP3 +# error Unexpected IFACE3_NUMBER_MAX_PROP3 +#endif + +#ifdef IFACE3_NUMBER_MIN_PROP5 +# error Unexpected IFACE3_NUMBER_MIN_PROP5 +#endif + +#ifndef STATIC1_BOOL_PROP5 +# error Expected STATIC1_BOOL_PROP5 +#endif + +#ifndef STATIC1_STRING_PROP4 +# error Expected STATIC1_STRING_PROP4 +#endif + +#ifndef STATIC1_NUMBER_MIN_PROP6 +# error Expected STATIC1_NUMBER_MIN_PROP6 +#endif + +#ifndef STATIC1_NUMBER_MAX_PROP4 +# error Expected STATIC1_NUMBER_MAX_PROP4 +#endif + +#ifndef OBJECT1_BOOL_PROP5 +# error Expected OBJECT1_BOOL_PROP5 +#endif + +#ifndef OBJECT1_STRING_PROP4 +# error Expected OBJECT1_STRING_PROP4 +#endif + +#ifndef OBJECT1_NUMBER_MIN_PROP6 +# error Expected OBJECT1_NUMBER_MIN_PROP6 +#endif + +#ifndef OBJECT1_NUMBER_MAX_PROP4 +# error Expected OBJECT1_NUMBER_MAX_PROP4 +#endif + +#ifndef IFACE3_BOOL_PROP5 +# error Expected IFACE3_BOOL_PROP5 +#endif + +#ifndef IFACE3_STRING_PROP4 +# error Expected IFACE3_STRING_PROP4 +#endif + +#ifndef IFACE3_NUMBER_MIN_PROP6 +# error Expected IFACE3_NUMBER_MIN_PROP6 +#endif + +#ifndef IFACE3_NUMBER_MAX_PROP4 +# error Expected IFACE3_NUMBER_MAX_PROP4 +#endif + template <bool test> struct CMakeStaticAssert; @@ -35,10 +133,12 @@ { NumericMaxTest1 = sizeof(CMakeStaticAssert<NUMBER_MAX_PROP1 == 100>), NumericMaxTest2 = sizeof(CMakeStaticAssert<NUMBER_MAX_PROP2 == 250>), + NumericMaxTest3 = sizeof(CMakeStaticAssert<NUMBER_MAX_PROP3 == 3>), NumericMinTest1 = sizeof(CMakeStaticAssert<NUMBER_MIN_PROP1 == 50>), NumericMinTest2 = sizeof(CMakeStaticAssert<NUMBER_MIN_PROP2 == 200>), NumericMinTest3 = sizeof(CMakeStaticAssert<NUMBER_MIN_PROP3 == 0xA>), - NumericMinTest4 = sizeof(CMakeStaticAssert<NUMBER_MIN_PROP4 == 0x10>) + NumericMinTest4 = sizeof(CMakeStaticAssert<NUMBER_MIN_PROP4 == 0x10>), + NumericMinTest5 = sizeof(CMakeStaticAssert<NUMBER_MIN_PROP5 == 5>) }; #include "iface2.h" @@ -51,6 +151,14 @@ int main(int argc, char** argv) { + int result = 0; + for (int i = 2; i < argc; i += 2) { + if (strcmp(argv[i - 1], argv[i]) != 0) { + fprintf(stderr, "Argument %d expected '%s' but got '%s'.\n", i, + argv[i - 1], argv[i]); + result = 1; + } + } Iface2 if2; - return if2.foo() + foo() + bar(); + return result + if2.foo() + foo() + bar(); }
diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt index d9bb447..614b721 100644 --- a/Tests/CompileFeatures/CMakeLists.txt +++ b/Tests/CompileFeatures/CMakeLists.txt
@@ -6,6 +6,31 @@ 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_CUDA) + enable_language(CUDA) + set(ext_CUDA cu) + set(std_CUDA 03 11 14 17 20 23 26) +endif() + +if(CMake_TEST_HIP) + enable_language(HIP) + set(ext_HIP hip) + set(std_HIP 98 11 14 17 20 23 26) +endif() + +foreach(lang C CXX CUDA HIP) + foreach(std IN LISTS std_${lang}) + string(TOLOWER "${lang}_std_${std}" feature) + if("${std}" IN_LIST CMake_TEST_${lang}_STANDARDS + OR CMAKE_${lang}${std}_STANDARD_COMPILE_OPTION) + add_library(test_${feature} OBJECT ${feature}.${ext_${lang}}) + target_compile_features(test_${feature} PRIVATE ${feature}) + endif() + endforeach() +endforeach() macro(run_test feature lang) if (${feature} IN_LIST CMAKE_${lang}_COMPILE_FEATURES) @@ -273,6 +298,7 @@ if (std_flag_idx EQUAL -1) add_executable(default_dialect default_dialect.cpp) target_compile_definitions(default_dialect PRIVATE + DEFAULT_CXX26=$<EQUAL:${CMAKE_CXX_STANDARD_DEFAULT},26> DEFAULT_CXX23=$<EQUAL:${CMAKE_CXX_STANDARD_DEFAULT},23> DEFAULT_CXX20=$<EQUAL:${CMAKE_CXX_STANDARD_DEFAULT},20> DEFAULT_CXX17=$<EQUAL:${CMAKE_CXX_STANDARD_DEFAULT},17> @@ -284,6 +310,22 @@ endif() endif () +if (CMake_TEST_CUDA + AND CMAKE_CUDA_COMPILE_FEATURES + AND CMAKE_CUDA_STANDARD_DEFAULT + AND NOT CMAKE_CUDA_FLAGS MATCHES "-std=") + add_executable(default_dialect_cuda default_dialect.cu) + target_compile_definitions(default_dialect_cuda PRIVATE + DEFAULT_CXX26=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},26> + DEFAULT_CXX23=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},23> + DEFAULT_CXX20=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},20> + DEFAULT_CXX17=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},17> + DEFAULT_CXX14=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},14> + DEFAULT_CXX11=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},11> + DEFAULT_CXX98=$<EQUAL:${CMAKE_CUDA_STANDARD_DEFAULT},03> + ) +endif () + # always add a target "CompileFeatures" if ((NOT CXX_expected_features) OR (NOT cxx_auto_type IN_LIST CXX_expected_features))
diff --git a/Tests/CompileFeatures/c_std.h b/Tests/CompileFeatures/c_std.h new file mode 100644 index 0000000..afe0308 --- /dev/null +++ b/Tests/CompileFeatures/c_std.h
@@ -0,0 +1,8 @@ +#define C_STD_99 199901L +#define C_STD_11 201112L +#define C_STD_17 201710L +#define C_STD_23 202311L + +#ifdef __STDC_VERSION__ +# define C_STD __STDC_VERSION__ +#endif
diff --git a/Tests/CompileFeatures/c_std_11.c b/Tests/CompileFeatures/c_std_11.c new file mode 100644 index 0000000..2938c17 --- /dev/null +++ b/Tests/CompileFeatures/c_std_11.c
@@ -0,0 +1,5 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD <= C_STD_99 && \ + !(defined(__SUNPRO_C) && __SUNPRO_C < 0x5140 && C_STD == C_STD_99) +# error "c_std_11 not honored" +#endif
diff --git a/Tests/CompileFeatures/c_std_17.c b/Tests/CompileFeatures/c_std_17.c new file mode 100644 index 0000000..938cba4 --- /dev/null +++ b/Tests/CompileFeatures/c_std_17.c
@@ -0,0 +1,4 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD <= C_STD_11 +# error "c_std_17 not honored" +#endif
diff --git a/Tests/CompileFeatures/c_std_23.c b/Tests/CompileFeatures/c_std_23.c new file mode 100644 index 0000000..a60a16e --- /dev/null +++ b/Tests/CompileFeatures/c_std_23.c
@@ -0,0 +1,5 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD <= C_STD_17 && \ + !(C_STD == C_STD_17 && defined(__clang_major__) && __clang_major__ < 14) +# error "c_std_23 not honored" +#endif
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/CompileFeatures/c_std_90.c similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/CompileFeatures/c_std_90.c
diff --git a/Tests/CompileFeatures/c_std_99.c b/Tests/CompileFeatures/c_std_99.c new file mode 100644 index 0000000..20b6372 --- /dev/null +++ b/Tests/CompileFeatures/c_std_99.c
@@ -0,0 +1,4 @@ +#include "c_std.h" +#if defined(C_STD) && C_STD < C_STD_99 +# error "c_std_99 not honored" +#endif
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/CompileFeatures/cuda_std_03.cu similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/CompileFeatures/cuda_std_03.cu
diff --git a/Tests/CompileFeatures/cuda_std_11.cu b/Tests/CompileFeatures/cuda_std_11.cu new file mode 100644 index 0000000..76813fd --- /dev/null +++ b/Tests/CompileFeatures/cuda_std_11.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD < CXX_STD_11 +# error "cuda_std_11 not honored" +#endif
diff --git a/Tests/CompileFeatures/cuda_std_14.cu b/Tests/CompileFeatures/cuda_std_14.cu new file mode 100644 index 0000000..53e23c8 --- /dev/null +++ b/Tests/CompileFeatures/cuda_std_14.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_11 +# error "cuda_std_14 not honored" +#endif
diff --git a/Tests/CompileFeatures/cuda_std_17.cu b/Tests/CompileFeatures/cuda_std_17.cu new file mode 100644 index 0000000..502defb --- /dev/null +++ b/Tests/CompileFeatures/cuda_std_17.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_14 +# error "cuda_std_17 not honored" +#endif
diff --git a/Tests/CompileFeatures/cuda_std_20.cu b/Tests/CompileFeatures/cuda_std_20.cu new file mode 100644 index 0000000..afdd3ba --- /dev/null +++ b/Tests/CompileFeatures/cuda_std_20.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_17 +# error "cuda_std_20 not honored" +#endif
diff --git a/Tests/CompileFeatures/cuda_std_23.cu b/Tests/CompileFeatures/cuda_std_23.cu new file mode 100644 index 0000000..6bdaeb9 --- /dev/null +++ b/Tests/CompileFeatures/cuda_std_23.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_20 +# error "cuda_std_23 not honored" +#endif
diff --git a/Tests/CompileFeatures/cuda_std_26.cu b/Tests/CompileFeatures/cuda_std_26.cu new file mode 100644 index 0000000..efeca8d --- /dev/null +++ b/Tests/CompileFeatures/cuda_std_26.cu
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_23 +# error "cuda_std_26 not honored" +#endif
diff --git a/Tests/CompileFeatures/cxx_std.h b/Tests/CompileFeatures/cxx_std.h new file mode 100644 index 0000000..f17b683 --- /dev/null +++ b/Tests/CompileFeatures/cxx_std.h
@@ -0,0 +1,54 @@ +#define CXX_STD_98 199711L +#define CXX_STD_11 201103L +#define CXX_STD_14 201402L +#define CXX_STD_17 201703L +#define CXX_STD_20 202002L +#define CXX_STD_23 202302L + +#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) +# if _MSVC_LANG > CXX_STD_17 +# define CXX_STD _MSVC_LANG +# elif _MSVC_LANG == CXX_STD_17 && defined(__cpp_aggregate_paren_init) +# define CXX_STD CXX_STD_20 +# elif _MSVC_LANG > CXX_STD_14 && __cplusplus > CXX_STD_17 +# define CXX_STD CXX_STD_20 +# elif _MSVC_LANG > CXX_STD_14 +# define CXX_STD CXX_STD_17 +# elif defined(__INTEL_CXX11_MODE__) && defined(__cpp_aggregate_nsdmi) +# define CXX_STD CXX_STD_14 +# elif defined(__INTEL_CXX11_MODE__) +# define CXX_STD CXX_STD_11 +# else +# define CXX_STD CXX_STD_98 +# endif +#elif defined(_MSC_VER) && defined(_MSVC_LANG) +# if _MSVC_LANG > __cplusplus +# define CXX_STD _MSVC_LANG +# else +# define CXX_STD __cplusplus +# endif +#elif defined(__NVCOMPILER) +# if __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init) +# define CXX_STD CXX_STD_20 +# else +# define CXX_STD __cplusplus +# endif +#elif defined(__INTEL_COMPILER) || defined(__PGI) +# if __cplusplus == CXX_STD_11 && defined(__cpp_namespace_attributes) +# define CXX_STD CXX_STD_17 +# elif __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi) +# define CXX_STD CXX_STD_14 +# else +# define CXX_STD __cplusplus +# endif +#elif (defined(__IBMCPP__) || defined(__ibmxl__)) && defined(__linux__) +# if __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi) +# define CXX_STD CXX_STD_14 +# else +# define CXX_STD __cplusplus +# endif +#elif __cplusplus == 1 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CXX_STD CXX_STD_11 +#else +# define CXX_STD __cplusplus +#endif
diff --git a/Tests/CompileFeatures/cxx_std_11.cpp b/Tests/CompileFeatures/cxx_std_11.cpp new file mode 100644 index 0000000..aa85cdb --- /dev/null +++ b/Tests/CompileFeatures/cxx_std_11.cpp
@@ -0,0 +1,6 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD < CXX_STD_11 && \ + !(CXX_STD == CXX_STD_98 && \ + (defined(__IBMCPP__) && defined(_AIX) && __IBMCPP__ == 1610)) +# error "cxx_std_11 not honored" +#endif
diff --git a/Tests/CompileFeatures/cxx_std_14.cpp b/Tests/CompileFeatures/cxx_std_14.cpp new file mode 100644 index 0000000..dabd2ee --- /dev/null +++ b/Tests/CompileFeatures/cxx_std_14.cpp
@@ -0,0 +1,9 @@ +#include "cxx_std.h" +#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_std_14 not honored" +#endif
diff --git a/Tests/CompileFeatures/cxx_std_17.cpp b/Tests/CompileFeatures/cxx_std_17.cpp new file mode 100644 index 0000000..3ca6bbe --- /dev/null +++ b/Tests/CompileFeatures/cxx_std_17.cpp
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_14 +# error "cxx_std_17 not honored" +#endif
diff --git a/Tests/CompileFeatures/cxx_std_20.cpp b/Tests/CompileFeatures/cxx_std_20.cpp new file mode 100644 index 0000000..35f5ac1 --- /dev/null +++ b/Tests/CompileFeatures/cxx_std_20.cpp
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_17 +# error "cxx_std_20 not honored" +#endif
diff --git a/Tests/CompileFeatures/cxx_std_23.cpp b/Tests/CompileFeatures/cxx_std_23.cpp new file mode 100644 index 0000000..020578d --- /dev/null +++ b/Tests/CompileFeatures/cxx_std_23.cpp
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_20 +# error "cxx_std_23 not honored" +#endif
diff --git a/Tests/CompileFeatures/cxx_std_26.cpp b/Tests/CompileFeatures/cxx_std_26.cpp new file mode 100644 index 0000000..db3f6bc --- /dev/null +++ b/Tests/CompileFeatures/cxx_std_26.cpp
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_23 +# error "cxx_std_26 not honored" +#endif
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/CompileFeatures/cxx_std_98.cpp similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/CompileFeatures/cxx_std_98.cpp
diff --git a/Tests/CompileFeatures/default_dialect.c b/Tests/CompileFeatures/default_dialect.c index cae107b..4cd4ca0 100644 --- a/Tests/CompileFeatures/default_dialect.c +++ b/Tests/CompileFeatures/default_dialect.c
@@ -1,18 +1,19 @@ +#include "c_std.h" #if DEFAULT_C23 -# if __STDC_VERSION__ <= 201710L +# if C_STD <= C_STD_17 # error Unexpected value for __STDC_VERSION__. # endif #elif DEFAULT_C17 -# if __STDC_VERSION__ < 201710L +# if C_STD <= C_STD_11 # error Unexpected value for __STDC_VERSION__. # endif #elif DEFAULT_C11 -# if __STDC_VERSION__ < 201112L +# if C_STD <= C_STD_99 # error Unexpected value for __STDC_VERSION__. # endif #elif DEFAULT_C99 -# if __STDC_VERSION__ != 199901L +# if C_STD != C_STD_99 # error Unexpected value for __STDC_VERSION__. # endif #else
diff --git a/Tests/CompileFeatures/default_dialect.cpp b/Tests/CompileFeatures/default_dialect.cpp index bd504ff..b873b14 100644 --- a/Tests/CompileFeatures/default_dialect.cpp +++ b/Tests/CompileFeatures/default_dialect.cpp
@@ -1,48 +1,37 @@ +#include "cxx_std.h" template <long l> struct Outputter; -#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG) && _MSVC_LANG < 201403L -# if defined(__INTEL_CXX11_MODE__) -# if defined(__cpp_aggregate_nsdmi) -# define CXX_STD 201402L -# else -# define CXX_STD 201103L -# endif -# else -# define CXX_STD 199711L +#if DEFAULT_CXX26 +# if CXX_STD <= CXX_STD_23 +Outputter<CXX_STD> o; # endif -#elif defined(_MSC_VER) && defined(_MSVC_LANG) -# define CXX_STD _MSVC_LANG -#else -# define CXX_STD __cplusplus -#endif - -#if DEFAULT_CXX23 -# if CXX_STD <= 202002L +#elif DEFAULT_CXX23 +# if CXX_STD <= CXX_STD_20 Outputter<CXX_STD> o; # endif #elif DEFAULT_CXX20 -# if CXX_STD <= 201703L +# if CXX_STD <= CXX_STD_17 Outputter<CXX_STD> o; # endif #elif DEFAULT_CXX17 -# if CXX_STD <= 201402L +# if CXX_STD <= CXX_STD_14 Outputter<CXX_STD> o; # endif #elif DEFAULT_CXX14 -# if CXX_STD != 201402L +# if CXX_STD <= CXX_STD_11 Outputter<CXX_STD> o; # endif #elif DEFAULT_CXX11 -# if CXX_STD != 201103L +# if CXX_STD != CXX_STD_11 Outputter<CXX_STD> o; # endif #else # if !DEFAULT_CXX98 # error Buildsystem error # endif -# if CXX_STD != 199711L && CXX_STD != 1 && \ +# if CXX_STD != CXX_STD_98 && CXX_STD != 1 && \ !defined(__GXX_EXPERIMENTAL_CXX0X__) Outputter<CXX_STD> o; # endif
diff --git a/Tests/CompileFeatures/default_dialect.cu b/Tests/CompileFeatures/default_dialect.cu new file mode 100644 index 0000000..a549a5c --- /dev/null +++ b/Tests/CompileFeatures/default_dialect.cu
@@ -0,0 +1 @@ +#include "default_dialect.cpp"
diff --git a/Tests/CompileFeatures/hip_std_11.hip b/Tests/CompileFeatures/hip_std_11.hip new file mode 100644 index 0000000..835aebd --- /dev/null +++ b/Tests/CompileFeatures/hip_std_11.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD < CXX_STD_11 +# error "hip_std_11 not honored" +#endif
diff --git a/Tests/CompileFeatures/hip_std_14.hip b/Tests/CompileFeatures/hip_std_14.hip new file mode 100644 index 0000000..9a605d1 --- /dev/null +++ b/Tests/CompileFeatures/hip_std_14.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_11 +# error "hip_std_14 not honored" +#endif
diff --git a/Tests/CompileFeatures/hip_std_17.hip b/Tests/CompileFeatures/hip_std_17.hip new file mode 100644 index 0000000..b7f3c84 --- /dev/null +++ b/Tests/CompileFeatures/hip_std_17.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_14 +# error "hip_std_17 not honored" +#endif
diff --git a/Tests/CompileFeatures/hip_std_20.hip b/Tests/CompileFeatures/hip_std_20.hip new file mode 100644 index 0000000..7dfbe75 --- /dev/null +++ b/Tests/CompileFeatures/hip_std_20.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_17 +# error "hip_std_20 not honored" +#endif
diff --git a/Tests/CompileFeatures/hip_std_23.hip b/Tests/CompileFeatures/hip_std_23.hip new file mode 100644 index 0000000..aa1af72 --- /dev/null +++ b/Tests/CompileFeatures/hip_std_23.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_20 +# error "hip_std_23 not honored" +#endif
diff --git a/Tests/CompileFeatures/hip_std_26.hip b/Tests/CompileFeatures/hip_std_26.hip new file mode 100644 index 0000000..3c7a2f5 --- /dev/null +++ b/Tests/CompileFeatures/hip_std_26.hip
@@ -0,0 +1,4 @@ +#include "cxx_std.h" +#if defined(CXX_STD) && CXX_STD <= CXX_STD_23 +# error "hip_std_26 not honored" +#endif
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/CompileFeatures/hip_std_98.hip similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/CompileFeatures/hip_std_98.hip
diff --git a/Tests/CompileOptions/CMakeLists.txt b/Tests/CompileOptions/CMakeLists.txt index 0fbfb83..e3d3ee7 100644 --- a/Tests/CompileOptions/CMakeLists.txt +++ b/Tests/CompileOptions/CMakeLists.txt
@@ -22,6 +22,8 @@ macro(get_compiler_test_genex lst lang) list(APPEND ${lst} -DTEST_${lang}_COMPILER_VERSION="$<${lang}_COMPILER_VERSION>") list(APPEND ${lst} -DTEST_${lang}_COMPILER_VERSION_EQUALITY=$<${lang}_COMPILER_VERSION:${CMAKE_${lang}_COMPILER_VERSION}>) + list(APPEND ${lst} -DTEST_${lang}_COMPILER_FRONTEND_VARIANT="$<${lang}_COMPILER_FRONTEND_VARIANT>") + list(APPEND ${lst} -DTEST_${lang}_COMPILER_FRONTEND_VARIANT_EQUALITY=$<${lang}_COMPILER_FRONTEND_VARIANT:${CMAKE_${lang}_COMPILER_FRONTEND_VARIANT}>) endmacro() get_compiler_test_genex(c_tests C) @@ -74,10 +76,8 @@ endif() set_property(TARGET CompileOptions APPEND PROPERTY COMPILE_OPTIONS "-DFLAG_B=2" "-DFLAG_C=2" "-DFLAG_D=1") set_property(TARGET testlib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "-DFLAG_D=2") - if(NOT CMAKE_GENERATOR MATCHES "^Visual Studio 9") - set_property(TARGET testlib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "-DFLAG_E=1") - set_property(SOURCE main.cpp PROPERTY COMPILE_OPTIONS "-DFLAG_E=2") - endif() + set_property(TARGET testlib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "-DFLAG_E=1") + set_property(SOURCE main.cpp PROPERTY COMPILE_OPTIONS "-DFLAG_E=2") endif() target_link_libraries(CompileOptions testlib) @@ -93,6 +93,8 @@ PRIVATE "EXPECTED_C_COMPILER_VERSION=\"${CMAKE_C_COMPILER_VERSION}\"" "EXPECTED_CXX_COMPILER_VERSION=\"${CMAKE_CXX_COMPILER_VERSION}\"" + "EXPECTED_C_COMPILER_FRONTEND_VARIANT=\"${CMAKE_C_COMPILER_FRONTEND_VARIANT}\"" + "EXPECTED_CXX_COMPILER_FRONTEND_VARIANT=\"${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}\"" ) if(TEST_FORTRAN) @@ -101,5 +103,6 @@ PRIVATE "TEST_FORTRAN" "EXPECTED_Fortran_COMPILER_VERSION=\"${CMAKE_Fortran_COMPILER_VERSION}\"" + "EXPECTED_Fortran_COMPILER_FRONTEND_VARIANT=\"${CMAKE_Fortran_COMPILER_FRONTEND_VARIANT}\"" ) endif()
diff --git a/Tests/CompileOptions/main.cpp b/Tests/CompileOptions/main.cpp index 23ce197..a7ce783 100644 --- a/Tests/CompileOptions/main.cpp +++ b/Tests/CompileOptions/main.cpp
@@ -68,15 +68,25 @@ #endif && strcmp(EXPECTED_C_COMPILER_VERSION, TEST_C_COMPILER_VERSION) == 0 && - strcmp(EXPECTED_CXX_COMPILER_VERSION, TEST_CXX_COMPILER_VERSION) == 0 + strcmp(EXPECTED_CXX_COMPILER_VERSION, TEST_CXX_COMPILER_VERSION) == + 0 && + strcmp(EXPECTED_C_COMPILER_FRONTEND_VARIANT, + TEST_C_COMPILER_FRONTEND_VARIANT) == 0 && + strcmp(EXPECTED_CXX_COMPILER_FRONTEND_VARIANT, + TEST_CXX_COMPILER_FRONTEND_VARIANT) == 0 #ifdef TEST_FORTRAN && strcmp(EXPECTED_Fortran_COMPILER_VERSION, - TEST_Fortran_COMPILER_VERSION) == 0 + TEST_Fortran_COMPILER_VERSION) == 0 && + strcmp(EXPECTED_Fortran_COMPILER_FRONTEND_VARIANT, + TEST_Fortran_COMPILER_FRONTEND_VARIANT) == 0 #endif && TEST_C_COMPILER_VERSION_EQUALITY == 1 && - TEST_CXX_COMPILER_VERSION_EQUALITY == 1 + TEST_CXX_COMPILER_VERSION_EQUALITY == 1 && + TEST_C_COMPILER_FRONTEND_VARIANT_EQUALITY == 1 && + TEST_CXX_COMPILER_FRONTEND_VARIANT_EQUALITY == 1 #ifdef TEST_FORTRAN - && TEST_Fortran_COMPILER_VERSION_EQUALITY == 1 + && TEST_Fortran_COMPILER_VERSION_EQUALITY == 1 && + TEST_Fortran_COMPILER_FRONTEND_VARIANT_EQUALITY == 1 #endif ) ? 0
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt index 04ef86d..c1952c5 100644 --- a/Tests/Cuda/CMakeLists.txt +++ b/Tests/Cuda/CMakeLists.txt
@@ -27,6 +27,7 @@ if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA STREQUAL "Clang") # Clang lacks __CUDACC_VER*__ defines. add_cuda_test_macro(Cuda.ProperDeviceLibraries ProperDeviceLibraries) + add_cuda_test_macro(Cuda.ToolkitBeforeLang ToolkitBeforeLang) endif() add_cuda_test_macro(Cuda.WithC CudaWithC)
diff --git a/Tests/Cuda/ToolkitBeforeLang/CMakeLists.txt b/Tests/Cuda/ToolkitBeforeLang/CMakeLists.txt new file mode 100644 index 0000000..6b97a21 --- /dev/null +++ b/Tests/Cuda/ToolkitBeforeLang/CMakeLists.txt
@@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.15) +project(ToolkitBeforeLang CXX) + +#Goal for this example: +# Validate that we use the same CUDA Toolkit as `enable_language(CUDA)` would find +find_package(CUDAToolkit REQUIRED) + +if(NOT DEFINED CUDAToolkit_VERSION) + message(FATAL_ERROR "expected CUDAToolkit variable CUDAToolkit_VERSION not found") +endif() + +enable_language(CUDA) + +message(STATUS "CUDAToolkit_NVCC_EXECUTABLE ${CUDAToolkit_NVCC_EXECUTABLE}") +message(STATUS "CMAKE_CUDA_COMPILER ${CMAKE_CUDA_COMPILER}") + +if( NOT CUDAToolkit_NVCC_EXECUTABLE STREQUAL CMAKE_CUDA_COMPILER) + message(FATAL_ERROR "CUDAToolkit_NVCC_EXECUTABLE ${CUDAToolkit_NVCC_EXECUTABLE} doesn't match CMAKE_CUDA_COMPILER ${CMAKE_CUDA_COMPILER}") +endif() + +add_executable(ToolkitBeforeLang main.cpp) +target_link_libraries(ToolkitBeforeLang PRIVATE CUDA::toolkit)
diff --git a/Tests/Cuda/ToolkitBeforeLang/main.cpp b/Tests/Cuda/ToolkitBeforeLang/main.cpp new file mode 100644 index 0000000..c8d5c6b --- /dev/null +++ b/Tests/Cuda/ToolkitBeforeLang/main.cpp
@@ -0,0 +1,8 @@ +// Only thing we care about is that these headers are found +#include <cuda.h> +#include <cuda_runtime_api.h> + +int main() +{ + return 0; +}
diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt index d46ee08..c145907 100644 --- a/Tests/CustomCommand/CMakeLists.txt +++ b/Tests/CustomCommand/CMakeLists.txt
@@ -588,3 +588,12 @@ # Test empty COMMANDs are omitted add_executable(empty_command empty_command.cxx) add_custom_command(TARGET empty_command POST_BUILD COMMAND $<0:date>) + +# Test OUTPUT allows filenames containing "#" on generators that support this +if(NOT CMAKE_GENERATOR MATCHES "Borland Makefiles") + add_custom_target(file_with_hash ALL DEPENDS "${PROJECT_BINARY_DIR}/hash#in#name.txt") + add_custom_command( + OUTPUT "${PROJECT_BINARY_DIR}/hash#in#name.txt" + COMMAND ${CMAKE_COMMAND} -E touch "${PROJECT_BINARY_DIR}/hash#in#name.txt" + ) +endif()
diff --git a/Tests/CustomTransitiveProperties/CMakeLists.txt b/Tests/CustomTransitiveProperties/CMakeLists.txt new file mode 100644 index 0000000..83ceff5 --- /dev/null +++ b/Tests/CustomTransitiveProperties/CMakeLists.txt
@@ -0,0 +1,146 @@ +cmake_minimum_required(VERSION 3.29) +cmake_policy(SET CMP0166 NEW) +project(CustomTransitiveProperties C) + +add_library(iface1 INTERFACE) +set_target_properties(iface1 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_A" # LINK_ONLY not pierced + TRANSITIVE_LINK_PROPERTIES "CUSTOM_U" # LINK_ONLY pierced + + INTERFACE_CUSTOM_A "CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_$<UPPER_CASE:$<TARGET_PROPERTY:NAME>>" + INTERFACE_CUSTOM_B "CUSTOM_B_IFACE1" + INTERFACE_CUSTOM_C "CUSTOM_C_IFACE1" + INTERFACE_CUSTOM_U "CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_$<UPPER_CASE:$<TARGET_PROPERTY:NAME>>" + INTERFACE_CUSTOM_V "CUSTOM_V_IFACE1" + INTERFACE_CUSTOM_W "CUSTOM_W_IFACE1" + ) + +add_library(iface2 INTERFACE) +set_target_properties(iface2 PROPERTIES + INTERFACE_CUSTOM_A "CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_$<TARGET_PROPERTY:TYPE>" + INTERFACE_CUSTOM_U "CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_$<TARGET_PROPERTY:TYPE>" + ) +target_link_libraries(iface2 INTERFACE iface1) + +# Test that the INTERFACE prefix is removed. +set(unnecessary_INTERFACE_ "INTERFACE_") + +add_library(static1 STATIC static1.c) +target_link_libraries(static1 PRIVATE iface2) +set_target_properties(static1 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "${unnecessary_INTERFACE_}CUSTOM_B" # LINK_ONLY not pierced + TRANSITIVE_LINK_PROPERTIES "${unnecessary_INTERFACE_}CUSTOM_V" # LINK_ONLY pierced + + CUSTOM_A "CUSTOM_A_STATIC1" + CUSTOM_B "CUSTOM_B_STATIC1" + CUSTOM_U "CUSTOM_U_STATIC1" + CUSTOM_V "CUSTOM_V_STATIC1" + INTERFACE_CUSTOM_A "CUSTOM_A_STATIC1_IFACE" + INTERFACE_CUSTOM_B "CUSTOM_B_STATIC1_IFACE" + INTERFACE_CUSTOM_U "CUSTOM_U_STATIC1_IFACE" + INTERFACE_CUSTOM_V "CUSTOM_V_STATIC1_IFACE" + ) +target_compile_definitions(static1 PRIVATE + $<TARGET_PROPERTY:CUSTOM_A> + $<TARGET_PROPERTY:CUSTOM_B> + $<TARGET_PROPERTY:CUSTOM_U> + $<TARGET_PROPERTY:CUSTOM_V> + ) + +add_library(object1 OBJECT object1.c) +target_link_libraries(object1 PRIVATE iface2) +set_target_properties(object1 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "${unnecessary_INTERFACE_}CUSTOM_C" # LINK_ONLY not pierced + TRANSITIVE_LINK_PROPERTIES "${unnecessary_INTERFACE_}CUSTOM_W" # LINK_ONLY pierced + + CUSTOM_A "CUSTOM_A_OBJECT1" + CUSTOM_C "CUSTOM_C_OBJECT1" + CUSTOM_U "CUSTOM_U_OBJECT1" + CUSTOM_W "CUSTOM_W_OBJECT1" + INTERFACE_CUSTOM_A "CUSTOM_A_OBJECT1_IFACE" + INTERFACE_CUSTOM_C "CUSTOM_C_OBJECT1_IFACE" + INTERFACE_CUSTOM_U "CUSTOM_U_OBJECT1_IFACE" + INTERFACE_CUSTOM_W "CUSTOM_W_OBJECT1_IFACE" + ) +target_compile_definitions(object1 PRIVATE + $<TARGET_PROPERTY:CUSTOM_A> + $<TARGET_PROPERTY:CUSTOM_C> + $<TARGET_PROPERTY:CUSTOM_U> + $<TARGET_PROPERTY:CUSTOM_W> + ) + +add_executable(CustomTransitiveProperties main.c) +target_link_libraries(CustomTransitiveProperties PRIVATE static1 object1) +set_target_properties(CustomTransitiveProperties PROPERTIES + CUSTOM_A "CUSTOM_A_MAIN" + CUSTOM_B "CUSTOM_B_MAIN" + CUSTOM_C "CUSTOM_C_MAIN" + CUSTOM_U "CUSTOM_U_MAIN" + CUSTOM_V "CUSTOM_V_MAIN" + CUSTOM_W "CUSTOM_W_MAIN" + ) + +# Test TRANSITIVE_*_PROPERTY evaluation within usage requirements. +target_compile_definitions(CustomTransitiveProperties PRIVATE + $<TARGET_PROPERTY:CUSTOM_A> + $<TARGET_PROPERTY:CUSTOM_B> + $<TARGET_PROPERTY:CUSTOM_C> + $<TARGET_PROPERTY:CUSTOM_U> + $<TARGET_PROPERTY:CUSTOM_V> + $<TARGET_PROPERTY:CUSTOM_W> + ) + +# Test TRANSITIVE_*_PROPERTY evaluation outside of usage requirements. +set(out "${CMAKE_CURRENT_BINARY_DIR}/out-$<CONFIG>.txt") +file(GENERATE OUTPUT "${out}" CONTENT "# file(GENERATE) produced: +iface1 CUSTOM_A: '$<TARGET_PROPERTY:iface1,CUSTOM_A>' +iface1 INTERFACE_CUSTOM_A: '$<TARGET_PROPERTY:iface1,INTERFACE_CUSTOM_A>' +iface2 CUSTOM_A: '$<TARGET_PROPERTY:iface2,CUSTOM_A>' +iface2 INTERFACE_CUSTOM_A: '$<TARGET_PROPERTY:iface2,INTERFACE_CUSTOM_A>' +static1 CUSTOM_A: '$<TARGET_PROPERTY:static1,CUSTOM_A>' +static1 INTERFACE_CUSTOM_A: '$<TARGET_PROPERTY:static1,INTERFACE_CUSTOM_A>' +static1 CUSTOM_B: '$<TARGET_PROPERTY:static1,CUSTOM_B>' +static1 INTERFACE_CUSTOM_B: '$<TARGET_PROPERTY:static1,INTERFACE_CUSTOM_B>' +static1 CUSTOM_U: '$<TARGET_PROPERTY:static1,CUSTOM_U>' +static1 INTERFACE_CUSTOM_U: '$<TARGET_PROPERTY:static1,INTERFACE_CUSTOM_U>' +static1 CUSTOM_V: '$<TARGET_PROPERTY:static1,CUSTOM_V>' +static1 INTERFACE_CUSTOM_V: '$<TARGET_PROPERTY:static1,INTERFACE_CUSTOM_V>' +object1 CUSTOM_A: '$<TARGET_PROPERTY:object1,CUSTOM_A>' +object1 INTERFACE_CUSTOM_A: '$<TARGET_PROPERTY:object1,INTERFACE_CUSTOM_A>' +object1 CUSTOM_C: '$<TARGET_PROPERTY:object1,CUSTOM_C>' +object1 INTERFACE_CUSTOM_C: '$<TARGET_PROPERTY:object1,INTERFACE_CUSTOM_C>' +object1 CUSTOM_U: '$<TARGET_PROPERTY:object1,CUSTOM_U>' +object1 INTERFACE_CUSTOM_U: '$<TARGET_PROPERTY:object1,INTERFACE_CUSTOM_U>' +object1 CUSTOM_W: '$<TARGET_PROPERTY:object1,CUSTOM_W>' +object1 INTERFACE_CUSTOM_W: '$<TARGET_PROPERTY:object1,INTERFACE_CUSTOM_W>' +main CUSTOM_A: '$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_A>' +main INTERFACE_CUSTOM_A: '$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_CUSTOM_A>' +main CUSTOM_B: '$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_B>' +main INTERFACE_CUSTOM_B: '$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_CUSTOM_B>' +main CUSTOM_C: '$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_C>' +main INTERFACE_CUSTOM_C: '$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_CUSTOM_C>' +main CUSTOM_U: '$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_U>' +main INTERFACE_CUSTOM_U: '$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_CUSTOM_U>' +main CUSTOM_V: '$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_V>' +main INTERFACE_CUSTOM_V: '$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_CUSTOM_V>' +main CUSTOM_W: '$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_W>' +main INTERFACE_CUSTOM_W: '$<TARGET_PROPERTY:CustomTransitiveProperties,INTERFACE_CUSTOM_W>' +") +add_custom_target(check ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -Dconfig=$<CONFIG> -Dout=${out} -P${CMAKE_CURRENT_SOURCE_DIR}/check.cmake + COMMAND CustomTransitiveProperties + "$<TARGET_PROPERTY:static1,CUSTOM_A>" "CUSTOM_A_STATIC1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_STATIC1" + "$<TARGET_PROPERTY:static1,CUSTOM_B>" "CUSTOM_B_STATIC1;CUSTOM_B_IFACE1" + "$<TARGET_PROPERTY:static1,CUSTOM_U>" "CUSTOM_U_STATIC1;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_STATIC1" + "$<TARGET_PROPERTY:static1,CUSTOM_V>" "CUSTOM_V_STATIC1;CUSTOM_V_IFACE1" + "$<TARGET_PROPERTY:object1,CUSTOM_A>" "CUSTOM_A_OBJECT1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_OBJECT1" + "$<TARGET_PROPERTY:object1,CUSTOM_C>" "CUSTOM_C_OBJECT1;CUSTOM_C_IFACE1" + "$<TARGET_PROPERTY:object1,CUSTOM_U>" "CUSTOM_U_OBJECT1;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_OBJECT1" + "$<TARGET_PROPERTY:object1,CUSTOM_W>" "CUSTOM_W_OBJECT1;CUSTOM_W_IFACE1" + "$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_A>" "CUSTOM_A_MAIN" + "$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_B>" "CUSTOM_B_MAIN;CUSTOM_B_STATIC1_IFACE" + "$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_C>" "CUSTOM_C_MAIN;CUSTOM_C_OBJECT1_IFACE" + "$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_U>" "CUSTOM_U_MAIN;CUSTOM_U_STATIC1_IFACE;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_EXECUTABLE;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_CUSTOMTRANSITIVEPROPERTIES;CUSTOM_U_OBJECT1_IFACE" + "$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_V>" "CUSTOM_V_MAIN;CUSTOM_V_STATIC1_IFACE;CUSTOM_V_IFACE1" + "$<TARGET_PROPERTY:CustomTransitiveProperties,CUSTOM_W>" "CUSTOM_W_MAIN;CUSTOM_W_IFACE1;CUSTOM_W_OBJECT1_IFACE" + )
diff --git a/Tests/CustomTransitiveProperties/check.cmake b/Tests/CustomTransitiveProperties/check.cmake new file mode 100644 index 0000000..d7130c8 --- /dev/null +++ b/Tests/CustomTransitiveProperties/check.cmake
@@ -0,0 +1,47 @@ +set(expect [[ +# file\(GENERATE\) produced: +iface1 CUSTOM_A: '' +iface1 INTERFACE_CUSTOM_A: 'CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_IFACE1' +iface2 CUSTOM_A: '' +iface2 INTERFACE_CUSTOM_A: 'CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_INTERFACE_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_IFACE2' +static1 CUSTOM_A: 'CUSTOM_A_STATIC1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_STATIC1' +static1 INTERFACE_CUSTOM_A: 'CUSTOM_A_STATIC1_IFACE' +static1 CUSTOM_B: 'CUSTOM_B_STATIC1;CUSTOM_B_IFACE1' +static1 INTERFACE_CUSTOM_B: 'CUSTOM_B_STATIC1_IFACE' +static1 CUSTOM_U: 'CUSTOM_U_STATIC1;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_STATIC1' +static1 INTERFACE_CUSTOM_U: 'CUSTOM_U_STATIC1_IFACE;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_STATIC_LIBRARY;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_STATIC1' +static1 CUSTOM_V: 'CUSTOM_V_STATIC1;CUSTOM_V_IFACE1' +static1 INTERFACE_CUSTOM_V: 'CUSTOM_V_STATIC1_IFACE;CUSTOM_V_IFACE1' +object1 CUSTOM_A: 'CUSTOM_A_OBJECT1;CUSTOM_A_IFACE2;CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_A_IFACE1;CUSTOM_A_TARGET_NAME_OBJECT1' +object1 INTERFACE_CUSTOM_A: 'CUSTOM_A_OBJECT1_IFACE' +object1 CUSTOM_C: 'CUSTOM_C_OBJECT1;CUSTOM_C_IFACE1' +object1 INTERFACE_CUSTOM_C: 'CUSTOM_C_OBJECT1_IFACE' +object1 CUSTOM_U: 'CUSTOM_U_OBJECT1;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_OBJECT1' +object1 INTERFACE_CUSTOM_U: 'CUSTOM_U_OBJECT1_IFACE;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_OBJECT_LIBRARY;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_OBJECT1' +object1 CUSTOM_W: 'CUSTOM_W_OBJECT1;CUSTOM_W_IFACE1' +object1 INTERFACE_CUSTOM_W: 'CUSTOM_W_OBJECT1_IFACE;CUSTOM_W_IFACE1' +main CUSTOM_A: 'CUSTOM_A_MAIN' +main INTERFACE_CUSTOM_A: '' +main CUSTOM_B: 'CUSTOM_B_MAIN;CUSTOM_B_STATIC1_IFACE' +main INTERFACE_CUSTOM_B: '' +main CUSTOM_C: 'CUSTOM_C_MAIN;CUSTOM_C_OBJECT1_IFACE' +main INTERFACE_CUSTOM_C: '' +main CUSTOM_U: 'CUSTOM_U_MAIN;CUSTOM_U_STATIC1_IFACE;CUSTOM_U_IFACE2;CUSTOM_U_TARGET_TYPE_EXECUTABLE;CUSTOM_U_IFACE1;CUSTOM_U_TARGET_NAME_CUSTOMTRANSITIVEPROPERTIES;CUSTOM_U_OBJECT1_IFACE' +main INTERFACE_CUSTOM_U: '' +main CUSTOM_V: 'CUSTOM_V_MAIN;CUSTOM_V_STATIC1_IFACE;CUSTOM_V_IFACE1' +main INTERFACE_CUSTOM_V: '' +main CUSTOM_W: 'CUSTOM_W_MAIN;CUSTOM_W_IFACE1;CUSTOM_W_OBJECT1_IFACE' +main INTERFACE_CUSTOM_W: '' +]]) +string(REGEX REPLACE "\r\n" "\n" expect "${expect}") +string(REGEX REPLACE "\n+$" "" expect "${expect}") + +file(READ "${out}" actual) +string(REGEX REPLACE "\r\n" "\n" actual "${actual}") +string(REGEX REPLACE "\n+$" "" actual "${actual}") + +if(NOT actual MATCHES "^${expect}$") + string(REPLACE "\n" "\n expect> " expect " expect> ${expect}") + string(REPLACE "\n" "\n actual> " actual " actual> ${actual}") + message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}") +endif()
diff --git a/Tests/CustomTransitiveProperties/main.c b/Tests/CustomTransitiveProperties/main.c new file mode 100644 index 0000000..ab70eb0 --- /dev/null +++ b/Tests/CustomTransitiveProperties/main.c
@@ -0,0 +1,137 @@ +#include <stdio.h> +#include <string.h> + +#ifdef CUSTOM_A_IFACE1 +# error "CUSTOM_A_IFACE1 incorrectly defined" +#endif + +#ifdef CUSTOM_A_IFACE2 +# error "CUSTOM_A_IFACE2 incorrectly defined" +#endif + +#ifdef CUSTOM_A_STATIC1_IFACE +# error "CUSTOM_A_STATIC1_IFACE incorrectly defined" +#endif + +#ifdef CUSTOM_A_OBJECT1_IFACE +# error "CUSTOM_A_OBJECT1_IFACE incorrectly defined" +#endif + +#ifndef CUSTOM_A_MAIN +# error "CUSTOM_A_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_B_IFACE1 +# error "CUSTOM_B_IFACE1 incorrectly defined" +#endif + +#ifndef CUSTOM_B_STATIC1_IFACE +# error "CUSTOM_B_STATIC1_IFACE incorrectly not defined" +#endif + +#ifndef CUSTOM_B_MAIN +# error "CUSTOM_B_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_C_IFACE1 +# error "CUSTOM_C_IFACE1 incorrectly defined" +#endif + +#ifndef CUSTOM_C_OBJECT1_IFACE +# error "CUSTOM_C_OBJECT1_IFACE incorrectly not defined" +#endif + +#ifndef CUSTOM_C_MAIN +# error "CUSTOM_C_MAIN incorrectly not defined" +#endif + +#ifndef CUSTOM_U_IFACE1 +# error "CUSTOM_U_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_IFACE2 +# error "CUSTOM_U_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_MAIN +# error "CUSTOM_U_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_U_STATIC1 +# error "CUSTOM_U_STATIC1 incorrectly defined" +#endif + +#ifndef CUSTOM_U_STATIC1_IFACE +# error "CUSTOM_U_STATIC1_IFACE incorrectly not defined" +#endif + +#ifdef CUSTOM_U_OBJECT1 +# error "CUSTOM_U_OBJECT1 incorrectly defined" +#endif + +#ifndef CUSTOM_U_OBJECT1_IFACE +# error "CUSTOM_U_OBJECT1_IFACE incorrectly not defined" +#endif + +#ifndef CUSTOM_U_TARGET_NAME_CUSTOMTRANSITIVEPROPERTIES +# error \ + "CUSTOM_U_TARGET_NAME_CUSTOMTRANSITIVEPROPERTIES incorrectly not defined" +#endif + +#ifndef CUSTOM_U_TARGET_TYPE_EXECUTABLE +# error "CUSTOM_U_TARGET_TYPE_EXECUTABLE incorrectly not defined" +#endif + +#ifndef CUSTOM_V_IFACE1 +# error "CUSTOM_V_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_V_MAIN +# error "CUSTOM_V_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_V_STATIC1 +# error "CUSTOM_V_STATIC1 incorrectly defined" +#endif + +#ifndef CUSTOM_V_STATIC1_IFACE +# error "CUSTOM_V_STATIC1_IFACE incorrectly not defined" +#endif + +#ifndef CUSTOM_W_IFACE1 +# error "CUSTOM_W_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_W_MAIN +# error "CUSTOM_W_MAIN incorrectly not defined" +#endif + +#ifdef CUSTOM_W_OBJECT1 +# error "CUSTOM_W_OBJECT1 incorrectly defined" +#endif + +#ifndef CUSTOM_W_OBJECT1_IFACE +# error "CUSTOM_W_OBJECT1_IFACE incorrectly not defined" +#endif + +extern int static1(void); +extern int object1(void); + +int check_args(int argc, char** argv) +{ + int result = 0; + int i; + for (i = 2; i < argc; i += 2) { + if (strcmp(argv[i - 1], argv[i]) != 0) { + fprintf(stderr, "Argument %d expected '%s' but got '%s'.\n", i, argv[i], + argv[i - 1]); + result = 1; + } + } + return result; +} + +int main(int argc, char** argv) +{ + return static1() + object1() + check_args(argc, argv); +}
diff --git a/Tests/CustomTransitiveProperties/object1.c b/Tests/CustomTransitiveProperties/object1.c new file mode 100644 index 0000000..4454118 --- /dev/null +++ b/Tests/CustomTransitiveProperties/object1.c
@@ -0,0 +1,64 @@ +#ifndef CUSTOM_A_IFACE1 +# error "CUSTOM_A_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_IFACE2 +# error "CUSTOM_A_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_OBJECT1 +# error "CUSTOM_A_OBJECT1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_NAME_OBJECT1 +# error "CUSTOM_A_TARGET_NAME_OBJECT1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY +# error "CUSTOM_A_TARGET_TYPE_OBJECT_LIBRARY incorrectly not defined" +#endif + +#ifndef CUSTOM_C_IFACE1 +# error "CUSTOM_C_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_C_OBJECT1 +# error "CUSTOM_C_OBJECT1 incorrectly not defined" +#endif + +#ifdef CUSTOM_C_OBJECT1_IFACE +# error "CUSTOM_C_OBJECT1_IFACE incorrectly defined" +#endif + +#ifndef CUSTOM_U_IFACE1 +# error "CUSTOM_U_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_IFACE2 +# error "CUSTOM_U_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_OBJECT1 +# error "CUSTOM_U_OBJECT1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_TARGET_NAME_OBJECT1 +# error "CUSTOM_U_TARGET_NAME_OBJECT1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_TARGET_TYPE_OBJECT_LIBRARY +# error "CUSTOM_U_TARGET_TYPE_OBJECT_LIBRARY incorrectly not defined" +#endif + +#ifndef CUSTOM_W_IFACE1 +# error "CUSTOM_W_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_W_OBJECT1 +# error "CUSTOM_W_OBJECT1 incorrectly not defined" +#endif + +int object1(void) +{ + return 0; +}
diff --git a/Tests/CustomTransitiveProperties/static1.c b/Tests/CustomTransitiveProperties/static1.c new file mode 100644 index 0000000..45e9507 --- /dev/null +++ b/Tests/CustomTransitiveProperties/static1.c
@@ -0,0 +1,64 @@ +#ifndef CUSTOM_A_IFACE1 +# error "CUSTOM_A_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_IFACE2 +# error "CUSTOM_A_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_STATIC1 +# error "CUSTOM_A_STATIC1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_NAME_STATIC1 +# error "CUSTOM_A_TARGET_NAME_STATIC1 incorrectly not defined" +#endif + +#ifndef CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY +# error "CUSTOM_A_TARGET_TYPE_STATIC_LIBRARY incorrectly not defined" +#endif + +#ifndef CUSTOM_B_IFACE1 +# error "CUSTOM_B_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_B_STATIC1 +# error "CUSTOM_B_STATIC1 incorrectly not defined" +#endif + +#ifdef CUSTOM_B_STATIC1_IFACE +# error "CUSTOM_B_STATIC1_IFACE incorrectly defined" +#endif + +#ifndef CUSTOM_U_IFACE1 +# error "CUSTOM_U_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_IFACE2 +# error "CUSTOM_U_IFACE2 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_STATIC1 +# error "CUSTOM_U_STATIC1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_TARGET_NAME_STATIC1 +# error "CUSTOM_U_TARGET_NAME_STATIC1 incorrectly not defined" +#endif + +#ifndef CUSTOM_U_TARGET_TYPE_STATIC_LIBRARY +# error "CUSTOM_U_TARGET_TYPE_STATIC_LIBRARY incorrectly not defined" +#endif + +#ifndef CUSTOM_V_IFACE1 +# error "CUSTOM_V_IFACE1 incorrectly not defined" +#endif + +#ifndef CUSTOM_V_STATIC1 +# error "CUSTOM_V_STATIC1 incorrectly not defined" +#endif + +int static1(void) +{ + return 0; +}
diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 0b7f739..28118ba 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt
@@ -29,6 +29,9 @@ target_compile_definitions(compileOnly INTERFACE FROM_compileOnly) target_link_options(compileOnly INTERFACE -fthis-flag-does-not-exist) +add_library(noUses INTERFACE) +target_link_libraries(noUses INTERFACE this::target_does_not_exist) + add_library(testLib1 STATIC testLib1.c) add_library(testLib2 STATIC testLib2.c) target_link_libraries(testLib2 testLib1) @@ -116,6 +119,37 @@ target_link_libraries(testLib9 PUBLIC Foo::Foo) cmake_policy(POP) +block() + cmake_policy(SET CMP0022 NEW) + add_library(testLib10 STATIC testLib10.c) + set_target_properties(testLib10 PROPERTIES + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_C" + TRANSITIVE_LINK_PROPERTIES "CUSTOM_L" + INTERFACE_CUSTOM_C "TESTLIB10_INTERFACE_CUSTOM_C" + INTERFACE_CUSTOM_L "TESTLIB10_INTERFACE_CUSTOM_L" + ) + target_compile_definitions(testLib10 INTERFACE + "$<TARGET_PROPERTY:CUSTOM_C>" + "$<TARGET_PROPERTY:CUSTOM_L>" + ) + add_library(testLib11 STATIC testLib11.c) + target_link_libraries(testLib11 PRIVATE testLib10) + set_target_properties(testLib11 PROPERTIES + INTERFACE_CUSTOM_C "TESTLIB11_INTERFACE_CUSTOM_C" + INTERFACE_CUSTOM_L "TESTLIB11_INTERFACE_CUSTOM_L" + TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_D" + TRANSITIVE_LINK_PROPERTIES "CUSTOM_M" + INTERFACE_CUSTOM_D "TESTLIB11_INTERFACE_CUSTOM_D" + INTERFACE_CUSTOM_M "TESTLIB11_INTERFACE_CUSTOM_M" + ) + target_compile_definitions(testLib11 INTERFACE + "$<TARGET_PROPERTY:CUSTOM_C>" + "$<TARGET_PROPERTY:CUSTOM_D>" + "$<TARGET_PROPERTY:CUSTOM_L>" + "$<TARGET_PROPERTY:CUSTOM_M>" + ) +endblock() + # Test using the target_link_libraries command to set the # LINK_INTERFACE_LIBRARIES* properties. We construct two libraries # providing the same two symbols. In each library one of the symbols @@ -570,10 +604,12 @@ install( TARGETS compileOnly + noUses testExe1 testLib1 testLib2 testExe2 testLib3 testLib4 testExe3 testExe4 testExe2lib testLib4lib testLib4libdbg testLib4libopt testLib6 testLib7 testLib8 testLib9 + testLib10 testLib11 testLibDeprecation testLibCycleA testLibCycleB testLibNoSONAME @@ -644,7 +680,7 @@ testSharedLibDepends renamed_on_export cmp0022NEW cmp0022OLD TopDirLib SubDirLinkA - systemlib + systemlib noUses testInterfaceIncludeUser testInterfaceIncludeUser2 NAMESPACE bld_ @@ -653,6 +689,7 @@ export(TARGETS testExe2 testLib4 testLib5 testLib6 testLib7 testExe3 testExe4 testExe2lib testLib8 testLib9 testLib9ObjPub testLib9ObjPriv testLib9ObjIface + testLib10 testLib11 testLibDeprecation testLib4lib testLib4libdbg testLib4libopt testLibCycleA testLibCycleB
diff --git a/Tests/ExportImport/Export/testLib10.c b/Tests/ExportImport/Export/testLib10.c new file mode 100644 index 0000000..d5ecb7f --- /dev/null +++ b/Tests/ExportImport/Export/testLib10.c
@@ -0,0 +1,4 @@ +int testLib10(void) +{ + return 0; +}
diff --git a/Tests/ExportImport/Export/testLib11.c b/Tests/ExportImport/Export/testLib11.c new file mode 100644 index 0000000..b288b29 --- /dev/null +++ b/Tests/ExportImport/Export/testLib11.c
@@ -0,0 +1,6 @@ +int testLib10(void); + +int testLib11(void) +{ + return testLib10(); +}
diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 2a57633..632825d 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt
@@ -328,6 +328,16 @@ endif() endforeach() +# Create executables to verify custom transitive properties. +add_executable(imp_testLib10 imp_testLib10.c) +target_link_libraries(imp_testLib10 PRIVATE exp_testLib10) +add_executable(imp_testLib10b imp_testLib10.c) +target_link_libraries(imp_testLib10b PRIVATE bld_testLib10) +add_executable(imp_testLib11 imp_testLib11.c) +target_link_libraries(imp_testLib11 PRIVATE exp_testLib11) +add_executable(imp_testLib11b imp_testLib11.c) +target_link_libraries(imp_testLib11b PRIVATE bld_testLib11) + #----------------------------------------------------------------------------- # Test that handling imported targets, including transitive dependencies, # works in CheckFunctionExists (...and hopefully all other try_compile() checks
diff --git a/Tests/ExportImport/Import/A/imp_testLib10.c b/Tests/ExportImport/Import/A/imp_testLib10.c new file mode 100644 index 0000000..5d6e823 --- /dev/null +++ b/Tests/ExportImport/Import/A/imp_testLib10.c
@@ -0,0 +1,14 @@ +#ifndef TESTLIB10_INTERFACE_CUSTOM_C +# error "TESTLIB10_INTERFACE_CUSTOM_C incorrectly not defined!" +#endif + +#ifndef TESTLIB10_INTERFACE_CUSTOM_L +# error "TESTLIB10_INTERFACE_CUSTOM_L incorrectly not defined!" +#endif + +int testLib10(void); + +int main(void) +{ + return testLib10(); +}
diff --git a/Tests/ExportImport/Import/A/imp_testLib11.c b/Tests/ExportImport/Import/A/imp_testLib11.c new file mode 100644 index 0000000..e8e10e6 --- /dev/null +++ b/Tests/ExportImport/Import/A/imp_testLib11.c
@@ -0,0 +1,30 @@ +#ifdef TESTLIB10_INTERFACE_CUSTOM_C +# error "TESTLIB10_INTERFACE_CUSTOM_C incorrectly defined!" +#endif + +#ifdef TESTLIB11_INTERFACE_CUSTOM_C +# error "TESTLIB11_INTERFACE_CUSTOM_C incorrectly defined!" +#endif + +#ifndef TESTLIB11_INTERFACE_CUSTOM_D +# error "TESTLIB11_INTERFACE_CUSTOM_D incorrectly not defined!" +#endif + +#ifndef TESTLIB10_INTERFACE_CUSTOM_L +# error "TESTLIB10_INTERFACE_CUSTOM_L incorrectly not defined!" +#endif + +#ifndef TESTLIB11_INTERFACE_CUSTOM_L +# error "TESTLIB11_INTERFACE_CUSTOM_L incorrectly not defined!" +#endif + +#ifndef TESTLIB11_INTERFACE_CUSTOM_M +# error "TESTLIB11_INTERFACE_CUSTOM_M incorrectly not defined!" +#endif + +int testLib11(void); + +int main(void) +{ + return testLib11(); +}
diff --git a/Tests/FindBacktrace/CMakeLists.txt b/Tests/FindBacktrace/CMakeLists.txt new file mode 100644 index 0000000..8ee2a4d --- /dev/null +++ b/Tests/FindBacktrace/CMakeLists.txt
@@ -0,0 +1,10 @@ +add_test(NAME FindBacktrace.Test COMMAND + ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindBacktrace/Test" + "${CMake_BINARY_DIR}/Tests/FindBacktrace/Test" + ${build_generator_args} + --build-project TestFindBacktrace + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> + )
diff --git a/Tests/FindBacktrace/Test/CMakeLists.txt b/Tests/FindBacktrace/Test/CMakeLists.txt new file mode 100644 index 0000000..7f5d8ec --- /dev/null +++ b/Tests/FindBacktrace/Test/CMakeLists.txt
@@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.29) +project(TestFindBLAS C) +include(CTest) + +find_package(Backtrace REQUIRED) + +add_executable(test_tgt backtrace.c) +target_link_libraries(test_tgt Backtrace::Backtrace) +target_compile_options(test_tgt PUBLIC -rdynamic -fno-omit-frame-pointer) +target_link_options(test_tgt PUBLIC -rdynamic -fno-omit-frame-pointer) +add_test(NAME test_tgt COMMAND test_tgt)
diff --git a/Tests/FindBacktrace/Test/backtrace.c b/Tests/FindBacktrace/Test/backtrace.c new file mode 100644 index 0000000..e143bc7 --- /dev/null +++ b/Tests/FindBacktrace/Test/backtrace.c
@@ -0,0 +1,56 @@ +/* This is the code from `man backtrace_symbols`, reformatted, and without + * requiring a command-line argument */ + +#include <execinfo.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define BT_BUF_SIZE 100 + +void myfunc3(void) +{ + int nptrs; + void* buffer[BT_BUF_SIZE]; + char** strings; + size_t j; + + nptrs = backtrace(buffer, BT_BUF_SIZE); + printf("backtrace() returned %d addresses\n", nptrs); + + /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) + would produce similar output to the following: */ + + strings = backtrace_symbols(buffer, nptrs); + if (strings == NULL) { + perror("backtrace_symbols"); + exit(EXIT_FAILURE); + } + + for (j = 0; j < nptrs; j++) { + printf("%s\n", strings[j]); + } + + free(strings); +} + +static void /* "static" means don't export the symbol... */ +myfunc2(void) +{ + myfunc3(); +} + +void myfunc(int ncalls) +{ + if (ncalls > 1) { + myfunc(ncalls - 1); + } else { + myfunc2(); + } +} + +int main(int argc, char* argv[]) +{ + myfunc(5); + exit(EXIT_SUCCESS); +}
diff --git a/Tests/FindGTK2/gdk/main.c b/Tests/FindGTK2/gdk/main.c index 71f523b..2d03fbe 100644 --- a/Tests/FindGTK2/gdk/main.c +++ b/Tests/FindGTK2/gdk/main.c
@@ -2,6 +2,6 @@ int main(int argc, char* argv[]) { - gdk_init(argc, argv); + gdk_init(&argc, &argv); return 0; }
diff --git a/Tests/FindJasper/Test/CMakeLists.txt b/Tests/FindJasper/Test/CMakeLists.txt index 1e9467d..93873bf 100644 --- a/Tests/FindJasper/Test/CMakeLists.txt +++ b/Tests/FindJasper/Test/CMakeLists.txt
@@ -4,7 +4,7 @@ find_package(Jasper) -add_definitions(-DCMAKE_EXPECTED_JASPER_VERSION=${JASPER_VERSION_STRING}) +add_definitions(-DCMAKE_EXPECTED_JASPER_VERSION="${JASPER_VERSION_STRING}") add_executable(test_jasper_tgt main.c) target_link_libraries(test_jasper_tgt Jasper::Jasper)
diff --git a/Tests/FindJasper/Test/main.c b/Tests/FindJasper/Test/main.c index 242ff7d..b9dbe1e 100644 --- a/Tests/FindJasper/Test/main.c +++ b/Tests/FindJasper/Test/main.c
@@ -1,17 +1,11 @@ -#include <assert.h> -// clang-format off -#include <stdio.h> #include <jasper/jasper.h> -// clang-format on +#include <string.h> int main(void) { - /* Without any JPEG file to open, test that the call fails as - expected. This tests that linking worked. */ - jas_init(); - jas_image_t* img = jas_image_create0(); - jas_image_destroy(img); - jas_cleanup(); - - return (JAS_VERSION != CMAKE_EXPECTED_JASPER_VERSION); + jas_conf_clear(); + jas_conf_set_max_mem_usage(0x100000); + jas_init_library(); + jas_cleanup_library(); + return strcmp(JAS_VERSION, CMAKE_EXPECTED_JASPER_VERSION); }
diff --git a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt index 58db0ec..7e1bbae 100644 --- a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt +++ b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt
@@ -58,7 +58,7 @@ # Link separately with Matlab::mx and Matlab::mex to ensure that compilation # and run of the test is successful -target_link_libraries(cmake_matlab_test_wrapper1 PRIVATE Matlab::mx Matlab::mex) +target_link_libraries(cmake_matlab_test_wrapper1 Matlab::mx Matlab::mex) if(RUN_UNIT_TESTS) matlab_add_unit_test(
diff --git a/Tests/FindMatlab/versions_checks/CMakeLists.txt b/Tests/FindMatlab/versions_checks/CMakeLists.txt index f203f4b..9a9e9a3 100644 --- a/Tests/FindMatlab/versions_checks/CMakeLists.txt +++ b/Tests/FindMatlab/versions_checks/CMakeLists.txt
@@ -40,6 +40,9 @@ message(FATAL_ERROR "version 14.9 does not give release dummy : '${release_name}' != dummy") endif() +# matlab_get_release_name_from_version does not describe what it does with release_name +# if the version requested does not exists, so make sure that release_name it is empty +set(release_name "") matlab_get_release_name_from_version("14.10" release_name) if(NOT release_name STREQUAL "") message(FATAL_ERROR "version 14.10 does not give empty release: '${release_name}' != ''")
diff --git a/Tests/FindOpenACC/CMakeLists.txt b/Tests/FindOpenACC/CMakeLists.txt index ef7de65..1b6bcf2 100644 --- a/Tests/FindOpenACC/CMakeLists.txt +++ b/Tests/FindOpenACC/CMakeLists.txt
@@ -1,11 +1,5 @@ - -set(langs C CXX) -if(NOT CMAKE_GENERATOR STREQUAL "Ninja") - list(APPEND langs Fortran) -endif() - -foreach(lang IN LISTS langs) - if(CMAKE_${lang}_COMPILER) +foreach(lang IN ITEMS C CXX Fortran) + if(CMake_TEST_FindOpenACC_${lang}) add_test(NAME FindOpenACC.Test${lang} COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> --build-and-test
diff --git a/Tests/FindOpenMP/Test/CMakeLists.txt b/Tests/FindOpenMP/Test/CMakeLists.txt index ebdb6b8..7ead835 100644 --- a/Tests/FindOpenMP/Test/CMakeLists.txt +++ b/Tests/FindOpenMP/Test/CMakeLists.txt
@@ -26,8 +26,21 @@ endif() endforeach() +if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" + AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.30 + AND NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64") + set(test_msvc_runtime 1) + set(OpenMP_RUNTIME_MSVC "llvm") +endif() + find_package(OpenMP REQUIRED) +if(test_msvc_runtime) + if(NOT OpenMP_C_FLAGS STREQUAL "-openmp:llvm") + message(FATAL_ERROR "OpenMP_RUNTIME_MSVC='${OpenMP_RUNTIME_MSVC}' not honored: '${OpenMP_C_FLAGS}'") + endif() +endif() + foreach(c C CXX Fortran) if(NOT "${OpenMP_TEST_${c}}") continue()
diff --git a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt index 18f8fda..f92db5f 100644 --- a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt +++ b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestArtifactsInteractive LANGUAGES C)
diff --git a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt index e0148f3..e9d14f5 100644 --- a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt +++ b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestCustomFailureMessage LANGUAGES NONE)
diff --git a/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt index 0fb3036..d72d258 100644 --- a/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt +++ b/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestCustomFailureMessage.Check LANGUAGES NONE)
diff --git a/Tests/FindPython/DifferentComponents/CMakeLists.txt b/Tests/FindPython/DifferentComponents/CMakeLists.txt index e3e7b36..e72b4bf 100644 --- a/Tests/FindPython/DifferentComponents/CMakeLists.txt +++ b/Tests/FindPython/DifferentComponents/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestDifferentComponents LANGUAGES C)
diff --git a/Tests/FindPython/ExactVersion/CMakeLists.txt b/Tests/FindPython/ExactVersion/CMakeLists.txt index 1bd94c4..e3bb43b 100644 --- a/Tests/FindPython/ExactVersion/CMakeLists.txt +++ b/Tests/FindPython/ExactVersion/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestExactVersion LANGUAGES C)
diff --git a/Tests/FindPython/FindPythonScript.cmake b/Tests/FindPython/FindPythonScript.cmake index 808496e..fa51f59 100644 --- a/Tests/FindPython/FindPythonScript.cmake +++ b/Tests/FindPython/FindPythonScript.cmake
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.15) if (PYTHON_MUST_NOT_BE_FOUND) find_package(${PYTHON_PACKAGE_NAME} QUIET) if (${PYTHON_PACKAGE_NAME}_FOUND)
diff --git a/Tests/FindPython/Implementation/CMakeLists.txt b/Tests/FindPython/Implementation/CMakeLists.txt index 8086c16..da33034 100644 --- a/Tests/FindPython/Implementation/CMakeLists.txt +++ b/Tests/FindPython/Implementation/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestImplementation${Python_REQUESTED_IMPLEMENTATION} LANGUAGES NONE)
diff --git a/Tests/FindPython/IronPython/CMakeLists.txt b/Tests/FindPython/IronPython/CMakeLists.txt index fd3182e..3c28527 100644 --- a/Tests/FindPython/IronPython/CMakeLists.txt +++ b/Tests/FindPython/IronPython/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestIronPython LANGUAGES NONE)
diff --git a/Tests/FindPython/IronPython2/CMakeLists.txt b/Tests/FindPython/IronPython2/CMakeLists.txt index b623972..0a4dcb4 100644 --- a/Tests/FindPython/IronPython2/CMakeLists.txt +++ b/Tests/FindPython/IronPython2/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestIronPython2 LANGUAGES NONE)
diff --git a/Tests/FindPython/IronPython3/CMakeLists.txt b/Tests/FindPython/IronPython3/CMakeLists.txt index b09097a..f39b84d 100644 --- a/Tests/FindPython/IronPython3/CMakeLists.txt +++ b/Tests/FindPython/IronPython3/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestIronPython3 LANGUAGES NONE)
diff --git a/Tests/FindPython/MultiplePackages/CMakeLists.txt b/Tests/FindPython/MultiplePackages/CMakeLists.txt index 352a2f6f..6536e46 100644 --- a/Tests/FindPython/MultiplePackages/CMakeLists.txt +++ b/Tests/FindPython/MultiplePackages/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestMultiplePackages C) @@ -20,7 +20,7 @@ add_test (NAME python2_spam2 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>" - "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")") + "${Python2_INTERPRETER}" -c "import spam2; spam2.system(\"cd\")") endif() @@ -40,6 +40,6 @@ add_test (NAME python3_spam3 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>" - "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")") + "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")") endif()
diff --git a/Tests/FindPython/NumPy/CMakeLists.txt b/Tests/FindPython/NumPy/CMakeLists.txt index 336bb83..c5552d8 100644 --- a/Tests/FindPython/NumPy/CMakeLists.txt +++ b/Tests/FindPython/NumPy/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestNumPy LANGUAGES C) @@ -12,7 +12,7 @@ add_test (NAME python2_arraytest COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest2>" - "${Python2_EXECUTABLE}" -c "import numpy; import arraytest2; arraytest2.vecsq(numpy.array([1, 2, 3]));") + "${Python2_INTERPRETER}" -c "import numpy; import arraytest2; arraytest2.vecsq(numpy.array([1, 2, 3]));") endif() @@ -26,6 +26,6 @@ add_test (NAME python3_arraytest COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest3>" - "${Python3_EXECUTABLE}" -c "import numpy; import arraytest3; arraytest3.vecsq(numpy.array([1, 2, 3]));") + "${Python3_INTERPRETER}" -c "import numpy; import arraytest3; arraytest3.vecsq(numpy.array([1, 2, 3]));") endif()
diff --git a/Tests/FindPython/NumPyOnly/CMakeLists.txt b/Tests/FindPython/NumPyOnly/CMakeLists.txt index 115cf2b..db7e68e 100644 --- a/Tests/FindPython/NumPyOnly/CMakeLists.txt +++ b/Tests/FindPython/NumPyOnly/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestNumPyOnly LANGUAGES C)
diff --git a/Tests/FindPython/PyPy/CMakeLists.txt b/Tests/FindPython/PyPy/CMakeLists.txt index dfc22d8..2e8bc58 100644 --- a/Tests/FindPython/PyPy/CMakeLists.txt +++ b/Tests/FindPython/PyPy/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPyPy LANGUAGES C)
diff --git a/Tests/FindPython/PyPy2/CMakeLists.txt b/Tests/FindPython/PyPy2/CMakeLists.txt index 5b7ce30..2d89531 100644 --- a/Tests/FindPython/PyPy2/CMakeLists.txt +++ b/Tests/FindPython/PyPy2/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPyPy2 LANGUAGES C)
diff --git a/Tests/FindPython/PyPy3/CMakeLists.txt b/Tests/FindPython/PyPy3/CMakeLists.txt index b702c99..93388a0 100644 --- a/Tests/FindPython/PyPy3/CMakeLists.txt +++ b/Tests/FindPython/PyPy3/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPyPy3 LANGUAGES C)
diff --git a/Tests/FindPython/Python/CMakeLists.txt b/Tests/FindPython/Python/CMakeLists.txt index 85b1711..7074649 100644 --- a/Tests/FindPython/Python/CMakeLists.txt +++ b/Tests/FindPython/Python/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython LANGUAGES C) @@ -32,7 +32,7 @@ add_test (NAME python_spam${Python_REQUESTED_VERSION} COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam${Python_REQUESTED_VERSION}>" - "${Python_EXECUTABLE}" -c "import spam${Python_REQUESTED_VERSION}; spam${Python_REQUESTED_VERSION}.system(\"cd\")") + "${Python_INTERPRETER}" -c "import spam${Python_REQUESTED_VERSION}; spam${Python_REQUESTED_VERSION}.system(\"cd\")") else() add_test(NAME findpython_script COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python
diff --git a/Tests/FindPython/Python2/CMakeLists.txt b/Tests/FindPython/Python2/CMakeLists.txt index 95ed495..f858574 100644 --- a/Tests/FindPython/Python2/CMakeLists.txt +++ b/Tests/FindPython/Python2/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython2 LANGUAGES C) @@ -39,7 +39,7 @@ add_test (NAME python2_spam2 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam2>" - "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")") + "${Python2_INTERPRETER}" -c "import spam2; spam2.system(\"cd\")") add_test(NAME findpython2_script COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python2
diff --git a/Tests/FindPython/Python2Embedded/CMakeLists.txt b/Tests/FindPython/Python2Embedded/CMakeLists.txt index d9b2d22..0a04f70 100644 --- a/Tests/FindPython/Python2Embedded/CMakeLists.txt +++ b/Tests/FindPython/Python2Embedded/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython2Embedded LANGUAGES C)
diff --git a/Tests/FindPython/Python2Fail/CMakeLists.txt b/Tests/FindPython/Python2Fail/CMakeLists.txt index 7a6520d..7f7b906 100644 --- a/Tests/FindPython/Python2Fail/CMakeLists.txt +++ b/Tests/FindPython/Python2Fail/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython2Fail C)
diff --git a/Tests/FindPython/Python2Module/CMakeLists.txt b/Tests/FindPython/Python2Module/CMakeLists.txt index 7334d7a..6fffad4 100644 --- a/Tests/FindPython/Python2Module/CMakeLists.txt +++ b/Tests/FindPython/Python2Module/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython2Module LANGUAGES C) @@ -34,4 +34,4 @@ add_test (NAME python2_spam2 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam2>" - "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")") + "${Python2_INTERPRETER}" -c "import spam2; spam2.system(\"cd\")")
diff --git a/Tests/FindPython/Python2SABIModule/CMakeLists.txt b/Tests/FindPython/Python2SABIModule/CMakeLists.txt index ffbaa33..c43dedc 100644 --- a/Tests/FindPython/Python2SABIModule/CMakeLists.txt +++ b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython2SABIModule LANGUAGES C)
diff --git a/Tests/FindPython/Python3/CMakeLists.txt b/Tests/FindPython/Python3/CMakeLists.txt index 42d31f2..28f68e1 100644 --- a/Tests/FindPython/Python3/CMakeLists.txt +++ b/Tests/FindPython/Python3/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython3 LANGUAGES C) @@ -39,7 +39,7 @@ add_test (NAME python3_spam3 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>" - "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")") + "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")") add_test(NAME findpython3_script COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python3 @@ -76,6 +76,11 @@ else() list (APPEND Python3_VALID_ABI "OFF") endif() +if (abi MATCHES "t") + list (APPEND Python3_VALID_ABI "ON") +else() + list (APPEND Python3_VALID_ABI "OFF") +endif() # build an invalid pattern for ABI set (Python3_INVALID_ABI) foreach (abi IN LISTS Python3_VALID_ABI)
diff --git a/Tests/FindPython/Python3Embedded/CMakeLists.txt b/Tests/FindPython/Python3Embedded/CMakeLists.txt index 1d362be..46f7042 100644 --- a/Tests/FindPython/Python3Embedded/CMakeLists.txt +++ b/Tests/FindPython/Python3Embedded/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython3Embedded LANGUAGES C)
diff --git a/Tests/FindPython/Python3Fail/CMakeLists.txt b/Tests/FindPython/Python3Fail/CMakeLists.txt index 5eca0cb..810375b 100644 --- a/Tests/FindPython/Python3Fail/CMakeLists.txt +++ b/Tests/FindPython/Python3Fail/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython3Fail C)
diff --git a/Tests/FindPython/Python3Module/CMakeLists.txt b/Tests/FindPython/Python3Module/CMakeLists.txt index 57c0ddf..e47a76a 100644 --- a/Tests/FindPython/Python3Module/CMakeLists.txt +++ b/Tests/FindPython/Python3Module/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestPython3Module LANGUAGES C) @@ -43,4 +43,4 @@ add_test (NAME python3_spam3 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>" - "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")") + "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")")
diff --git a/Tests/FindPython/Python3SABIModule/CMakeLists.txt b/Tests/FindPython/Python3SABIModule/CMakeLists.txt index e045b69..77d3abc 100644 --- a/Tests/FindPython/Python3SABIModule/CMakeLists.txt +++ b/Tests/FindPython/Python3SABIModule/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.15) project(TestPython3SABIModule LANGUAGES C) @@ -52,4 +52,4 @@ add_test (NAME python3_spam3 COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>" - "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")") + "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")")
diff --git a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt index eec28a5..5df01c6 100644 --- a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt +++ b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestRequiredArtifacts LANGUAGES C)
diff --git a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt index 4d9c7c8..25bdcf3 100644 --- a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt +++ b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestRequiredArtifacts.Check LANGUAGES C)
diff --git a/Tests/FindPython/SOABI/CMakeLists.txt b/Tests/FindPython/SOABI/CMakeLists.txt index 6c0e9a9..362df7f 100644 --- a/Tests/FindPython/SOABI/CMakeLists.txt +++ b/Tests/FindPython/SOABI/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestSOABI LANGUAGES C)
diff --git a/Tests/FindPython/VirtualEnv/CMakeLists.txt b/Tests/FindPython/VirtualEnv/CMakeLists.txt index ea742ea..7837916 100644 --- a/Tests/FindPython/VirtualEnv/CMakeLists.txt +++ b/Tests/FindPython/VirtualEnv/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestVirtualEnv LANGUAGES NONE)
diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake index 020ecac..8f56d00 100644 --- a/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake +++ b/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake
@@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.15) find_package (Python3 REQUIRED)
diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake index 29a4924..9ae4975 100644 --- a/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake +++ b/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake
@@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.15) # # Virtual environment is defined for python3
diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake index 89f27d8..353a91b 100644 --- a/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake +++ b/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake
@@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.15) set (Python3_FIND_VIRTUALENV STANDARD) find_package (Python3 REQUIRED)
diff --git a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt index 3a64c31..6482793 100644 --- a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt +++ b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.15) project(TestVirtualEnvConda LANGUAGES NONE)
diff --git a/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake b/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake index 020ecac..8f56d00 100644 --- a/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake +++ b/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake
@@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.15) find_package (Python3 REQUIRED)
diff --git a/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake b/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake index 29a4924..9ae4975 100644 --- a/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake +++ b/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake
@@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.15) # # Virtual environment is defined for python3
diff --git a/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake b/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake index 89f27d8..353a91b 100644 --- a/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake +++ b/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake
@@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.15) set (Python3_FIND_VIRTUALENV STANDARD) find_package (Python3 REQUIRED)
diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index df7cda0..be750e1 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt
@@ -70,6 +70,7 @@ -Dtest_strequal_comma=$<STREQUAL:$<COMMA>,$<COMMA>> -Dtest_strequal_semicolon=$<STREQUAL:$<SEMICOLON>,$<SEMICOLON>> -Dtest_strequal_angle_r_comma=$<STREQUAL:$<ANGLE-R>,$<COMMA>> + -Dtest_strequal_quote=$<STREQUAL:$<QUOTE>,$<QUOTE>> -Dtest_strequal_both_empty=$<STREQUAL:,> -Dtest_strequal_one_empty=$<STREQUAL:something,> -Dtest_inlist_true=$<IN_LIST:a,a$<SEMICOLON>b>
diff --git a/Tests/GeneratorExpression/check-part1.cmake b/Tests/GeneratorExpression/check-part1.cmake index 41bcd6d..ddf2507 100644 --- a/Tests/GeneratorExpression/check-part1.cmake +++ b/Tests/GeneratorExpression/check-part1.cmake
@@ -47,6 +47,7 @@ check(test_strequal_comma "1") check(test_strequal_semicolon "1") check(test_strequal_angle_r_comma "0") +check(test_strequal_quote "1") check(test_strequal_both_empty "1") check(test_strequal_one_empty "0") check(test_inlist_true "1")
diff --git a/Tests/GoogleTest/Test/CMakeLists.txt b/Tests/GoogleTest/Test/CMakeLists.txt index baf00d5..201c39e 100644 --- a/Tests/GoogleTest/Test/CMakeLists.txt +++ b/Tests/GoogleTest/Test/CMakeLists.txt
@@ -81,10 +81,30 @@ if(NOT TEST GoogleTest.Foo) message(FATAL_ERROR "Test case GoogleTest.Foo not defined") endif() +# Check if test has property DEF_SOURCE_LINE +get_test_property(GoogleTest.Foo DEF_SOURCE_LINE testSourceLineFoo) +if(NOT testSourceLineFoo) + message(FATAL_ERROR "Test GoogleTest.Foo should have DEF_SOURCE_LINE property") +endif() +# If check property ends with correct value +string(FIND "${testSourceLineFoo}" "main3.cxx:3" testSourceLineFooFound) +if(${testSourceLineFooFound} EQUAL -1) + message(FATAL_ERROR "Test GoogleTest.Foo should have DEF_SOURCE_LINE property with value main3.cxx:3") +endif() + if(NOT TEST GoogleTest.Bar) message(FATAL_ERROR "Test case GoogleTest.Bar not defined") endif() - +# Check if test has property DEF_SOURCE_LINE +get_test_property(GoogleTest.Bar DEF_SOURCE_LINE testSourceLineBar) +if(NOT testSourceLineBar) + message(FATAL_ERROR "Test GoogleTest.Bar should have DEF_SOURCE_LINE property") +endif() +# If check property ends with correct value +string(FIND "${testSourceLineBar}" "main3.cxx:8" testSourceLineBarFound) +if(${testSourceLineBarFound} EQUAL -1) + message(FATAL_ERROR "Test GoogleTest.Bar should have DEF_SOURCE_LINE property with value main3.cxx:8") +endif() # Non-keyword form, explicitly specified sources. Allows a non-target to be # given for the executable.
diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt index e82cea2..99cad41 100644 --- a/Tests/IncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/CMakeLists.txt
@@ -98,8 +98,8 @@ # NMake and Borland seem to have no way to encode a single '^'. string(APPEND special_chars "^") endif() -if(NOT CMAKE_GENERATOR MATCHES "Visual Studio 9 2008|Watcom WMake") - # The vcproj format separates values with ','. +if(NOT CMAKE_GENERATOR MATCHES "Watcom WMake") + # The wmake format does not support ','. string(APPEND special_chars ",") endif() if(NOT WIN32 AND NOT CYGWIN)
diff --git a/Tests/MSVCRuntimeLibrary/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/CMakeLists.txt index f7d9fec..f1ed9b4 100644 --- a/Tests/MSVCRuntimeLibrary/CMakeLists.txt +++ b/Tests/MSVCRuntimeLibrary/CMakeLists.txt
@@ -57,10 +57,6 @@ # VS 2005 and above default to multi-threaded. target_compile_definitions(empty-${lang} PRIVATE VERIFY_MT) endif() - if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])") - # VS 2010 and above have a different default runtime library for projects than 'cl'. - target_compile_definitions(empty-${lang} PRIVATE VERIFY_DLL) - endif() endif() endfunction()
diff --git a/Tests/NasmOnly/CMakeLists.txt b/Tests/NasmOnly/CMakeLists.txt new file mode 100644 index 0000000..e4190c2 --- /dev/null +++ b/Tests/NasmOnly/CMakeLists.txt
@@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.28) + +project(NasmOnly LANGUAGES ASM_NASM) + +add_library(testnasm1 STATIC libnasm1.nasm) +add_executable(NasmOnly nasmonly.nasm) +target_link_libraries(NasmOnly testnasm1)
diff --git a/Tests/NasmOnly/libnasm1.nasm b/Tests/NasmOnly/libnasm1.nasm new file mode 100644 index 0000000..c72a1ad --- /dev/null +++ b/Tests/NasmOnly/libnasm1.nasm
@@ -0,0 +1,6 @@ +global LibNasm1Func + +section .text +LibNasm1Func: + mov rax, 1 + ret
diff --git a/Tests/NasmOnly/nasmonly.nasm b/Tests/NasmOnly/nasmonly.nasm new file mode 100644 index 0000000..db9b655 --- /dev/null +++ b/Tests/NasmOnly/nasmonly.nasm
@@ -0,0 +1,19 @@ +global _start + +extern LibNasm1Func + +section .text +_start: + xor rax, rax + call LibNasm1Func + cmp rax, 1 + jne err + + mov rax, 60 + xor rdi, rdi + syscall + +err: + mov rax, 60 + mov rdi, 1 + syscall
diff --git a/Tests/README.rst b/Tests/README.rst index 3e98938..2810fec 100644 --- a/Tests/README.rst +++ b/Tests/README.rst
@@ -29,3 +29,7 @@ Tests that run CMake and/or other tools while precisely checking their return code and stdout/stderr content. Useful for testing error cases and diagnostic output. + + See `RunCMake/README.rst`_. + +.. _`RunCMake/README.rst`: RunCMake/README.rst
diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index fada37a..a5f9622 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
@@ -180,8 +180,7 @@ unset(RunCMake_TEST_NO_CLEAN) endif() -if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode" AND - NOT RunCMake_GENERATOR MATCHES "Visual Studio (9|10)( |$)") +if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode") unset(run_BuildDepends_skip_step_3) run_BuildDepends(CustomCommandDepfile) set(run_BuildDepends_skip_step_3 1) @@ -191,8 +190,7 @@ run_BuildDepends(MakeDependencies) endif() -if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 " OR - (RunCMake_GENERATOR MATCHES "Ninja" AND ninja_version VERSION_LESS 1.7)) +if(RunCMake_GENERATOR MATCHES "Ninja" AND ninja_version VERSION_LESS 1.7) # This build tool misses the dependency. set(run_BuildDepends_skip_step_2 1) endif()
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-stderr.txt b/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-stderr.txt deleted file mode 100644 index 1159ec0..0000000 --- a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-stderr.txt +++ /dev/null
@@ -1,6 +0,0 @@ -^CMake Error at CMP0069-NEW-generator\.cmake:[0-9]+ \(add_executable\): - CMake doesn't support IPO for current generator -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator.cmake b/Tests/RunCMake/CMP0069/CMP0069-NEW-generator.cmake deleted file mode 100644 index 80d4e15..0000000 --- a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator.cmake +++ /dev/null
@@ -1,7 +0,0 @@ -cmake_policy(SET CMP0069 NEW) - -set(_CMAKE_CXX_IPO_SUPPORTED_BY_CMAKE YES) -set(_CMAKE_CXX_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES) - -add_executable(foo main.cpp) -set_target_properties(foo PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
diff --git a/Tests/RunCMake/CMP0069/RunCMakeTest.cmake b/Tests/RunCMake/CMP0069/RunCMakeTest.cmake index 456e6a6..6128325 100644 --- a/Tests/RunCMake/CMP0069/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0069/RunCMakeTest.cmake
@@ -4,7 +4,3 @@ run_cmake(CMP0069-NEW-cmake) run_cmake(CMP0069-NEW-compiler) run_cmake(CMP0069-WARN) - -if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ") - run_cmake(CMP0069-NEW-generator) -endif()
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test16.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test16.cmake new file mode 100644 index 0000000..909be6f --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test16.cmake
@@ -0,0 +1,38 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0118-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) + + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_command(TARGET custom2 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) + +add_subdirectory(subdir-Common-Test16) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test1b.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test1b.cmake new file mode 100644 index 0000000..622633f --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test1b.cmake
@@ -0,0 +1,9 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +get_source_file_property(prop + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test2b.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test2b.cmake new file mode 100644 index 0000000..5317781 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test2b.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_source_files_properties( + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTIES GENERATED "1") +get_source_file_property(prop + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test2c.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test2c.cmake new file mode 100644 index 0000000..2f2d3a2 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test2c.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_property(SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED "1") +get_source_file_property(prop + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test2d.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test2d.cmake new file mode 100644 index 0000000..67f9b8c --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test2d.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_source_files_properties( + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTIES GENERATED "1") +get_property(prop SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test3.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test3.cmake index 27516b7..2880aec 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test3.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test3.cmake
@@ -35,7 +35,7 @@ PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_path3.txt") @@ -61,6 +61,6 @@ PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_source_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test3b.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test3b.cmake index 3e03a1f..64294d9 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test3b.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test3b.cmake
@@ -9,7 +9,7 @@ PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_path3.txt") @@ -35,7 +35,7 @@ PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_source_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" PROPERTY GENERATED "1") get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test4.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test4.cmake index 953e26a..dbc603c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test4.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test4.cmake
@@ -35,7 +35,7 @@ PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_path3.txt") @@ -61,6 +61,6 @@ PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_source_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test4b.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test4b.cmake index 025caa8..833714c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test4b.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test4b.cmake
@@ -9,7 +9,7 @@ PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_path3.txt") @@ -35,7 +35,7 @@ PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_source_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" PROPERTY GENERATED "0") get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test5.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test5.cmake index 5349bff..549ce1e 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test5.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test5.cmake
@@ -65,7 +65,7 @@ get_and_print_GENERATED_property("Generated_with_relative_path3.txt") -set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_source_path1.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" PROPERTY GENERATED "tRue") get_and_print_GENERATED_property("Generated_with_full_source_path1.txt") @@ -73,6 +73,6 @@ PROPERTY GENERATED "SomeVar-NOTFOUND") get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") -set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/"Generated_with_full_source_path3.txt" +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" PROPERTY GENERATED "Junk-value") get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test8.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test8.cmake index 5cc6e99..e6b3cce 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test8.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test8.cmake
@@ -27,6 +27,7 @@ ) +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" CONTENT "int func();\nint main(){ return func(); }"
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test8b.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test8b.cmake new file mode 100644 index 0000000..704300a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test8b.cmake
@@ -0,0 +1,51 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0118-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + +add_subdirectory(subdir-Common-Test8b) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test9.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test9.cmake index b7d36ef..e9abf36 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-Common-Test9.cmake +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test9.cmake
@@ -27,6 +27,7 @@ ) +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" CONTENT "int func();\nint main(){ return func(); }"
diff --git a/Tests/RunCMake/CMP0118/CMP0118-Common-Test9b.cmake b/Tests/RunCMake/CMP0118/CMP0118-Common-Test9b.cmake new file mode 100644 index 0000000..a452fdb --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-Common-Test9b.cmake
@@ -0,0 +1,51 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0118-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + +add_subdirectory(subdir-Common-Test9b) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16-result.txt similarity index 100% rename from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt rename to Tests/RunCMake/CMP0118/CMP0118-NEW-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16-stderr.txt new file mode 100644 index 0000000..a513b83 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16.cmake new file mode 100644 index 0000000..74b05c4 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test16.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test16.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b-stderr.txt new file mode 100644 index 0000000..419c571 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0118-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b.cmake new file mode 100644 index 0000000..30cd6cd --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test1b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2b.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2b.cmake new file mode 100644 index 0000000..27f2a2d --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test2b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2c-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2c.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2c.cmake new file mode 100644 index 0000000..ade66fd --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2c.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test2c.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2d-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2d.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2d.cmake new file mode 100644 index 0000000..456760d --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test2d.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test2d.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt index 929dafd..4175587 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt
@@ -12,10 +12,10 @@ Generated_with_full_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` @@ -48,14 +48,14 @@ Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -63,7 +63,7 @@ CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -71,15 +71,7 @@ CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) -Call Stack \(most recent call first\): - CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt index 6a096b8..e9a1ee2 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt
@@ -12,10 +12,10 @@ Generated_with_full_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` @@ -48,14 +48,14 @@ Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[12]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -63,7 +63,7 @@ CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[12]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -71,15 +71,7 @@ CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) -Call Stack \(most recent call first\): - CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[12]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt index 0e27c1d..686ba23 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt
@@ -76,12 +76,12 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` -Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` -Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +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\): Unsetting the 'GENERATED' property is not allowed under CMP0118! @@ -118,7 +118,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -126,7 +126,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -134,7 +134,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -142,7 +142,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -150,7 +150,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -158,15 +158,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) -Call Stack \(most recent call first\): - CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test8b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test8b-stderr.txt new file mode 100644 index 0000000..f9ce9be --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test8b-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test8b.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test8b.cmake new file mode 100644 index 0000000..a39f89f --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test8b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test8b.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-stderr.txt new file mode 100644 index 0000000..093f1b8 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b.cmake b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b.cmake new file mode 100644 index 0000000..36e30f4 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 NEW) +include(CMP0118-Common-Test9b.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt new file mode 100644 index 0000000..2ca118d --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16-stderr.txt
@@ -0,0 +1,38 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test16\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16.cmake new file mode 100644 index 0000000..c0e40fc --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test16.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test16.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt new file mode 100644 index 0000000..ad3ea43 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-stderr.txt
@@ -0,0 +1,21 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test1b\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 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\) ++ +prop: `NOTFOUND` +CMake Error at CMP0118-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-OLD-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b.cmake new file mode 100644 index 0000000..ad1a211 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test1b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt new file mode 100644 index 0000000..41114ba --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test2b\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 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\) ++ +prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b.cmake new file mode 100644 index 0000000..5bfe045 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test2b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt new file mode 100644 index 0000000..ba36763 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test2c\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 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\) ++ +prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c.cmake new file mode 100644 index 0000000..e225de4 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2c.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test2c.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt new file mode 100644 index 0000000..0a9a8b7 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test2d\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 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\) ++ +prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d.cmake new file mode 100644 index 0000000..ad1c640 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2d.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test2d.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt index cce5b19..28fe2f0 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
@@ -23,10 +23,10 @@ Generated_with_full_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` @@ -59,14 +59,14 @@ Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -74,7 +74,7 @@ CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -82,15 +82,7 @@ CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) -Call Stack \(most recent call first\): - CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt index 47eee2e..819b8c0 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
@@ -23,10 +23,10 @@ Generated_with_full_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` @@ -59,14 +59,14 @@ Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -74,7 +74,7 @@ CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -82,15 +82,7 @@ CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) -Call Stack \(most recent call first\): - CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt index 4a67fa7..e20ffee 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
@@ -45,12 +45,12 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` -Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` -Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `1` Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` @@ -66,7 +66,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -74,7 +74,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -82,7 +82,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -90,7 +90,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -98,7 +98,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -106,15 +106,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) -Call Stack \(most recent call first\): - CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt new file mode 100644 index 0000000..285051c --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-stderr.txt
@@ -0,0 +1,56 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test8b\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 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\) ++ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0118-Common-Test8b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-OLD-Test8b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b.cmake new file mode 100644 index 0000000..6676e7c --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test8b.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt new file mode 100644 index 0000000..9b2fdad --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-stderr.txt
@@ -0,0 +1,72 @@ +^CMake Deprecation Warning at CMP0118-OLD-Test9b\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0118 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\) ++ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b.cmake b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b.cmake new file mode 100644 index 0000000..3446063 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9b.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0118 OLD) +include(CMP0118-Common-Test9b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt index 93d8b83..9395cd7 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt
@@ -1,6 +1,6 @@ ^((CMake Warning \(dev\) at subdir-Common-Test11/CMakeLists\.txt:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -9,8 +9,8 @@ This warning is for project developers\. Use -Wno-dev to suppress it\. +)+ (CMake Warning \(dev\) at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_subdirectory\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt index 6d59cb4..957cc69 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test13-stderr.txt
@@ -8,8 +8,8 @@ TARGET 'custom[4-6]' was not created in this directory\. + ((CMake Warning \(dev\) at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -18,8 +18,8 @@ This warning is for project developers\. Use -Wno-dev to suppress it\. +)+ (CMake Warning \(dev\) at CMP0118-Common-Test13\.cmake:[0-9]+ \(add_subdirectory\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt index 9e6a4a5..eab051f 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt
@@ -1,6 +1,6 @@ ^((CMake Warning \(dev\) at subdir-Common-Test15/CMakeLists\.txt:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -9,8 +9,8 @@ This warning is for project developers\. Use -Wno-dev to suppress it\. +)+ (CMake Warning \(dev\) at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_subdirectory\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-WARN-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16-stderr.txt new file mode 100644 index 0000000..a513b83 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16.cmake new file mode 100644 index 0000000..f50df7f --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test16.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test16.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b-stderr.txt new file mode 100644 index 0000000..86b9327 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0118-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b.cmake new file mode 100644 index 0000000..c055570 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1b.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test1b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2b.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2b.cmake new file mode 100644 index 0000000..82a192c --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2b.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test2b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2c-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2c.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2c.cmake new file mode 100644 index 0000000..ec47683 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2c.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test2c.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2d-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2d.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2d.cmake new file mode 100644 index 0000000..a5e3fd7 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test2d.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test2d.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt index 58ba793..491eab0 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt
@@ -12,10 +12,10 @@ Generated_with_full_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` @@ -48,14 +48,14 @@ Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -63,7 +63,7 @@ CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -71,15 +71,7 @@ CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) -Call Stack \(most recent call first\): - CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt index cdb7cb4..e170849 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt
@@ -12,10 +12,10 @@ Generated_with_full_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` @@ -48,14 +48,14 @@ Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[12]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -63,7 +63,7 @@ CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[12]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -71,15 +71,7 @@ CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) -Call Stack \(most recent call first\): - CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[12]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt index 4cd401c..6340f91 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt
@@ -1,6 +1,6 @@ ^CMake Warning \(dev\) at CMP0118-Common-Test4\.cmake:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -18,8 +18,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -37,8 +37,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -56,8 +56,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -75,8 +75,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -94,8 +94,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -113,8 +113,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -132,8 +132,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -151,8 +151,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt index 6acd3df..a213ae5 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt
@@ -1,6 +1,6 @@ ^CMake Warning \(dev\) at CMP0118-Common-Test4b\.cmake:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -18,8 +18,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -37,8 +37,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -56,8 +56,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -75,8 +75,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -94,8 +94,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -113,8 +113,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -132,8 +132,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -151,8 +151,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt index 2805cbf..2f7a390 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt
@@ -5,8 +5,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -24,8 +24,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -57,8 +57,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -76,8 +76,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -102,15 +102,15 @@ Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` -Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` -Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` -Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` -Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -128,8 +128,8 @@ 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\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -157,7 +157,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -165,7 +165,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -173,7 +173,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -181,7 +181,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -189,7 +189,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) @@ -197,15 +197,7 @@ CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): Cannot find source file: -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) -Call Stack \(most recent call first\): - CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\): - Cannot find source file: - -[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) Call Stack \(most recent call first\): CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt index cde1164..1feef65 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt
@@ -1,6 +1,6 @@ ^((CMake Warning \(dev\) at subdir-Common-Test7/CMakeLists\.txt:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -9,8 +9,8 @@ This warning is for project developers\. Use -Wno-dev to suppress it\. +)+ (CMake Warning \(dev\) at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_subdirectory\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b-stderr.txt new file mode 100644 index 0000000..c063326 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0118-Common-Test8b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN-Test8b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b.cmake new file mode 100644 index 0000000..b44331a --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8b.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test8b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt index 547e820..cbce4ab 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt
@@ -1,6 +1,6 @@ ^((CMake Warning \(dev\) at subdir-Common-Test9/CMakeLists\.txt:[0-9]+ \(set_property\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\. @@ -9,8 +9,8 @@ This warning is for project developers\. Use -Wno-dev to suppress it\. +)+ (CMake Warning \(dev\) at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_subdirectory\): - Policy CMP0118 is not set: The GENERATED source file property is now - visible in all directories\. Run "cmake --help-policy CMP0118" for policy + 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\.
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-stderr.txt new file mode 100644 index 0000000..878812d --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0118-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b.cmake b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b.cmake new file mode 100644 index 0000000..9726b30 --- /dev/null +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9b.cmake
@@ -0,0 +1 @@ +include(CMP0118-Common-Test9b.cmake)
diff --git a/Tests/RunCMake/CMP0118/CMakeLists.txt b/Tests/RunCMake/CMP0118/CMakeLists.txt index 957fe03..8bf7896 100644 --- a/Tests/RunCMake/CMP0118/CMakeLists.txt +++ b/Tests/RunCMake/CMP0118/CMakeLists.txt
@@ -1,4 +1,4 @@ cmake_minimum_required(VERSION 3.19) -cmake_policy(SET CMP0115 NEW) +cmake_policy(SET CMP0115 NEW) # CMP0115 is correct here (for reducing error-output)! project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/CMP0118/RunCMakeTest.cmake b/Tests/RunCMake/CMP0118/RunCMakeTest.cmake index f7f135e..207f3d7 100644 --- a/Tests/RunCMake/CMP0118/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0118/RunCMakeTest.cmake
@@ -5,7 +5,11 @@ run_cmake(GenInSubdir-NEW) run_cmake(CMP0118-OLD-Test1) +run_cmake(CMP0118-OLD-Test1b) run_cmake(CMP0118-OLD-Test2) +run_cmake(CMP0118-OLD-Test2b) +run_cmake(CMP0118-OLD-Test2c) +run_cmake(CMP0118-OLD-Test2d) run_cmake(CMP0118-OLD-Test3) run_cmake(CMP0118-OLD-Test3b) run_cmake(CMP0118-OLD-Test4) @@ -14,16 +18,23 @@ run_cmake(CMP0118-OLD-Test6) run_cmake(CMP0118-OLD-Test7) run_cmake(CMP0118-OLD-Test8) +run_cmake(CMP0118-OLD-Test8b) run_cmake(CMP0118-OLD-Test9) +run_cmake(CMP0118-OLD-Test9b) run_cmake(CMP0118-OLD-Test10) run_cmake(CMP0118-OLD-Test11) run_cmake(CMP0118-OLD-Test12) run_cmake(CMP0118-OLD-Test13) run_cmake(CMP0118-OLD-Test14) run_cmake(CMP0118-OLD-Test15) +run_cmake(CMP0118-OLD-Test16) run_cmake(CMP0118-WARN-Test1) +run_cmake(CMP0118-WARN-Test1b) run_cmake(CMP0118-WARN-Test2) +run_cmake(CMP0118-WARN-Test2b) +run_cmake(CMP0118-WARN-Test2c) +run_cmake(CMP0118-WARN-Test2d) run_cmake(CMP0118-WARN-Test3) run_cmake(CMP0118-WARN-Test3b) run_cmake(CMP0118-WARN-Test4) @@ -32,16 +43,23 @@ run_cmake(CMP0118-WARN-Test6) run_cmake(CMP0118-WARN-Test7) run_cmake(CMP0118-WARN-Test8) +run_cmake(CMP0118-WARN-Test8b) run_cmake(CMP0118-WARN-Test9) +run_cmake(CMP0118-WARN-Test9b) run_cmake(CMP0118-WARN-Test10) run_cmake(CMP0118-WARN-Test11) run_cmake(CMP0118-WARN-Test12) run_cmake(CMP0118-WARN-Test13) run_cmake(CMP0118-WARN-Test14) run_cmake(CMP0118-WARN-Test15) +run_cmake(CMP0118-WARN-Test16) run_cmake(CMP0118-NEW-Test1) +run_cmake(CMP0118-NEW-Test1b) run_cmake(CMP0118-NEW-Test2) +run_cmake(CMP0118-NEW-Test2b) +run_cmake(CMP0118-NEW-Test2c) +run_cmake(CMP0118-NEW-Test2d) run_cmake(CMP0118-NEW-Test3) run_cmake(CMP0118-NEW-Test3b) run_cmake(CMP0118-NEW-Test4) @@ -50,10 +68,13 @@ run_cmake(CMP0118-NEW-Test6) run_cmake(CMP0118-NEW-Test7) run_cmake(CMP0118-NEW-Test8) +run_cmake(CMP0118-NEW-Test8b) run_cmake(CMP0118-NEW-Test9) +run_cmake(CMP0118-NEW-Test9b) run_cmake(CMP0118-NEW-Test10) run_cmake(CMP0118-NEW-Test11) run_cmake(CMP0118-NEW-Test12) run_cmake(CMP0118-NEW-Test13) run_cmake(CMP0118-NEW-Test14) run_cmake(CMP0118-NEW-Test15) +run_cmake(CMP0118-NEW-Test16)
diff --git a/Tests/RunCMake/CMP0118/subdir-Common-Test13/CMakeLists.txt b/Tests/RunCMake/CMP0118/subdir-Common-Test13/CMakeLists.txt index f2929aa..7ad02df 100644 --- a/Tests/RunCMake/CMP0118/subdir-Common-Test13/CMakeLists.txt +++ b/Tests/RunCMake/CMP0118/subdir-Common-Test13/CMakeLists.txt
@@ -1,13 +1,16 @@ +# Note: Target "custom4" was not created in this directory, so setting this build-event will fail! add_custom_command(TARGET custom4 PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" ) +# Note: Target "custom5" was not created in this directory, so setting this build-event will fail! add_custom_command(TARGET custom5 PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" ) +# Note: Target "custom6" was not created in this directory, so setting this build-event will fail! add_custom_command(TARGET custom6 PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt"
diff --git a/Tests/RunCMake/CMP0118/subdir-Common-Test16/CMakeLists.txt b/Tests/RunCMake/CMP0118/subdir-Common-Test16/CMakeLists.txt new file mode 100644 index 0000000..877c829 --- /dev/null +++ b/Tests/RunCMake/CMP0118/subdir-Common-Test16/CMakeLists.txt
@@ -0,0 +1,10 @@ +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" +) +add_custom_command(TARGET custom4 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +)
diff --git a/Tests/RunCMake/CMP0118/subdir-Common-Test8/CMakeLists.txt b/Tests/RunCMake/CMP0118/subdir-Common-Test8/CMakeLists.txt index 55debd1..eefe18c 100644 --- a/Tests/RunCMake/CMP0118/subdir-Common-Test8/CMakeLists.txt +++ b/Tests/RunCMake/CMP0118/subdir-Common-Test8/CMakeLists.txt
@@ -1,3 +1,4 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" CONTENT "int func();\nint main(){ return func(); }"
diff --git a/Tests/RunCMake/CMP0118/subdir-Common-Test8b/CMakeLists.txt b/Tests/RunCMake/CMP0118/subdir-Common-Test8b/CMakeLists.txt new file mode 100644 index 0000000..1e4cccd --- /dev/null +++ b/Tests/RunCMake/CMP0118/subdir-Common-Test8b/CMakeLists.txt
@@ -0,0 +1,13 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + CONTENT "int func();\nint main(){ return func(); }" +)
diff --git a/Tests/RunCMake/CMP0118/subdir-Common-Test9/CMakeLists.txt b/Tests/RunCMake/CMP0118/subdir-Common-Test9/CMakeLists.txt index cdb8884..99f6d73 100644 --- a/Tests/RunCMake/CMP0118/subdir-Common-Test9/CMakeLists.txt +++ b/Tests/RunCMake/CMP0118/subdir-Common-Test9/CMakeLists.txt
@@ -1,3 +1,4 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" CONTENT "int func();\nint main(){ return func(); }"
diff --git a/Tests/RunCMake/CMP0118/subdir-Common-Test9b/CMakeLists.txt b/Tests/RunCMake/CMP0118/subdir-Common-Test9b/CMakeLists.txt new file mode 100644 index 0000000..f94f36f --- /dev/null +++ b/Tests/RunCMake/CMP0118/subdir-Common-Test9b/CMakeLists.txt
@@ -0,0 +1,13 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" + CONTENT "int func();\nint main(){ return func(); }" +)
diff --git a/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt index 6bf944e..1632d63 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.cmake:[0-9]+ \(message\): +CMake Warning \(dev\) 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 \(dev\) 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/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt index b4afc27..ea3b5c1 100644 --- a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt
@@ -1,3 +1,3 @@ ld: warning: ignoring duplicate libraries: '.*liblib1.a', '.*liblib2.a' ld: fatal warning\(s\) induced error \(-fatal_warnings\) -clang: error: linker command failed with exit code 1 \(use -v to see invocation\) +(cc|clang): error: linker command failed with exit code 1 \(use -v to see invocation\)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1-stderr.txt new file mode 100644 index 0000000..a8b3503 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `0` +CMake Error at CMP0163-Common-Test1\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test1\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test10-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test10-stderr.txt new file mode 100644 index 0000000..680a830 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test10-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test10.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test10.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt new file mode 100644 index 0000000..7cdc233 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11-stderr.txt
@@ -0,0 +1,55 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test11.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12-stderr.txt new file mode 100644 index 0000000..6133a35 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12-stderr.txt
@@ -0,0 +1,51 @@ +^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test12.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt new file mode 100644 index 0000000..e65140a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13-stderr.txt
@@ -0,0 +1,64 @@ +^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +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\): + Unsetting the 'GENERATED' property is not allowed under CMP0163! + +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test13.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test14-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test14-stderr.txt new file mode 100644 index 0000000..680a830 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test14-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test14.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test14.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt new file mode 100644 index 0000000..979481f --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15-stderr.txt
@@ -0,0 +1,55 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test15.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16-stderr.txt new file mode 100644 index 0000000..c18109c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test16.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b-stderr.txt new file mode 100644 index 0000000..fae8bc7 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0163-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b.cmake new file mode 100644 index 0000000..e1b9d22 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test1b.cmake
@@ -0,0 +1,6 @@ +cmake_policy(SET CMP0118 NEW) +# Extract from the current file the name of the test-case and value for CMP0163 and call the test-case. +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM testcase) +string(REGEX MATCH "NEW|OLD|WARN" value_for_CMP0163 "${testcase}") +string(REGEX REPLACE "${value_for_CMP0163}" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2a.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2a.cmake new file mode 100644 index 0000000..b1e932c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2a.cmake
@@ -0,0 +1,3 @@ +cmake_policy(SET CMP0118 NEW) +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM CMP0163_TESTCASE) +include(${CMAKE_CURRENT_LIST_DIR}/../Test-with-CMP0163-NEW.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2b.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2c-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2c.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2c.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2d-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2d.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test2d.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3-stderr.txt new file mode 100644 index 0000000..2f90661 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-NEW-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-NEW-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-NEW-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b-stderr.txt new file mode 100644 index 0000000..db40354 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-NEW-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-NEW-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-NEW-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test3b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt new file mode 100644 index 0000000..9d75891 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4-stderr.txt
@@ -0,0 +1,167 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt new file mode 100644 index 0000000..c3f35a2 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b-stderr.txt
@@ -0,0 +1,135 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test4b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt new file mode 100644 index 0000000..779d708 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5-stderr.txt
@@ -0,0 +1,126 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0163 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0163 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0163 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*(Generated_with_relative_path[2-3]\.txt|.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test5.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test6-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test6-stderr.txt new file mode 100644 index 0000000..ee30636 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test6-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test6.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test6.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt new file mode 100644 index 0000000..9ab3d50 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test7.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8-stderr.txt new file mode 100644 index 0000000..ee30636 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8b-stderr.txt new file mode 100644 index 0000000..f9ce9be --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8b-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8b.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test8b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt new file mode 100644 index 0000000..02d9bd3 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-stderr.txt new file mode 100644 index 0000000..672d3b4 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b.cmake new file mode 100644 index 0000000..2faf482 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-NEW-Test9b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1-stderr.txt new file mode 100644 index 0000000..be3e906 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `0` +CMake Error at CMP0163-Common-Test1\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test1\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test10-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test10-stderr.txt new file mode 100644 index 0000000..2bd1cba --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test10-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test10.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test10.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt new file mode 100644 index 0000000..02ecd50 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11-stderr.txt
@@ -0,0 +1,55 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test11.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12-stderr.txt new file mode 100644 index 0000000..e6c429c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12-stderr.txt
@@ -0,0 +1,51 @@ +^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test12.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt new file mode 100644 index 0000000..fe1ccc3 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13-stderr.txt
@@ -0,0 +1,64 @@ +^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +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\): + Unsetting the 'GENERATED' property is not allowed under CMP0118! + +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test13.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test14-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test14-stderr.txt new file mode 100644 index 0000000..2bd1cba --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test14-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test14.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test14.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt new file mode 100644 index 0000000..b37461a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15-stderr.txt
@@ -0,0 +1,55 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test15.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16-stderr.txt new file mode 100644 index 0000000..a513b83 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test16.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b-stderr.txt new file mode 100644 index 0000000..9232a57 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0163-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b.cmake new file mode 100644 index 0000000..e1b9d22 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test1b.cmake
@@ -0,0 +1,6 @@ +cmake_policy(SET CMP0118 NEW) +# Extract from the current file the name of the test-case and value for CMP0163 and call the test-case. +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM testcase) +string(REGEX MATCH "NEW|OLD|WARN" value_for_CMP0163 "${testcase}") +string(REGEX REPLACE "${value_for_CMP0163}" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2a.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2a.cmake new file mode 100644 index 0000000..60f065c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2a.cmake
@@ -0,0 +1,3 @@ +cmake_policy(SET CMP0118 NEW) +cmake_path(GET CMAKE_CURRENT_LIST_FILE FILENAME test_file) +include(${CMAKE_CURRENT_LIST_DIR}/../${test_file})
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2b.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2c-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2c.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2c.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2d-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2d.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test2d.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3-stderr.txt new file mode 100644 index 0000000..085f091 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-OLD-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-OLD-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-OLD-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b-stderr.txt new file mode 100644 index 0000000..1c5a178 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-OLD-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-OLD-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-OLD-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test3b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt new file mode 100644 index 0000000..22ae491 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4-stderr.txt
@@ -0,0 +1,167 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt new file mode 100644 index 0000000..23dc427 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b-stderr.txt
@@ -0,0 +1,135 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test4b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt new file mode 100644 index 0000000..ee3ee76 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5-stderr.txt
@@ -0,0 +1,126 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0118 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0118 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0118 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/(Generated_with_relative_path[2-3]\.txt|Generated_with_full_path[2-3]\.txt)) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test5.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test6-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test6-stderr.txt new file mode 100644 index 0000000..0c84cfe --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test6-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test6.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test6.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt new file mode 100644 index 0000000..ced4fcf --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test7.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8-stderr.txt new file mode 100644 index 0000000..f723875 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8b-stderr.txt new file mode 100644 index 0000000..f9ce9be --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8b-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8b.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test8b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt new file mode 100644 index 0000000..e855458 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-stderr.txt new file mode 100644 index 0000000..769a3f5 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b.cmake new file mode 100644 index 0000000..9e979e8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-OLD-Test9b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1-stderr.txt new file mode 100644 index 0000000..4e5f4ac --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `0` +CMake Error at CMP0163-Common-Test1\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test1\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test10-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test10-stderr.txt new file mode 100644 index 0000000..2bd1cba --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test10-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test10.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test10.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt new file mode 100644 index 0000000..bd98012 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11-stderr.txt
@@ -0,0 +1,55 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test11.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12-stderr.txt new file mode 100644 index 0000000..e6c429c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12-stderr.txt
@@ -0,0 +1,51 @@ +^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test12.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt new file mode 100644 index 0000000..6422e36 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13-stderr.txt
@@ -0,0 +1,64 @@ +^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +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\): + Unsetting the 'GENERATED' property is not allowed under CMP0118! + +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test13.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test14-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test14-stderr.txt new file mode 100644 index 0000000..2bd1cba --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test14-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test14.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test14.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt new file mode 100644 index 0000000..c34acb2 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15-stderr.txt
@@ -0,0 +1,55 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test15.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16-stderr.txt new file mode 100644 index 0000000..a513b83 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test16.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b-stderr.txt new file mode 100644 index 0000000..9037f5d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0163-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b.cmake new file mode 100644 index 0000000..e1b9d22 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test1b.cmake
@@ -0,0 +1,6 @@ +cmake_policy(SET CMP0118 NEW) +# Extract from the current file the name of the test-case and value for CMP0163 and call the test-case. +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM testcase) +string(REGEX MATCH "NEW|OLD|WARN" value_for_CMP0163 "${testcase}") +string(REGEX REPLACE "${value_for_CMP0163}" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2a.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2a.cmake new file mode 100644 index 0000000..60f065c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2a.cmake
@@ -0,0 +1,3 @@ +cmake_policy(SET CMP0118 NEW) +cmake_path(GET CMAKE_CURRENT_LIST_FILE FILENAME test_file) +include(${CMAKE_CURRENT_LIST_DIR}/../${test_file})
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2b.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2c-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2c.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2c.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2d-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2d.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test2d.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3-stderr.txt new file mode 100644 index 0000000..212398e --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-WARN-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-WARN-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-WARN-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b-stderr.txt new file mode 100644 index 0000000..d36919b --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-WARN-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-WARN-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-NEW/CMP0163-WARN-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test3b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt new file mode 100644 index 0000000..0b71575 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4-stderr.txt
@@ -0,0 +1,167 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt new file mode 100644 index 0000000..0640b89 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b-stderr.txt
@@ -0,0 +1,135 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW/CMP0163-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test4b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt new file mode 100644 index 0000000..f882081 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5-stderr.txt
@@ -0,0 +1,126 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0118 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0118 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0118 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/(Generated_with_relative_path[2-3]\.txt|Generated_with_full_path[2-3]\.txt)) +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test5.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test6-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test6-stderr.txt new file mode 100644 index 0000000..0c84cfe --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test6-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test6.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test6.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt new file mode 100644 index 0000000..4b7b12e --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test7.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8-stderr.txt new file mode 100644 index 0000000..f723875 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8b-stderr.txt new file mode 100644 index 0000000..f9ce9be --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8b-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8b.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test8b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt new file mode 100644 index 0000000..f8795b9 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-stderr.txt new file mode 100644 index 0000000..e8c28d3 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-NEW/CMP0163-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b.cmake new file mode 100644 index 0000000..f3ec67a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-NEW/CMP0163-WARN-Test9b.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0118 NEW) +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1-stderr.txt new file mode 100644 index 0000000..ca58b9a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `0` +CMake Error at CMP0163-Common-Test1\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test1\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test10-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test10-stderr.txt new file mode 100644 index 0000000..680a830 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test10-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test10.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test10.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt new file mode 100644 index 0000000..182a43c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11-stderr.txt
@@ -0,0 +1,55 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test11.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12-stderr.txt new file mode 100644 index 0000000..6133a35 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12-stderr.txt
@@ -0,0 +1,51 @@ +^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test12.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt new file mode 100644 index 0000000..59d81ec --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13-stderr.txt
@@ -0,0 +1,64 @@ +^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +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\): + Unsetting the 'GENERATED' property is not allowed under CMP0163! + +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test13.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test14-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test14-stderr.txt new file mode 100644 index 0000000..680a830 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test14-stderr.txt
@@ -0,0 +1,42 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test14.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test14.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt new file mode 100644 index 0000000..4c3be67 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15-stderr.txt
@@ -0,0 +1,55 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test15.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16-stderr.txt new file mode 100644 index 0000000..c18109c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test16.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b-stderr.txt new file mode 100644 index 0000000..b2d0e65 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0163-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b.cmake new file mode 100644 index 0000000..8dc6b3d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test1b.cmake
@@ -0,0 +1,6 @@ +# Leave CMP0118 unset! +# Extract from the current file the name of the test-case and value for CMP0163 and call the test-case. +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM testcase) +string(REGEX MATCH "NEW|OLD|WARN" value_for_CMP0163 "${testcase}") +string(REGEX REPLACE "${value_for_CMP0163}" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2a.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2a.cmake new file mode 100644 index 0000000..b1e932c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2a.cmake
@@ -0,0 +1,3 @@ +cmake_policy(SET CMP0118 NEW) +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM CMP0163_TESTCASE) +include(${CMAKE_CURRENT_LIST_DIR}/../Test-with-CMP0163-NEW.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2b.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2c-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2c.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2c.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2d-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2d.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test2d.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3-stderr.txt new file mode 100644 index 0000000..3298fca --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-NEW-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-NEW-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-NEW-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b-stderr.txt new file mode 100644 index 0000000..0f351ea --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-NEW-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-NEW-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-NEW-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test3b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt new file mode 100644 index 0000000..ffc7224 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4-stderr.txt
@@ -0,0 +1,126 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt new file mode 100644 index 0000000..4634ba9 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b-stderr.txt
@@ -0,0 +1,135 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-NEW-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test4b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt new file mode 100644 index 0000000..4b0961d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5-stderr.txt
@@ -0,0 +1,126 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0163 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0163 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + Policy CMP0163 is set to NEW and the following non-boolean value given for + property 'GENERATED' is therefore not allowed: + + Junk-value + + Replace it with a boolean value! + +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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +(CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*(Generated_with_relative_path[2-3]\.txt|.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++)+ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test5.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test6-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test6-stderr.txt new file mode 100644 index 0000000..ee30636 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test6-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test6.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test6.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt new file mode 100644 index 0000000..1e3bc32 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test7.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8-stderr.txt new file mode 100644 index 0000000..ee30636 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `1` +Generated_source4\.txt: # 1b # GENERATED = `1` +Generated_source4\.txt: # 2a # GENERATED = `1` +Generated_source4\.txt: # 2b # GENERATED = `1` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8b-stderr.txt new file mode 100644 index 0000000..f9ce9be --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8b-stderr.txt
@@ -0,0 +1,36 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8b.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test8b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt new file mode 100644 index 0000000..cb63bf3 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-stderr.txt
@@ -0,0 +1,74 @@ +^(CMake Warning \(dev\) 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-stderr.txt new file mode 100644 index 0000000..2eb755a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-NEW-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b.cmake new file mode 100644 index 0000000..62e4dc6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-NEW-Test9b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 NEW) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "NEW" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1-stderr.txt new file mode 100644 index 0000000..98b8b6a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `0` +CMake Error at CMP0163-Common-Test1\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test1\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10-stderr.txt new file mode 100644 index 0000000..f51de9b --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10-stderr.txt
@@ -0,0 +1,51 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test10\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test10\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test10.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt new file mode 100644 index 0000000..63be2a7 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-stderr.txt
@@ -0,0 +1,90 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test11\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test11\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test11\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test11\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test11\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test11\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test11.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12-stderr.txt new file mode 100644 index 0000000..e6c429c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12-stderr.txt
@@ -0,0 +1,51 @@ +^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test12.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt new file mode 100644 index 0000000..3068eb3 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13-stderr.txt
@@ -0,0 +1,74 @@ +^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +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\): + 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test13.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14-stderr.txt new file mode 100644 index 0000000..c6ffe0c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14-stderr.txt
@@ -0,0 +1,51 @@ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test14\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test14\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test14.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt new file mode 100644 index 0000000..7ce1d4e --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-stderr.txt
@@ -0,0 +1,90 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test15\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test15\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test15\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test15\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test15\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test15\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test15.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16-stderr.txt new file mode 100644 index 0000000..a513b83 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test16.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b-stderr.txt new file mode 100644 index 0000000..798641d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0163-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b.cmake new file mode 100644 index 0000000..8dc6b3d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test1b.cmake
@@ -0,0 +1,6 @@ +# Leave CMP0118 unset! +# Extract from the current file the name of the test-case and value for CMP0163 and call the test-case. +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM testcase) +string(REGEX MATCH "NEW|OLD|WARN" value_for_CMP0163 "${testcase}") +string(REGEX REPLACE "${value_for_CMP0163}" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2a.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2a.cmake new file mode 100644 index 0000000..0729939 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2a.cmake
@@ -0,0 +1,2 @@ +cmake_path(GET CMAKE_CURRENT_LIST_FILE FILENAME test_file) +include(${CMAKE_CURRENT_LIST_DIR}/../${test_file})
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2b.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2c-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2c.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2c.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2d-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2d.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test2d.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3-stderr.txt new file mode 100644 index 0000000..9f7d5de --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-OLD-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-OLD-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-OLD-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b-stderr.txt new file mode 100644 index 0000000..81687a3 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-OLD-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-OLD-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-OLD-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test3b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt new file mode 100644 index 0000000..9098ddd --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4-stderr.txt
@@ -0,0 +1,212 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt new file mode 100644 index 0000000..6893cc9 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b-stderr.txt
@@ -0,0 +1,212 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-OLD-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test4b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt new file mode 100644 index 0000000..7fa3ffe --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5-stderr.txt
@@ -0,0 +1,205 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. + + Attempt to set property 'GENERATED' with the following non-boolean value + \(which will be interpreted as "0"\): + + Junk-value + + That exact value will not be retrievable\. A value of "0" will be returned + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. + + Attempt to set property 'GENERATED' with the following non-boolean value + \(which will be interpreted as "0"\): + + Junk-value + + That exact value will not be retrievable\. A value of "0" will be returned + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. + + Attempt to set property 'GENERATED' with the following non-boolean value + \(which will be interpreted as "0"\): + + Junk-value + + That exact value will not be retrievable\. A value of "0" will be returned + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-OLD-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test5.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6-stderr.txt new file mode 100644 index 0000000..da10641 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test6\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test6\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test6.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt new file mode 100644 index 0000000..c4ae929 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-stderr.txt
@@ -0,0 +1,100 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test7.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8-stderr.txt new file mode 100644 index 0000000..5c4014f --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test8\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test8\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b-stderr.txt new file mode 100644 index 0000000..81458aa --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test8b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test8b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test8b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt new file mode 100644 index 0000000..7dbd729 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-stderr.txt
@@ -0,0 +1,84 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-stderr.txt new file mode 100644 index 0000000..a03cce0 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-OLD-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b.cmake new file mode 100644 index 0000000..5069712 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-OLD-Test9b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +cmake_policy(SET CMP0163 OLD) + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "OLD" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1-stderr.txt new file mode 100644 index 0000000..327067d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `0` +CMake Error at CMP0163-Common-Test1\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test1\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10-stderr.txt new file mode 100644 index 0000000..0a5d4b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10-stderr.txt
@@ -0,0 +1,51 @@ +^Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test10\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test10\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test10.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt new file mode 100644 index 0000000..bffefb6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-stderr.txt
@@ -0,0 +1,90 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test11\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test11\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test11\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test11\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test11\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test11\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test11.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12-stderr.txt new file mode 100644 index 0000000..e6c429c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12-stderr.txt
@@ -0,0 +1,51 @@ +^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test12.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt new file mode 100644 index 0000000..d9ea88e --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13-stderr.txt
@@ -0,0 +1,74 @@ +^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom[4-6]' was not created in this directory\. ++ +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\): + 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\. ++ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test13.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14-stderr.txt new file mode 100644 index 0000000..451c413 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14-stderr.txt
@@ -0,0 +1,51 @@ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test14\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test14\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test14.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt new file mode 100644 index 0000000..ac80737 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-stderr.txt
@@ -0,0 +1,90 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source0\.txt: # 1a # GENERATED = `1` +Generated_source0\.txt: # 1b # GENERATED = `1` +Generated_source0\.txt: # 2a # GENERATED = `1` +Generated_source0\.txt: # 2b # GENERATED = `1` +Generated_source0\.txt: # 3a # GENERATED = `0` +Generated_source0\.txt: # 3b # GENERATED = `0` +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test15\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test15\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test15\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test15\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test15\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test15\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test15.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16-stderr.txt new file mode 100644 index 0000000..a513b83 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16-stderr.txt
@@ -0,0 +1,27 @@ +^CMake Error at subdir-Common-Test16/CMakeLists\.txt:[0-9]+ \(add_custom_command\): + TARGET 'custom4' was not created in this directory\. ++ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test16.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b-stderr.txt new file mode 100644 index 0000000..f7e6846 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b-stderr.txt
@@ -0,0 +1,10 @@ +^prop: `NOTFOUND` +CMake Error at CMP0163-Common-Test1b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b-build/GeneratedMain\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test1b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b.cmake new file mode 100644 index 0000000..8dc6b3d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test1b.cmake
@@ -0,0 +1,6 @@ +# Leave CMP0118 unset! +# Extract from the current file the name of the test-case and value for CMP0163 and call the test-case. +cmake_path(GET CMAKE_CURRENT_LIST_FILE STEM testcase) +string(REGEX MATCH "NEW|OLD|WARN" value_for_CMP0163 "${testcase}") +string(REGEX REPLACE "${value_for_CMP0163}" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2a.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2a.cmake new file mode 100644 index 0000000..0729939 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2a.cmake
@@ -0,0 +1,2 @@ +cmake_path(GET CMAKE_CURRENT_LIST_FILE FILENAME test_file) +include(${CMAKE_CURRENT_LIST_DIR}/../${test_file})
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2b-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2b-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2b.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2c-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2c-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2c-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2c.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2c.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2d-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2d-stderr.txt new file mode 100644 index 0000000..403ce5a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2d-stderr.txt
@@ -0,0 +1 @@ +^prop: `1`$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2d.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test2d.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3-stderr.txt new file mode 100644 index 0000000..1a41192 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-WARN-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-WARN-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-WARN-Test3-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test3\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b-stderr.txt new file mode 100644 index 0000000..005f005 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b-stderr.txt
@@ -0,0 +1,79 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_path3\.txt: # 3b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path2\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path2\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `1` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path1\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path2\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `1` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `1` +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-WARN-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-WARN-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test3b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-2]\.txt|CMP0118-WARN/CMP0163-WARN-Test3b-build/Generated_with_full_path3\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test3b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test3b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt new file mode 100644 index 0000000..fbdf1da --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4-stderr.txt
@@ -0,0 +1,212 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt new file mode 100644 index 0000000..caf555c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b-stderr.txt
@@ -0,0 +1,212 @@ +^CMake Warning \(dev\) 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\. ++ +Generated_with_full_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path1\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test4b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN/CMP0163-WARN-Test4b-build/Generated_with_relative_path1\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test4b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test4b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt new file mode 100644 index 0000000..83556e6 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5-stderr.txt
@@ -0,0 +1,205 @@ +^Generated_with_full_path1\.txt: # 1a # GENERATED = `1` +Generated_with_full_path1\.txt: # 1b # GENERATED = `1` +Generated_with_full_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. + + Attempt to set property 'GENERATED' with the following non-boolean value + \(which will be interpreted as "0"\): + + Junk-value + + That exact value will not be retrievable\. A value of "0" will be returned + 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\. ++ +Generated_with_full_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_path3\.txt: # 3b # GENERATED = `0` +Generated_with_relative_path1\.txt: # 1a # GENERATED = `1` +Generated_with_relative_path1\.txt: # 1b # GENERATED = `1` +Generated_with_relative_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_relative_path2\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path2\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. + + Attempt to set property 'GENERATED' with the following non-boolean value + \(which will be interpreted as "0"\): + + Junk-value + + That exact value will not be retrievable\. A value of "0" will be returned + 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\. ++ +Generated_with_relative_path3\.txt: # 1a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 1b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 2b # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3a # GENERATED = `0` +Generated_with_relative_path3\.txt: # 3b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path1\.txt: # 2a # GENERATED = `1` +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\): + 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\. ++ +Generated_with_full_source_path2\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path2\.txt: # 2a # GENERATED = `0` +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\): + 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\. + + Attempt to set property 'GENERATED' with the following non-boolean value + \(which will be interpreted as "0"\): + + Junk-value + + That exact value will not be retrievable\. A value of "0" will be returned + 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\. ++ +Generated_with_full_source_path3\.txt: # 1a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 1b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0` +Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test5\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/(Generated_with_full_source_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN/CMP0163-WARN-Test5-build/Generated_with_full_path[2-3]\.txt) +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test5\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test5.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6-stderr.txt new file mode 100644 index 0000000..08711ab --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test6\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test6\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test6.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt new file mode 100644 index 0000000..9d8af4f --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-stderr.txt
@@ -0,0 +1,100 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `1` +Generated_source1\.txt: # 1b # GENERATED = `1` +Generated_source1\.txt: # 2a # GENERATED = `1` +Generated_source1\.txt: # 2b # GENERATED = `1` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test7\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7-build/Generated_source[2-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test7\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test7.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8-stderr.txt new file mode 100644 index 0000000..4928234 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `1` +Generated_source2\.txt: # 1b # GENERATED = `1` +Generated_source2\.txt: # 2a # GENERATED = `1` +Generated_source2\.txt: # 2b # GENERATED = `1` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `1` +Generated_source3\.txt: # 1b # GENERATED = `1` +Generated_source3\.txt: # 2a # GENERATED = `1` +Generated_source3\.txt: # 2b # GENERATED = `1` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `1` +Generated_source5\.txt: # 1b # GENERATED = `1` +Generated_source5\.txt: # 2a # GENERATED = `1` +Generated_source5\.txt: # 2b # GENERATED = `1` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `1` +Generated_source6\.txt: # 1b # GENERATED = `1` +Generated_source6\.txt: # 2a # GENERATED = `1` +Generated_source6\.txt: # 2b # GENERATED = `1` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test8\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8-build/Generated_source4\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test8\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b-stderr.txt new file mode 100644 index 0000000..200a180 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b-stderr.txt
@@ -0,0 +1,45 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test8b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test8b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test8b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt new file mode 100644 index 0000000..4d35ef0 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-stderr.txt
@@ -0,0 +1,84 @@ +^((CMake Warning \(dev\) 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\. ++)+ +(CMake Warning \(dev\) 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\. ++)+)+ +Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test9\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-result.txt
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-stderr.txt b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-stderr.txt new file mode 100644 index 0000000..ed65a7c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-stderr.txt
@@ -0,0 +1,61 @@ +^Generated_source1\.txt: # 1a # GENERATED = `0` +Generated_source1\.txt: # 1b # GENERATED = `0` +Generated_source1\.txt: # 2a # GENERATED = `0` +Generated_source1\.txt: # 2b # GENERATED = `0` +Generated_source1\.txt: # 3a # GENERATED = `0` +Generated_source1\.txt: # 3b # GENERATED = `0` +Generated_source2\.txt: # 1a # GENERATED = `0` +Generated_source2\.txt: # 1b # GENERATED = `0` +Generated_source2\.txt: # 2a # GENERATED = `0` +Generated_source2\.txt: # 2b # GENERATED = `0` +Generated_source2\.txt: # 3a # GENERATED = `0` +Generated_source2\.txt: # 3b # GENERATED = `0` +Generated_source3\.txt: # 1a # GENERATED = `0` +Generated_source3\.txt: # 1b # GENERATED = `0` +Generated_source3\.txt: # 2a # GENERATED = `0` +Generated_source3\.txt: # 2b # GENERATED = `0` +Generated_source3\.txt: # 3a # GENERATED = `0` +Generated_source3\.txt: # 3b # GENERATED = `0` +Generated_source4\.txt: # 1a # GENERATED = `0` +Generated_source4\.txt: # 1b # GENERATED = `0` +Generated_source4\.txt: # 2a # GENERATED = `0` +Generated_source4\.txt: # 2b # GENERATED = `0` +Generated_source4\.txt: # 3a # GENERATED = `0` +Generated_source4\.txt: # 3b # GENERATED = `0` +Generated_source5\.txt: # 1a # GENERATED = `0` +Generated_source5\.txt: # 1b # GENERATED = `0` +Generated_source5\.txt: # 2a # GENERATED = `0` +Generated_source5\.txt: # 2b # GENERATED = `0` +Generated_source5\.txt: # 3a # GENERATED = `0` +Generated_source5\.txt: # 3b # GENERATED = `0` +Generated_source6\.txt: # 1a # GENERATED = `0` +Generated_source6\.txt: # 1b # GENERATED = `0` +Generated_source6\.txt: # 2a # GENERATED = `0` +Generated_source6\.txt: # 2b # GENERATED = `0` +Generated_source6\.txt: # 3a # GENERATED = `0` +Generated_source6\.txt: # 3b # GENERATED = `0` +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at CMP0163-Common-Test9b\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + +[ \t]*.*Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b-build/Generated_source[4-6]\.txt +Call Stack \(most recent call first\): + CMP0118-WARN/CMP0163-WARN-Test9b\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Generate step failed\. Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b.cmake new file mode 100644 index 0000000..c0327b8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0118-WARN/CMP0163-WARN-Test9b.cmake
@@ -0,0 +1,7 @@ +# Leave CMP0118 unset! +# Leave CMP0163 unset! + +# Call the associated test. +cmake_path(GET RunCMake_TEST STEM testcase) +string(REGEX REPLACE "WARN" "Common" testcase "${testcase}") +include(${CMAKE_CURRENT_LIST_DIR}/../${testcase}.cmake)
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Helper.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Helper.cmake new file mode 100644 index 0000000..b237b7f --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Helper.cmake
@@ -0,0 +1,14 @@ +macro(get_and_print_GENERATED_property filename) + get_property(prop SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${filename}" PROPERTY GENERATED) + message(NOTICE "${filename}: # 1a # GENERATED = `${prop}`") + get_source_file_property(prop "${CMAKE_CURRENT_BINARY_DIR}/${filename}" GENERATED) + message(NOTICE "${filename}: # 1b # GENERATED = `${prop}`") + get_property(prop SOURCE "${filename}" PROPERTY GENERATED) + message(NOTICE "${filename}: # 2a # GENERATED = `${prop}`") + get_source_file_property(prop "${filename}" GENERATED) + message(NOTICE "${filename}: # 2b # GENERATED = `${prop}`") + get_property(prop SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${filename}" PROPERTY GENERATED) + message(NOTICE "${filename}: # 3a # GENERATED = `${prop}`") + get_source_file_property(prop "${CMAKE_CURRENT_SOURCE_DIR}/${filename}" GENERATED) + message(NOTICE "${filename}: # 3b # GENERATED = `${prop}`") +endmacro()
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test1.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test1.cmake new file mode 100644 index 0000000..2b66515 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test1.cmake
@@ -0,0 +1,9 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +get_property(prop SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test10.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test10.cmake new file mode 100644 index 0000000..97984d8 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test10.cmake
@@ -0,0 +1,65 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +# The sources of custom0 will not be modified by set_property! +add_custom_target(custom0) +target_sources(custom0 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +# The sources of custom[1-6] will (tried to) be modified by set_property! +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) + +add_subdirectory(subdir-Common-Test10) + +get_and_print_GENERATED_property("Generated_source0.txt") +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test11.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test11.cmake new file mode 100644 index 0000000..59d92f4 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test11.cmake
@@ -0,0 +1,65 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +# The sources of custom0 will not be modified by set_property! +add_custom_target(custom0) +target_sources(custom0 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +# The sources of custom[1-6] will (tried to) be modified by set_property! +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) + +add_subdirectory(subdir-Common-Test11) + +get_and_print_GENERATED_property("Generated_source0.txt") +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test12.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test12.cmake new file mode 100644 index 0000000..9d514f0 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test12.cmake
@@ -0,0 +1,65 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +# The sources of custom0 will not be modified by set_property! +add_custom_target(custom0) +target_sources(custom0 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +# The sources of custom[1-6] will (tried to) be modified by set_property! +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +add_custom_command(TARGET custom0 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +add_custom_command(TARGET custom1 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_command(TARGET custom2 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_command(TARGET custom3 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) + +add_subdirectory(subdir-Common-Test12) + +get_and_print_GENERATED_property("Generated_source0.txt") +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test13.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test13.cmake new file mode 100644 index 0000000..b79f369 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test13.cmake
@@ -0,0 +1,65 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +# The sources of custom0 will not be modified by set_property! +add_custom_target(custom0) +target_sources(custom0 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +# The sources of custom[1-6] will (tried to) be modified by set_property! +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +add_custom_command(TARGET custom0 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +add_custom_command(TARGET custom1 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_command(TARGET custom2 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_command(TARGET custom3 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) + +add_subdirectory(subdir-Common-Test13) + +get_and_print_GENERATED_property("Generated_source0.txt") +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test14.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test14.cmake new file mode 100644 index 0000000..203b114 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test14.cmake
@@ -0,0 +1,65 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +# The sources of custom0 will not be modified by set_property! +add_custom_target(custom0) +target_sources(custom0 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +# The sources of custom[1-6] will (tried to) be modified by set_property! +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +add_custom_target(custom0_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +add_custom_target(custom1_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) + +add_subdirectory(subdir-Common-Test14) + +get_and_print_GENERATED_property("Generated_source0.txt") +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test15.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test15.cmake new file mode 100644 index 0000000..f66011a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test15.cmake
@@ -0,0 +1,65 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +# The sources of custom0 will not be modified by set_property! +add_custom_target(custom0) +target_sources(custom0 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +# The sources of custom[1-6] will (tried to) be modified by set_property! +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +add_custom_target(custom0_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source0.txt" +) +add_custom_target(custom1_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) + +add_subdirectory(subdir-Common-Test15) + +get_and_print_GENERATED_property("Generated_source0.txt") +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test16.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test16.cmake new file mode 100644 index 0000000..3c63277 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test16.cmake
@@ -0,0 +1,38 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) + + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_command(TARGET custom2 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) + +add_subdirectory(subdir-Common-Test16) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test1b.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test1b.cmake new file mode 100644 index 0000000..622633f --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test1b.cmake
@@ -0,0 +1,9 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +get_source_file_property(prop + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test2.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2.cmake new file mode 100644 index 0000000..1180b6b --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_property(SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED "1") +get_property(prop SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test2b.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2b.cmake new file mode 100644 index 0000000..5317781 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2b.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_source_files_properties( + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTIES GENERATED "1") +get_source_file_property(prop + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test2c.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2c.cmake new file mode 100644 index 0000000..2f2d3a2 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2c.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_property(SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED "1") +get_source_file_property(prop + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test2d.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2d.cmake new file mode 100644 index 0000000..67f9b8c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test2d.cmake
@@ -0,0 +1,12 @@ +add_custom_target(custom) +target_sources(custom PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" +) + +set_source_files_properties( + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTIES GENERATED "1") +get_property(prop SOURCE + "${CMAKE_CURRENT_BINARY_DIR}/GeneratedMain.txt" + PROPERTY GENERATED) +message(NOTICE "prop: `${prop}`")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test3.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test3.cmake new file mode 100644 index 0000000..ce47460 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test3.cmake
@@ -0,0 +1,66 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path2.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path3.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "Generated_with_relative_path1.txt" + "Generated_with_relative_path2.txt" + "Generated_with_relative_path3.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path2.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_path1.txt") + +set_property(SOURCE "Generated_with_full_path2.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_relative_path1.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_relative_path1.txt") + +set_property(SOURCE "Generated_with_relative_path2.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_relative_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_relative_path3.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_relative_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_source_path1.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_source_path1.txt") + +set_property(SOURCE "Generated_with_full_source_path2.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test3b.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test3b.cmake new file mode 100644 index 0000000..8dda231 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test3b.cmake
@@ -0,0 +1,66 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_path1.txt") + +set_property(SOURCE "Generated_with_full_path2.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_relative_path1.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_relative_path1.txt") + +set_property(SOURCE "Generated_with_relative_path2.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_relative_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_relative_path3.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_relative_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_source_path1.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_source_path1.txt") + +set_property(SOURCE "Generated_with_full_source_path2.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" + PROPERTY GENERATED "1") +get_and_print_GENERATED_property("Generated_with_full_source_path3.txt") + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path2.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path3.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "Generated_with_relative_path1.txt" + "Generated_with_relative_path2.txt" + "Generated_with_relative_path3.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path2.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" +)
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test4.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test4.cmake new file mode 100644 index 0000000..8de4f04 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test4.cmake
@@ -0,0 +1,66 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path2.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path3.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "Generated_with_relative_path1.txt" + "Generated_with_relative_path2.txt" + "Generated_with_relative_path3.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path2.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_path1.txt") + +set_property(SOURCE "Generated_with_full_path2.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_relative_path1.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_relative_path1.txt") + +set_property(SOURCE "Generated_with_relative_path2.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_relative_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_relative_path3.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_relative_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_source_path1.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_source_path1.txt") + +set_property(SOURCE "Generated_with_full_source_path2.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test4b.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test4b.cmake new file mode 100644 index 0000000..d218f5d --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test4b.cmake
@@ -0,0 +1,66 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_path1.txt") + +set_property(SOURCE "Generated_with_full_path2.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_path3.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_relative_path1.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_relative_path1.txt") + +set_property(SOURCE "Generated_with_relative_path2.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_relative_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_relative_path3.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_relative_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_source_path1.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_source_path1.txt") + +set_property(SOURCE "Generated_with_full_source_path2.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" + PROPERTY GENERATED "0") +get_and_print_GENERATED_property("Generated_with_full_source_path3.txt") + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path2.txt" + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path3.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "Generated_with_relative_path1.txt" + "Generated_with_relative_path2.txt" + "Generated_with_relative_path3.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path2.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" +)
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test5.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test5.cmake new file mode 100644 index 0000000..235e5e5 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test5.cmake
@@ -0,0 +1,78 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "Generated_with_relative_path1.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "Generated_with_relative_path2.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "Generated_with_relative_path3.txt" +) +add_custom_target(custom7) +target_sources(custom7 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" +) +add_custom_target(custom8) +target_sources(custom8 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path2.txt" +) +add_custom_target(custom9) +target_sources(custom9 PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path1.txt" + PROPERTY GENERATED "tRue") +get_and_print_GENERATED_property("Generated_with_full_path1.txt") + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path2.txt" + PROPERTY GENERATED "SomeVar-NOTFOUND") +get_and_print_GENERATED_property("Generated_with_full_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_with_full_path3.txt" + PROPERTY GENERATED "Junk-value") +get_and_print_GENERATED_property("Generated_with_full_path3.txt") + + +set_property(SOURCE "Generated_with_relative_path1.txt" + PROPERTY GENERATED "tRue") +get_and_print_GENERATED_property("Generated_with_relative_path1.txt") + +set_property(SOURCE "Generated_with_relative_path2.txt" + PROPERTY GENERATED "SomeVar-NOTFOUND") +get_and_print_GENERATED_property("Generated_with_relative_path2.txt") + +set_property(SOURCE "Generated_with_relative_path3.txt" + PROPERTY GENERATED "Junk-value") +get_and_print_GENERATED_property("Generated_with_relative_path3.txt") + + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path1.txt" + PROPERTY GENERATED "tRue") +get_and_print_GENERATED_property("Generated_with_full_source_path1.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path2.txt" + PROPERTY GENERATED "SomeVar-NOTFOUND") +get_and_print_GENERATED_property("Generated_with_full_source_path2.txt") + +set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Generated_with_full_source_path3.txt" + PROPERTY GENERATED "Junk-value") +get_and_print_GENERATED_property("Generated_with_full_source_path3.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test6.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test6.cmake new file mode 100644 index 0000000..b89af65 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test6.cmake
@@ -0,0 +1,44 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + PROPERTY GENERATED "1") + +add_subdirectory(subdir-Common-Test6) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test7.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test7.cmake new file mode 100644 index 0000000..d1586f5 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test7.cmake
@@ -0,0 +1,44 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + PROPERTY GENERATED "1") + +add_subdirectory(subdir-Common-Test7) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test8.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test8.cmake new file mode 100644 index 0000000..c2bfd03 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test8.cmake
@@ -0,0 +1,51 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + +add_subdirectory(subdir-Common-Test8) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test8b.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test8b.cmake new file mode 100644 index 0000000..cba4817 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test8b.cmake
@@ -0,0 +1,51 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + +add_subdirectory(subdir-Common-Test8b) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test9.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test9.cmake new file mode 100644 index 0000000..dd2e128 --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test9.cmake
@@ -0,0 +1,51 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + +add_subdirectory(subdir-Common-Test9) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMP0163-Common-Test9b.cmake b/Tests/RunCMake/CMP0163/CMP0163-Common-Test9b.cmake new file mode 100644 index 0000000..c7d494c --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMP0163-Common-Test9b.cmake
@@ -0,0 +1,51 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0163-Common-Helper.cmake) + + +add_custom_target(custom1) +target_sources(custom1 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" +) +add_custom_target(custom2) +target_sources(custom2 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" +) +add_custom_target(custom3) +target_sources(custom3 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" +) +add_custom_target(custom4) +target_sources(custom4 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" +) +add_custom_target(custom5) +target_sources(custom5 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" +) +add_custom_target(custom6) +target_sources(custom6 PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" +) + + +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source1.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source2.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source3.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + +add_subdirectory(subdir-Common-Test9b) + +get_and_print_GENERATED_property("Generated_source1.txt") +get_and_print_GENERATED_property("Generated_source2.txt") +get_and_print_GENERATED_property("Generated_source3.txt") +get_and_print_GENERATED_property("Generated_source4.txt") +get_and_print_GENERATED_property("Generated_source5.txt") +get_and_print_GENERATED_property("Generated_source6.txt")
diff --git a/Tests/RunCMake/CMP0163/CMakeLists.txt b/Tests/RunCMake/CMP0163/CMakeLists.txt new file mode 100644 index 0000000..4ae248a --- /dev/null +++ b/Tests/RunCMake/CMP0163/CMakeLists.txt
@@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.19) # Must be older than 3.20 in order to also not set CMP0118! +cmake_policy(SET CMP0115 NEW) # CMP0115 is correct here (for reducing error-output)! +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/CMP0163/RunCMakeTest.cmake b/Tests/RunCMake/CMP0163/RunCMakeTest.cmake new file mode 100644 index 0000000..edfe130 --- /dev/null +++ b/Tests/RunCMake/CMP0163/RunCMakeTest.cmake
@@ -0,0 +1,147 @@ +include(RunCMake) + +run_cmake(CMP0118-WARN/CMP0163-OLD-Test1) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test1b) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test2) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test2b) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test2c) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test2d) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test3) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test3b) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test4) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test4b) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test5) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test6) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test7) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test8) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test8b) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test9) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test9b) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test10) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test11) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test12) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test13) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test14) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test15) +run_cmake(CMP0118-WARN/CMP0163-OLD-Test16) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test1) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test1b) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test2) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test2b) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test2c) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test2d) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test3) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test3b) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test4) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test4b) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test5) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test6) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test7) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test8) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test8b) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test9) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test9b) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test10) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test11) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test12) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test13) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test14) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test15) +run_cmake(CMP0118-WARN/CMP0163-WARN-Test16) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test1) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test1b) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test2) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test2b) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test2c) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test2d) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test3) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test3b) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test4) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test4b) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test5) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test6) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test7) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test8) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test8b) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test9) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test9b) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test10) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test11) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test12) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test13) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test14) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test15) +run_cmake(CMP0118-WARN/CMP0163-NEW-Test16) + +run_cmake(CMP0118-NEW/CMP0163-OLD-Test1) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test1b) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test2) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test2b) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test2c) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test2d) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test3) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test3b) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test4) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test4b) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test5) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test6) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test7) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test8) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test8b) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test9) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test9b) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test10) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test11) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test12) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test13) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test14) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test15) +run_cmake(CMP0118-NEW/CMP0163-OLD-Test16) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test1) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test1b) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test2) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test2b) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test2c) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test2d) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test3) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test3b) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test4) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test4b) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test5) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test6) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test7) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test8) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test8b) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test9) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test9b) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test10) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test11) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test12) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test13) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test14) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test15) +run_cmake(CMP0118-NEW/CMP0163-WARN-Test16) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test1) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test1b) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test2) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test2b) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test2c) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test2d) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test3) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test3b) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test4) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test4b) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test5) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test6) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test7) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test8) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test8b) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test9) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test9b) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test10) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test11) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test12) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test13) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test14) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test15) +run_cmake(CMP0118-NEW/CMP0163-NEW-Test16)
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test10/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test10/CMakeLists.txt new file mode 100644 index 0000000..6bc99cb --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test10/CMakeLists.txt
@@ -0,0 +1,33 @@ +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test11/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test11/CMakeLists.txt new file mode 100644 index 0000000..fbd7e6f --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test11/CMakeLists.txt
@@ -0,0 +1,33 @@ +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" +) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test12/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test12/CMakeLists.txt new file mode 100644 index 0000000..916725c --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test12/CMakeLists.txt
@@ -0,0 +1,33 @@ +add_custom_command(TARGET custom4 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +) +add_custom_command(TARGET custom5 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" +) +add_custom_command(TARGET custom6 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test13/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test13/CMakeLists.txt new file mode 100644 index 0000000..7ad02df --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test13/CMakeLists.txt
@@ -0,0 +1,36 @@ +# Note: Target "custom4" was not created in this directory, so setting this build-event will fail! +add_custom_command(TARGET custom4 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +) +# Note: Target "custom5" was not created in this directory, so setting this build-event will fail! +add_custom_command(TARGET custom5 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" +) +# Note: Target "custom6" was not created in this directory, so setting this build-event will fail! +add_custom_command(TARGET custom6 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test14/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test14/CMakeLists.txt new file mode 100644 index 0000000..7397db9 --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test14/CMakeLists.txt
@@ -0,0 +1,33 @@ +add_custom_target(custom4_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +) +add_custom_target(custom5_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" +) +add_custom_target(custom6_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test15/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test15/CMakeLists.txt new file mode 100644 index 0000000..314e427 --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test15/CMakeLists.txt
@@ -0,0 +1,33 @@ +add_custom_target(custom4_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +) +add_custom_target(custom5_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" +) +add_custom_target(custom6_source_generator ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test16/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test16/CMakeLists.txt new file mode 100644 index 0000000..877c829 --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test16/CMakeLists.txt
@@ -0,0 +1,10 @@ +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" +) +add_custom_command(TARGET custom4 PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Source.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + BYPRODUCT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" +)
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test6/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test6/CMakeLists.txt new file mode 100644 index 0000000..fa307d1 --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test6/CMakeLists.txt
@@ -0,0 +1,16 @@ +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test7/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test7/CMakeLists.txt new file mode 100644 index 0000000..6362f78 --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test7/CMakeLists.txt
@@ -0,0 +1,16 @@ +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test8/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test8/CMakeLists.txt new file mode 100644 index 0000000..eefe18c --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test8/CMakeLists.txt
@@ -0,0 +1,31 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "1") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "1")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test8b/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test8b/CMakeLists.txt new file mode 100644 index 0000000..1e4cccd --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test8b/CMakeLists.txt
@@ -0,0 +1,13 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + CONTENT "int func();\nint main(){ return func(); }" +)
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test9/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test9/CMakeLists.txt new file mode 100644 index 0000000..99f6d73 --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test9/CMakeLists.txt
@@ -0,0 +1,31 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" + CONTENT "int func();\nint main(){ return func(); }" +) + + +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source1.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source2.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source3.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source4.txt" + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source5.txt" + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." + PROPERTY GENERATED "0") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/../Generated_source6.txt" + TARGET_DIRECTORY custom1 + PROPERTY GENERATED "0")
diff --git a/Tests/RunCMake/CMP0163/subdir-Common-Test9b/CMakeLists.txt b/Tests/RunCMake/CMP0163/subdir-Common-Test9b/CMakeLists.txt new file mode 100644 index 0000000..f94f36f --- /dev/null +++ b/Tests/RunCMake/CMP0163/subdir-Common-Test9b/CMakeLists.txt
@@ -0,0 +1,13 @@ +# Note: Currently, `file(GENERATE)` does not set the `GENERATED` property! +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source4.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source5.txt" + CONTENT "int func();\nint main(){ return func(); }" +) +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Generated_source6.txt" + CONTENT "int func();\nint main(){ return func(); }" +)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/CMP0165/CMP0165-NEW-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/CMP0165/CMP0165-NEW-result.txt
diff --git a/Tests/RunCMake/CMP0165/CMP0165-NEW-stderr.txt b/Tests/RunCMake/CMP0165/CMP0165-NEW-stderr.txt new file mode 100644 index 0000000..662d8ad --- /dev/null +++ b/Tests/RunCMake/CMP0165/CMP0165-NEW-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at CMP0165-NEW\.cmake:[0-9]+ \(enable_language\): + project\(\) must be called prior to this enable_language\(\) call\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0165/CMP0165-NEW.cmake b/Tests/RunCMake/CMP0165/CMP0165-NEW.cmake new file mode 100644 index 0000000..8bcb3fd --- /dev/null +++ b/Tests/RunCMake/CMP0165/CMP0165-NEW.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0165 NEW) +enable_language(C)
diff --git a/Tests/RunCMake/CMP0165/CMP0165-OLD.cmake b/Tests/RunCMake/CMP0165/CMP0165-OLD.cmake new file mode 100644 index 0000000..c80ba76 --- /dev/null +++ b/Tests/RunCMake/CMP0165/CMP0165-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0165 OLD) +enable_language(C)
diff --git a/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt b/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt new file mode 100644 index 0000000..32a79df --- /dev/null +++ b/Tests/RunCMake/CMP0165/CMP0165-WARN-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(dev\) 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\.
diff --git a/Tests/RunCMake/CMP0165/CMP0165-WARN.cmake b/Tests/RunCMake/CMP0165/CMP0165-WARN.cmake new file mode 100644 index 0000000..75149dd --- /dev/null +++ b/Tests/RunCMake/CMP0165/CMP0165-WARN.cmake
@@ -0,0 +1,2 @@ +# Don't touch the policy, it should warn by default +enable_language(C)
diff --git a/Tests/RunCMake/CMP0165/CMakeLists.txt b/Tests/RunCMake/CMP0165/CMakeLists.txt new file mode 100644 index 0000000..2fa8c4e --- /dev/null +++ b/Tests/RunCMake/CMP0165/CMakeLists.txt
@@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.29) + +# This is different to the usual RunCMake test pattern. We are specifically +# testing the scenario where enable_language() is called before project(). +include(${RunCMake_TEST}.cmake) + +project(${RunCMake_TEST} NONE)
diff --git a/Tests/RunCMake/CMP0165/RunCMakeTest.cmake b/Tests/RunCMake/CMP0165/RunCMakeTest.cmake new file mode 100644 index 0000000..f5f167c --- /dev/null +++ b/Tests/RunCMake/CMP0165/RunCMakeTest.cmake
@@ -0,0 +1,5 @@ +include(RunCMake) + +run_cmake(CMP0165-WARN) +run_cmake(CMP0165-OLD) +run_cmake(CMP0165-NEW)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/CMP0169/CMP0169-NEW-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/CMP0169/CMP0169-NEW-result.txt
diff --git a/Tests/RunCMake/CMP0169/CMP0169-NEW-stderr.txt b/Tests/RunCMake/CMP0169/CMP0169-NEW-stderr.txt new file mode 100644 index 0000000..95418fb --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-NEW-stderr.txt
@@ -0,0 +1,10 @@ +CMake Error 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 + ability to call it with declared details will be removed completely in a + future version\. +Call Stack \(most recent call first\): + CMP0169\.cmake:[0-9]+ \(FetchContent_Populate\) + CMP0169-NEW\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0169/CMP0169-NEW-stdout.txt b/Tests/RunCMake/CMP0169/CMP0169-NEW-stdout.txt new file mode 100644 index 0000000..b74fa26 --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-NEW-stdout.txt
@@ -0,0 +1,2 @@ +Direct download +-- Configuring incomplete, errors occurred!
diff --git a/Tests/RunCMake/CMP0169/CMP0169-NEW.cmake b/Tests/RunCMake/CMP0169/CMP0169-NEW.cmake new file mode 100644 index 0000000..32328b6 --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-NEW.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0169 NEW) +include(CMP0169.cmake)
diff --git a/Tests/RunCMake/CMP0169/CMP0169-OLD-stdout.txt b/Tests/RunCMake/CMP0169/CMP0169-OLD-stdout.txt new file mode 100644 index 0000000..ea56073 --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-OLD-stdout.txt
@@ -0,0 +1,4 @@ +Direct download +Declared download +-- Configuring done \([0-9]+\.[0-9]s\) +-- Generating done \([0-9]+\.[0-9]s\)
diff --git a/Tests/RunCMake/CMP0169/CMP0169-OLD.cmake b/Tests/RunCMake/CMP0169/CMP0169-OLD.cmake new file mode 100644 index 0000000..217f137 --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0169 OLD) +include(CMP0169.cmake)
diff --git a/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt b/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt new file mode 100644 index 0000000..7a06200 --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-WARN-stderr.txt
@@ -0,0 +1,11 @@ +CMake Warning \(dev\) 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 + ability to call it with declared details will be removed completely in a + future version\. +Call Stack \(most recent call first\): + 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\.
diff --git a/Tests/RunCMake/CMP0169/CMP0169-WARN-stdout.txt b/Tests/RunCMake/CMP0169/CMP0169-WARN-stdout.txt new file mode 100644 index 0000000..ea56073 --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-WARN-stdout.txt
@@ -0,0 +1,4 @@ +Direct download +Declared download +-- Configuring done \([0-9]+\.[0-9]s\) +-- Generating done \([0-9]+\.[0-9]s\)
diff --git a/Tests/RunCMake/CMP0169/CMP0169-WARN.cmake b/Tests/RunCMake/CMP0169/CMP0169-WARN.cmake new file mode 100644 index 0000000..63a9ecc --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169-WARN.cmake
@@ -0,0 +1 @@ +include(CMP0169.cmake)
diff --git a/Tests/RunCMake/CMP0169/CMP0169.cmake b/Tests/RunCMake/CMP0169/CMP0169.cmake new file mode 100644 index 0000000..e7a900d --- /dev/null +++ b/Tests/RunCMake/CMP0169/CMP0169.cmake
@@ -0,0 +1,14 @@ +cmake_policy(SET CMP0168 NEW) # Faster, don't need to test with sub-build + +include(FetchContent) + +FetchContent_Populate(t1 + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Direct download" + UPDATE_COMMAND "" +) + +FetchContent_Declare(t2 + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Declared download" + UPDATE_COMMAND "" +) +FetchContent_Populate(t2)
diff --git a/Tests/RunCMake/CMP0169/CMakeLists.txt b/Tests/RunCMake/CMP0169/CMakeLists.txt new file mode 100644 index 0000000..94e43ba --- /dev/null +++ b/Tests/RunCMake/CMP0169/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/CMP0169/RunCMakeTest.cmake b/Tests/RunCMake/CMP0169/RunCMakeTest.cmake new file mode 100644 index 0000000..92d7fcd --- /dev/null +++ b/Tests/RunCMake/CMP0169/RunCMakeTest.cmake
@@ -0,0 +1,5 @@ +include(RunCMake) + +run_cmake(CMP0169-WARN) +run_cmake(CMP0169-OLD) +run_cmake(CMP0169-NEW)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/CMP0170/CMP0170-NEW-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/CMP0170/CMP0170-NEW-result.txt
diff --git a/Tests/RunCMake/CMP0170/CMP0170-NEW-stderr.txt b/Tests/RunCMake/CMP0170/CMP0170-NEW-stderr.txt new file mode 100644 index 0000000..f786bd6 --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-NEW-stderr.txt
@@ -0,0 +1,14 @@ +CMake Error at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): + FETCHCONTENT_FULLY_DISCONNECTED is set to true, which requires the source + directory for dependency t1 to already be populated\. This generally means + it must not be set to true the first time CMake is run in a build + directory\. The following source directory should already be populated, but + it doesn't exist:[ +]+ .*/Tests/RunCMake/CMP0170/IdoNotExist[ +]+ Policy CMP0170 controls enforcement of this requirement\. +Call Stack \(most recent call first\): + .*/Modules/FetchContent\.cmake:[0-9]+:EVAL:1 \(__FetchContent_Populate\) + .*/Modules/FetchContent\.cmake:[0-9]+ \(cmake_language\) + CMP0170\.cmake:[0-9]+ \(FetchContent_Populate\) + CMP0170-NEW\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0170/CMP0170-NEW-stdout.txt b/Tests/RunCMake/CMP0170/CMP0170-NEW-stdout.txt new file mode 100644 index 0000000..2dc34a6 --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-NEW-stdout.txt
@@ -0,0 +1,2 @@ +-- Starting population +-- Configuring incomplete, errors occurred!
diff --git a/Tests/RunCMake/CMP0170/CMP0170-NEW.cmake b/Tests/RunCMake/CMP0170/CMP0170-NEW.cmake new file mode 100644 index 0000000..60b386a --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-NEW.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0170 NEW) +include(CMP0170.cmake)
diff --git a/Tests/RunCMake/CMP0170/CMP0170-OLD-stdout.txt b/Tests/RunCMake/CMP0170/CMP0170-OLD-stdout.txt new file mode 100644 index 0000000..82c260b --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-OLD-stdout.txt
@@ -0,0 +1,3 @@ +-- Starting population +-- Configuring done \([0-9]+\.[0-9]s\) +-- Generating done \([0-9]+\.[0-9]s\)
diff --git a/Tests/RunCMake/CMP0170/CMP0170-OLD.cmake b/Tests/RunCMake/CMP0170/CMP0170-OLD.cmake new file mode 100644 index 0000000..812cca0 --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0170 OLD) +include(CMP0170.cmake)
diff --git a/Tests/RunCMake/CMP0170/CMP0170-WARN-stderr.txt b/Tests/RunCMake/CMP0170/CMP0170-WARN-stderr.txt new file mode 100644 index 0000000..1253abd --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-WARN-stderr.txt
@@ -0,0 +1,28 @@ +CMake Warning at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): + FETCHCONTENT_FULLY_DISCONNECTED is set to true, which requires the source + directory for dependency t1 to already be populated\. This generally means + it must not be set to true the first time CMake is run in a build + directory\. The following source directory should already be populated, but + it doesn't exist:[ +]+ .*/Tests/RunCMake/CMP0170/IdoNotExist[ +]+ Policy CMP0170 controls enforcement of this requirement\. +Call Stack \(most recent call first\): + .*/Modules/FetchContent\.cmake:[0-9]+:EVAL:1 \(__FetchContent_Populate\) + .*/Modules/FetchContent\.cmake:[0-9]+ \(cmake_language\) + CMP0170\.cmake:[0-9]+ \(FetchContent_Populate\) + CMP0170-WARN\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) +[ +]+CMake Warning at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): + FETCHCONTENT_FULLY_DISCONNECTED is set to true, which requires the source + directory for dependency t2 to already be populated\. This generally means + it must not be set to true the first time CMake is run in a build + directory\. The following source directory should already be populated, but + it doesn't exist:[ +]+ .*/Tests/RunCMake/CMP0170/IdoNotExist[ +]+ Policy CMP0170 controls enforcement of this requirement\. +Call Stack \(most recent call first\): + .*/Modules/FetchContent\.cmake:[0-9]+ \(__FetchContent_Populate\) + CMP0170\.cmake:[0-9]+ \(FetchContent_MakeAvailable\) + CMP0170-WARN\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0170/CMP0170-WARN-stdout.txt b/Tests/RunCMake/CMP0170/CMP0170-WARN-stdout.txt new file mode 100644 index 0000000..82c260b --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-WARN-stdout.txt
@@ -0,0 +1,3 @@ +-- Starting population +-- Configuring done \([0-9]+\.[0-9]s\) +-- Generating done \([0-9]+\.[0-9]s\)
diff --git a/Tests/RunCMake/CMP0170/CMP0170-WARN.cmake b/Tests/RunCMake/CMP0170/CMP0170-WARN.cmake new file mode 100644 index 0000000..1754c24 --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170-WARN.cmake
@@ -0,0 +1 @@ +include(CMP0170.cmake)
diff --git a/Tests/RunCMake/CMP0170/CMP0170.cmake b/Tests/RunCMake/CMP0170/CMP0170.cmake new file mode 100644 index 0000000..4e63e25 --- /dev/null +++ b/Tests/RunCMake/CMP0170/CMP0170.cmake
@@ -0,0 +1,18 @@ +cmake_policy(SET CMP0168 NEW) # Faster, don't need to test with sub-build +cmake_policy(SET CMP0169 OLD) # So we can test FetchContent_Populate() directly + +set(FETCHCONTENT_FULLY_DISCONNECTED TRUE) + +include(FetchContent) + +message(STATUS "Starting population") + +FetchContent_Declare(t1 + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/IdoNotExist" +) +FetchContent_Populate(t1) + +FetchContent_Declare(t2 + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/IdoNotExist" +) +FetchContent_MakeAvailable(t2)
diff --git a/Tests/RunCMake/CMP0170/CMakeLists.txt b/Tests/RunCMake/CMP0170/CMakeLists.txt new file mode 100644 index 0000000..94e43ba --- /dev/null +++ b/Tests/RunCMake/CMP0170/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/CMP0170/RunCMakeTest.cmake b/Tests/RunCMake/CMP0170/RunCMakeTest.cmake new file mode 100644 index 0000000..0ba64e3 --- /dev/null +++ b/Tests/RunCMake/CMP0170/RunCMakeTest.cmake
@@ -0,0 +1,5 @@ +include(RunCMake) + +run_cmake(CMP0170-WARN) +run_cmake(CMP0170-OLD) +run_cmake(CMP0170-NEW)
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt new file mode 100644 index 0000000..1953091 --- /dev/null +++ b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt
@@ -0,0 +1,9 @@ +^CMake Deprecation Warning 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\. + + 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\.$
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index a9fe571..e205d9f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt
@@ -173,6 +173,10 @@ add_RunCMake_test(CMP0156 -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) add_RunCMake_test(CMP0160) +add_RunCMake_test(CMP0163) +add_RunCMake_test(CMP0165) +add_RunCMake_test(CMP0169) +add_RunCMake_test(CMP0170) # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode @@ -250,6 +254,7 @@ add_RunCMake_test(NinjaPrivateDeps -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION} -DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig}) + add_RunCMake_test(InstallParallel) endif() add_RunCMake_test(CTest) @@ -438,6 +443,16 @@ ) add_RunCMake_test(Graphviz) add_RunCMake_test(Languages) + +add_RunCMake_test(LanguageStandards + -DCMake_NO_C_STANDARD=${CMake_NO_C_STANDARD} + -DCMake_NO_CXX_STANDARD=${CMake_NO_CXX_STANDARD} + -DCMake_TEST_CUDA=${CMake_TEST_CUDA} + -DCMake_TEST_HIP=${CMake_TEST_HIP} + -DCMake_TEST_OBJC=${CMake_TEST_OBJC} +) +set_property(TEST RunCMake.LanguageStandards APPEND PROPERTY LABELS "CUDA" "HIP") + add_RunCMake_test(LinkItemValidation) add_RunCMake_test(LinkStatic) if(CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|Fujitsu|FujitsuClang)$") @@ -504,10 +519,15 @@ add_RunCMake_test(add_test -DPSEUDO_EMULATOR=$<TARGET_FILE:pseudo_emulator>) add_RunCMake_test(build_command) add_executable(exit_code exit_code.c) +if(NOT CMAKE_C_COMPILER_ID MATCHES "OrangeC|Watcom" + AND NOT CMAKE_C_FLAGS MATCHES "-fsanitize=") + add_executable(exit_crash exit_crash.c) + set(EXIT_CRASH_EXE $<TARGET_FILE:exit_crash>) +endif() set(execute_process_ARGS -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code> + -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin> - -DPython_EXECUTABLE=${Python_EXECUTABLE} -DCYGWIN=${CYGWIN} ) if(NOT CMake_TEST_EXTERNAL_CMAKE) @@ -557,13 +577,18 @@ foreach(var CMake_TEST_NO_NETWORK CMake_TEST_TLS_VERIFY_URL + CMake_TEST_TLS_VERIFY_URL_BAD + CMake_TEST_TLS_VERSION ) if(DEFINED ${var}) list(APPEND file-DOWNLOAD_ARGS -D${var}=${${var}}) endif() endforeach() add_RunCMake_test(file-DOWNLOAD) -add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) +add_RunCMake_test(file-RPATH + -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DCMake_TEST_ELF_LARGE=${CMake_TEST_ELF_LARGE} +) 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}) @@ -724,23 +749,19 @@ if("${CMAKE_GENERATOR}" MATCHES "Visual Studio") add_RunCMake_test(CMAKE_MSVCIDE_RUN_PATH) add_RunCMake_test(include_external_msproject -DVS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}) - if("${CMAKE_GENERATOR}" MATCHES "Visual Studio (9|10)" AND NOT CMAKE_VS_DEVENV_COMMAND) - set(NO_USE_FOLDERS 1) - endif() - add_RunCMake_test(VSSolution -DNO_USE_FOLDERS=${NO_USE_FOLDERS}) -endif() - -if("${CMAKE_GENERATOR}" MATCHES "Visual Studio ([^9]|9[0-9])") + add_RunCMake_test(VSSolution) add_RunCMake_test(VS10Project -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION} ) + add_RunCMake_test(VS10ProjectUseDebugLibraries) if( vs12 AND wince ) add_RunCMake_test( VS10ProjectWinCE "-DRunCMake_GENERATOR_PLATFORM=${wince_sdk}") endif() endif() -if(CMAKE_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])") +if(CMAKE_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])" + AND NOT CMAKE_GENERATOR_TOOLSET MATCHES "^(v80|v90|v100|v110|v120)$") add_RunCMake_test(VsDotnetSdk) add_RunCMake_test(VsNugetPackageRestore) endif() @@ -1034,6 +1055,7 @@ DEB.DEB_PACKAGE_VERSION_BACK_COMPATIBILITY DEB.DEB_DESCRIPTION DEB.PROJECT_META + DEB.COMPONENT_WITH_SPECIAL_CHARS RPM.AUTO_SUFFIXES RPM.CUSTOM_BINARY_SPEC_FILE @@ -1056,6 +1078,7 @@ RPM.SYMLINKS RPM.USER_FILELIST RPM.PROJECT_META + RPM.COMPONENT_WITH_SPECIAL_CHARS 7Z TBZ2 @@ -1084,11 +1107,22 @@ DEB.CUSTOM_NAMES DEB.DEB_PACKAGE_VERSION_BACK_COMPATIBILITY DEB.PROJECT_META DEB.DEPENDENCIES + DEB.EXTRA + DEB.GENERATE_SHLIBS_LDCONFIG + DEB.LONG_FILENAMES + DEB.MD5SUMS + DEB.EMPTY_DIR + DEB.DEFAULT_PERMISSIONS + DEB.VERSION + DEB.TIMESTAMPS RPM.PARTIALLY_RELOCATABLE_WARNING RPM.PER_COMPONENT_FIELDS RPM.USER_FILELIST RPM.DIST RPM.AUTO_SUFFIXES + 7Z + TXZ + TBZ2 TGZ ZIP STGZ @@ -1099,6 +1133,18 @@ endif() add_RunCMake_test_group(CPack "${cpack_tests}") + +if(CMake_TEST_CPACK_WIX3 OR CMake_TEST_CPACK_WIX4) + add_RunCMake_test(CPack_WIX + -DCMake_TEST_CPACK_WIX3=${CMake_TEST_CPACK_WIX3} + -DCMake_TEST_CPACK_WIX4=${CMake_TEST_CPACK_WIX4} + ) +endif() + +if(CMake_TEST_CPACK_NUGET) + add_RunCMake_test(CPack_NuGet) +endif() + # add a test to make sure symbols are exported from a shared library # for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used add_RunCMake_test(AutoExportDll @@ -1129,7 +1175,7 @@ set_property(TEST RunCMake.Android PROPERTY TIMEOUT ${CMake_TEST_ANDROID_TIMEOUT}) endif() -if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])") +if(${CMAKE_GENERATOR} MATCHES "Visual Studio") add_RunCMake_test(CSharpCustomCommand) if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64") add_RunCMake_test(CSharpReferenceImport)
diff --git a/Tests/RunCMake/CMakePackage/NestedConfigFile-stdout.txt b/Tests/RunCMake/CMakePackage/NestedConfigFile-stdout.txt new file mode 100644 index 0000000..cdb3384 --- /dev/null +++ b/Tests/RunCMake/CMakePackage/NestedConfigFile-stdout.txt
@@ -0,0 +1,6 @@ +(-- )?Before find_dependency: PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_b +(-- )?Hello from pkg_a +(-- )?Leaving pkg_a-config\.cmake with PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_a +(-- )?After find_dependency: PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_b +(-- )?Hello from pkg_b +(-- )?Leaving pkg_b-config\.cmake with PACKAGE_PREFIX_DIR = .*/Tests/RunCMake/CMakePackage/NestedConfigFile-build/install_pkg_b
diff --git a/Tests/RunCMake/CMakePackage/NestedConfigFile.cmake b/Tests/RunCMake/CMakePackage/NestedConfigFile.cmake new file mode 100644 index 0000000..2eeaebb --- /dev/null +++ b/Tests/RunCMake/CMakePackage/NestedConfigFile.cmake
@@ -0,0 +1,43 @@ +set(CMAKE_INSTALL_DATADIR share) + +include(CMakePackageConfigHelpers) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pkg_a-config.cmake.in [[ +@PACKAGE_INIT@ +include("@PACKAGE_CMAKE_INSTALL_DATADIR@/pkg_a_included.cmake") +message(STATUS "Leaving pkg_a-config.cmake with PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}") +]]) +configure_package_config_file( + ${CMAKE_CURRENT_BINARY_DIR}/pkg_a-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_a/pkg_a-config.cmake + INSTALL_DESTINATION . + PATH_VARS CMAKE_INSTALL_DATADIR +) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_a/share/pkg_a_included.cmake + [[message(STATUS "Hello from pkg_a")]] +) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pkg_b-config.cmake.in [[ +@PACKAGE_INIT@ +include(CMakeFindDependencyMacro) +message(STATUS "Before find_dependency: PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}") +find_dependency(pkg_a NO_DEFAULT_PATH + PATHS "@CMAKE_CURRENT_BINARY_DIR@/install_pkg_a" +) +message(STATUS "After find_dependency: PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}") +include("@PACKAGE_CMAKE_INSTALL_DATADIR@/pkg_b_included.cmake") +message(STATUS "Leaving pkg_b-config.cmake with PACKAGE_PREFIX_DIR = ${PACKAGE_PREFIX_DIR}") +]]) +configure_package_config_file( + ${CMAKE_CURRENT_BINARY_DIR}/pkg_b-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_b/pkg_b-config.cmake + INSTALL_DESTINATION . + PATH_VARS CMAKE_INSTALL_DATADIR +) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_b/share/pkg_b_included.cmake + [[message(STATUS "Hello from pkg_b")]] +) + +find_package(pkg_b REQUIRED NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_BINARY_DIR}/install_pkg_b +)
diff --git a/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake b/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake index 3b4bef5..c4a05e3 100644 --- a/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake
@@ -4,6 +4,8 @@ set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Release) endif() +run_cmake_with_options(NestedConfigFile ${maybe_CMAKE_BUILD_TYPE}) + function(apple_export platform system_name archs sysroot) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-export-${platform}-build) string(REPLACE ";" "\\;" archs "${archs}")
diff --git a/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt b/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt new file mode 100644 index 0000000..d3f1afc --- /dev/null +++ b/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros-stdout.txt
@@ -0,0 +1,5 @@ +^Not searching for unused variables given on the command line\. +Available configure presets: + + "Include" + "IncludeCommon"$
diff --git a/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros.json.in b/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros.json.in new file mode 100644 index 0000000..c5693b5 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/IncludeExpansionOtherMacros.json.in
@@ -0,0 +1,10 @@ +{ + "version": 9, + "include": ["${sourceDir}/IncludeCommon.json"], + "configurePresets": [ + { + "name": "Include", + "inherits": ["IncludeCommon"] + } + ] +}
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index 88027fb..2ec0a43 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -262,15 +262,13 @@ run_cmake_presets(VisualStudioWin32) run_cmake_presets(VisualStudioWin64) run_cmake_presets(VisualStudioWin32Override -A x64) - if(NOT RunCMake_GENERATOR STREQUAL "Visual Studio 9 2008") - run_cmake_presets(VisualStudioToolset) - run_cmake_presets(VisualStudioToolsetOverride -T "Test Toolset") - run_cmake_presets(VisualStudioInheritanceParent) - run_cmake_presets(VisualStudioInheritanceChild) - run_cmake_presets(VisualStudioInheritanceOverride) - run_cmake_presets(VisualStudioInheritanceMulti) - run_cmake_presets(VisualStudioInheritanceMultiSecond) - endif() + run_cmake_presets(VisualStudioToolset) + run_cmake_presets(VisualStudioToolsetOverride -T "Test Toolset") + run_cmake_presets(VisualStudioInheritanceParent) + run_cmake_presets(VisualStudioInheritanceChild) + run_cmake_presets(VisualStudioInheritanceOverride) + run_cmake_presets(VisualStudioInheritanceMulti) + run_cmake_presets(VisualStudioInheritanceMultiSecond) else() run_cmake_presets(ArchToolsetStrategyNone) run_cmake_presets(ArchToolsetStrategyDefault) @@ -407,6 +405,7 @@ set(ENV{TEST_ENV_INCLUDE_DIR} ${RunCMake_BINARY_DIR}/IncludeExpansion) run_cmake_presets(IncludeExpansion --list-presets) unset(ENV{TEST_ENV_INCLUDE_DIR}) +run_cmake_presets(IncludeExpansionOtherMacros --list-presets) unset(CMakePresets_EXTRA_FILES) run_cmake_presets(IncludeNotFound) run_cmake_presets(IncludeCycle)
diff --git a/Tests/RunCMake/CPack/DEB/packaging_GROUP_default.cmake b/Tests/RunCMake/CPack/DEB/packaging_GROUP_default.cmake new file mode 100644 index 0000000..8821ab9 --- /dev/null +++ b/Tests/RunCMake/CPack/DEB/packaging_GROUP_default.cmake
@@ -0,0 +1 @@ +set(CPACK_PACKAGE_CONTACT "someone")
diff --git a/Tests/RunCMake/CPack/External/Prerequirements.cmake b/Tests/RunCMake/CPack/External/Prerequirements.cmake index e69de29..dbaf682 100644 --- a/Tests/RunCMake/CPack/External/Prerequirements.cmake +++ b/Tests/RunCMake/CPack/External/Prerequirements.cmake
@@ -0,0 +1,4 @@ +function(get_test_prerequirements found_var config_file) + file(WRITE "${config_file}" "") + set(${found_var} true PARENT_SCOPE) +endfunction()
diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index 258f6a6..5d32404 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake
@@ -71,3 +71,5 @@ run_cpack_test(PROJECT_META "RPM.PROJECT_META;DEB.PROJECT_META" false "MONOLITHIC;COMPONENT") run_cpack_test_package_target(PRE_POST_SCRIPTS "ZIP" false "MONOLITHIC;COMPONENT") run_cpack_test_subtests(DUPLICATE_FILE "success;conflict_file;conflict_symlink" "TGZ" false "COMPONENT;GROUP") +run_cpack_test(COMPONENT_WITH_SPECIAL_CHARS "RPM.COMPONENT_WITH_SPECIAL_CHARS;DEB.COMPONENT_WITH_SPECIAL_CHARS;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ" false "MONOLITHIC;COMPONENT;GROUP") +run_cpack_test_package_target(COMPONENT_WITH_SPECIAL_CHARS "RPM.COMPONENT_WITH_SPECIAL_CHARS;DEB.COMPONENT_WITH_SPECIAL_CHARS;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ" false "MONOLITHIC;COMPONENT;GROUP")
diff --git a/Tests/RunCMake/CPack/tests/COMPONENT_WITH_SPECIAL_CHARS/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/COMPONENT_WITH_SPECIAL_CHARS/ExpectedFiles.cmake new file mode 100644 index 0000000..0eb250d --- /dev/null +++ b/Tests/RunCMake/CPack/tests/COMPONENT_WITH_SPECIAL_CHARS/ExpectedFiles.cmake
@@ -0,0 +1,42 @@ +if(PACKAGING_TYPE STREQUAL "MONOLITHIC") + set(EXPECTED_FILES_COUNT "1") + set(EXPECTED_FILE_CONTENT_1_LIST + "/foo1" + "/foo1/CMakeLists.txt" + "/foo2" + "/foo2/CMakeLists.txt" + "/foo3" + "/foo3/CMakeLists.txt" + ) +elseif(PACKAGING_TYPE STREQUAL "COMPONENT") + set(EXPECTED_FILES_COUNT "3") + set(EXPECTED_FILE_1 "*-comp1.test1.*") + set(EXPECTED_FILE_CONTENT_1_LIST + "/foo1" + "/foo1/CMakeLists.txt" + ) + set(EXPECTED_FILE_2 "*-component2.*") + set(EXPECTED_FILE_CONTENT_2_LIST + "/foo2" + "/foo2/CMakeLists.txt" + ) + set(EXPECTED_FILE_3 "*-component3.*") + set(EXPECTED_FILE_CONTENT_3_LIST + "/foo3" + "/foo3/CMakeLists.txt" + ) +elseif(PACKAGING_TYPE STREQUAL "GROUP") + set(EXPECTED_FILES_COUNT "2") + set(EXPECTED_FILE_1 "*-group1.*") + set(EXPECTED_FILE_CONTENT_1_LIST + "/foo1" + "/foo1/CMakeLists.txt" + "/foo2" + "/foo2/CMakeLists.txt" + ) + set(EXPECTED_FILE_2 "*-group2.*") + set(EXPECTED_FILE_CONTENT_2_LIST + "/foo3" + "/foo3/CMakeLists.txt" + ) +endif()
diff --git a/Tests/RunCMake/CPack/tests/COMPONENT_WITH_SPECIAL_CHARS/test.cmake b/Tests/RunCMake/CPack/tests/COMPONENT_WITH_SPECIAL_CHARS/test.cmake new file mode 100644 index 0000000..b905cc3 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/COMPONENT_WITH_SPECIAL_CHARS/test.cmake
@@ -0,0 +1,34 @@ +install(FILES CMakeLists.txt DESTINATION foo1 COMPONENT comp1.test1) +install(FILES CMakeLists.txt DESTINATION foo2 COMPONENT comp2::test2) +install(FILES CMakeLists.txt DESTINATION foo3 COMPONENT comp3/test3) + +if(PACKAGING_TYPE STREQUAL "COMPONENT") + foreach(gen IN ITEMS ARCHIVE DEBIAN RPM) + set(CPACK_${gen}_COMP2::TEST2_FILE_NAME "component_with_special_chars-0.1.1-${CMAKE_SYSTEM_NAME}-component2") + set(CPACK_${gen}_COMP3/TEST3_FILE_NAME "component_with_special_chars-0.1.1-${CMAKE_SYSTEM_NAME}-component3") + endforeach() +elseif(PACKAGING_TYPE STREQUAL "GROUP") + set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP) + foreach(gen IN ITEMS ARCHIVE DEB RPM) + set(CPACK_${gen}_COMPONENT_INSTALL ON) + endforeach() + include(CPackComponent) + + cpack_add_component_group(group1 DISPLAY_NAME "Group 1") + cpack_add_component_group(group2 DISPLAY_NAME "Group 2") + cpack_add_component(comp1.test1 + DISPLAY_NAME "Group 1" + DESCRIPTION "Component for Group 1" + GROUP group1 + ) + cpack_add_component(comp2::test2 + DISPLAY_NAME "Group 1" + DESCRIPTION "Component for Group 1" + GROUP group1 + ) + cpack_add_component(comp3/test3 + DISPLAY_NAME "Group 2" + DESCRIPTION "Component for Group 2" + GROUP group2 + ) +endif()
diff --git a/Tests/RunCMake/CPack_NuGet/.gitattributes b/Tests/RunCMake/CPack_NuGet/.gitattributes new file mode 100644 index 0000000..4a8bbd0 --- /dev/null +++ b/Tests/RunCMake/CPack_NuGet/.gitattributes
@@ -0,0 +1,3 @@ +# Do not check whitespace in the nuspec file for comparison. It needs to +# look identical to the file generated in the test. +expected.nuspec -whitespace
diff --git a/Tests/RunCMake/CPack_NuGet/NuGetLib-cpack-NuGet-check.cmake b/Tests/RunCMake/CPack_NuGet/NuGetLib-cpack-NuGet-check.cmake new file mode 100644 index 0000000..e83f007 --- /dev/null +++ b/Tests/RunCMake/CPack_NuGet/NuGetLib-cpack-NuGet-check.cmake
@@ -0,0 +1,23 @@ +file(GLOB generated_nuspec "${RunCMake_TEST_BINARY_DIR}/_CPack_Packages/*/NuGet/GeneratorTest-1.2.3-*/CPack.NuGet.nuspec") +if(NOT generated_nuspec) + set(RunCMake_TEST_FAILED "No nuspec file generated under ${RunCMake_TEST_BINARY_DIR}") +else() + # Read in the generated nuspec file content + file(READ "${generated_nuspec}" actual_nuspec) + # Read in the expected file content + file(READ "${CMAKE_CURRENT_LIST_DIR}/expected.nuspec" expected_nuspec) + + # Compare the file contents + string(COMPARE EQUAL "${actual_nuspec}" "${expected_nuspec}" nuspec_matches) + + if(NOT nuspec_matches) + set(RunCMake_TEST_FAILED "generated nuspec file incorrect") + set(failure_msg "") + # This would be nicer with a `diff` output, but it needs to be portable + string(APPEND failure_msg "\nExpected file:\n") + string(APPEND failure_msg "${expected_nuspec}") + string(APPEND failure_msg "Actual file:\n") + string(APPEND failure_msg "${actual_nuspec}") + set(RunCMake_TEST_FAILURE_MESSAGE "${failure_msg}") + endif() +endif()
diff --git a/Tests/RunCMake/CPack_NuGet/RunCMakeTest.cmake b/Tests/RunCMake/CPack_NuGet/RunCMakeTest.cmake new file mode 100644 index 0000000..af8ad31 --- /dev/null +++ b/Tests/RunCMake/CPack_NuGet/RunCMakeTest.cmake
@@ -0,0 +1,7 @@ +include(RunCPack) + +set(env_PATH "$ENV{PATH}") + +set(RunCPack_GENERATORS NuGet) + +run_cpack(NuGetLib)
diff --git a/Tests/RunCMake/CPack_NuGet/expected.nuspec b/Tests/RunCMake/CPack_NuGet/expected.nuspec new file mode 100644 index 0000000..d5c6c45 --- /dev/null +++ b/Tests/RunCMake/CPack_NuGet/expected.nuspec
@@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> + <metadata> + <!-- Required elements--> + <id>GeneratorTest</id> + <version>1.2.3</version> + <description><![CDATA[A NuGet package for testing CMake's CPack NuGet generator]]></description> + <authors><![CDATA[ACME Inc]]></authors> + + <!-- Optional elements --> + + <owners><![CDATA[ACME Inc]]></owners> + <projectUrl><![CDATA[https://www.example.com]]></projectUrl> + + <license type="expression"><![CDATA[MIT]]></license> + + + + + <summary><![CDATA[A test NuGet package]]></summary> + + + + + <repository type="git" url="https://github.com/example/nugetlib.git" /> + <dependencies> + <group> + <dependency id="Baz" version="9.8.6" /> + </group> + <group targetFramework=".NETFramework4"> + <dependency id="Foo" version="1.23" /> + <dependency id="Bar" version="4.3.2" /> + </group> + <group targetFramework="net6.0" /> + </dependencies> + </metadata> + +</package>
diff --git a/Tests/RunCMake/CPack_WIX/3-AppWiX-cpack-WIX-check.cmake b/Tests/RunCMake/CPack_WIX/3-AppWiX-cpack-WIX-check.cmake new file mode 100644 index 0000000..a7a28ae --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/3-AppWiX-cpack-WIX-check.cmake
@@ -0,0 +1 @@ +include(${RunCMake_SOURCE_DIR}/cpack-check-common.cmake)
diff --git a/Tests/RunCMake/CPack_WIX/3-AppWiX-cpack-WIX-stdout.txt b/Tests/RunCMake/CPack_WIX/3-AppWiX-cpack-WIX-stdout.txt new file mode 100644 index 0000000..2998440 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/3-AppWiX-cpack-WIX-stdout.txt
@@ -0,0 +1,11 @@ +CPack: Create package using WIX +CPack: Install projects +CPack: - Install project: CPackWiXGenerator \[Release\] +CPack: - Install component: applications +CPack: - Install component: applications2 +CPack: - Install component: extras +CPack: - Install component: headers +CPack: - Install component: libraries +CPack: Create package +CPack: - package: [^ +]*/Tests/RunCMake/CPack_WIX/3-AppWiX-build/MyLib-1\.0\.0-(win64|windows-arm64)\.msi generated\.
diff --git a/Tests/RunCMake/CPack_WIX/3-AppWiX-verify-stdout.txt b/Tests/RunCMake/CPack_WIX/3-AppWiX-verify-stdout.txt new file mode 100644 index 0000000..4713447 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/3-AppWiX-verify-stdout.txt
@@ -0,0 +1,33 @@ +-- MyLib-1\.0\.0-(win64|windows-arm64)\.msi +Component: 'CM_CP_applications.bin.my_libapp.exe' 'CM_DP_applications.bin' +Component: 'CM_SHORTCUT_applications' 'PROGRAM_MENU_FOLDER' +Component: 'CM_SHORTCUT_DESKTOP_applications' 'DesktopFolder' +Component: 'CM_CP_applications2.bin.my_other_app.exe' 'CM_DP_applications2.bin' +Component: 'CM_SHORTCUT_applications2' 'PROGRAM_MENU_FOLDER' +Component: 'CM_SHORTCUT_DESKTOP_applications2' 'DesktopFolder' +Component: 'CM_C_EMPTY_CM_DP_extras.extras.empty' 'CM_DP_extras.extras.empty' +Component: 'CM_CP_headers.include.file_with_spaces.h' 'CM_DP_headers.include' +Component: 'CM_CP_headers.include.mylib.h' 'CM_DP_headers.include' +Component: 'CM_CP_libraries.lib.mylib.lib' 'CM_DP_libraries.lib' +Directory: 'INSTALL_ROOT' 'ProgramFiles64Folder' '[^']*\|CPack Component Example' +Directory: 'CM_DP_applications.bin' 'INSTALL_ROOT' 'bin' +Directory: 'PROGRAM_MENU_FOLDER' 'ProgramMenuFolder' 'MyLib' +Directory: 'DesktopFolder' 'TARGETDIR' 'Desktop' +Directory: 'CM_DP_applications2.bin' 'INSTALL_ROOT' 'bin' +Directory: 'CM_DP_extras.extras.empty' 'CM_DP_extras.extras' 'empty' +Directory: 'CM_DP_headers.include' 'INSTALL_ROOT' 'include' +Directory: 'CM_DP_libraries.lib' 'INSTALL_ROOT' 'lib' +Directory: 'CM_DP_extras.extras' 'INSTALL_ROOT' 'extras' +Directory: 'ProgramFiles64Folder' 'TARGETDIR' '.' +Directory: 'TARGETDIR' '' 'SourceDir' +Directory: 'ProgramMenuFolder' 'TARGETDIR' '.' +File: 'CM_FP_applications.bin.my_libapp.exe' 'CM_CP_applications.bin.my_libapp.exe' '[^']*\|my-libapp.exe' +File: 'CM_FP_applications2.bin.my_other_app.exe' 'CM_CP_applications2.bin.my_other_app.exe' '[^']*\|my-other-app.exe' +File: 'CM_FP_headers.include.file_with_spaces.h' 'CM_CP_headers.include.file_with_spaces.h' '[^']*\|file with spaces.h' +File: 'CM_FP_headers.include.mylib.h' 'CM_CP_headers.include.mylib.h' 'mylib.h' +File: 'CM_FP_libraries.lib.mylib.lib' 'CM_CP_libraries.lib.mylib.lib' 'mylib.lib' +Shortcut: 'CM_SP_applications.bin.my_libapp.exe' 'PROGRAM_MENU_FOLDER' '[^']*\|CPack WiX Test' 'CM_SHORTCUT_applications' +Shortcut: 'CM_DSP_applications.bin.my_libapp.exe' 'DesktopFolder' '[^']*\|CPack WiX Test' 'CM_SHORTCUT_DESKTOP_applications' +Shortcut: 'CM_SP_applications2.bin.my_other_app.exe' 'PROGRAM_MENU_FOLDER' '[^']*\|Second CPack WiX Test' 'CM_SHORTCUT_applications2' +Shortcut: 'CM_DSP_applications2.bin.my_other_app.exe' 'DesktopFolder' '[^']*\|Second CPack WiX Test' 'CM_SHORTCUT_DESKTOP_applications2' +--
diff --git a/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-check.cmake b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-check.cmake new file mode 100644 index 0000000..a7a28ae --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-check.cmake
@@ -0,0 +1 @@ +include(${RunCMake_SOURCE_DIR}/cpack-check-common.cmake)
diff --git a/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-stdout.txt b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-stdout.txt new file mode 100644 index 0000000..51f06ca --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/4-AppWiX-cpack-WIX-stdout.txt
@@ -0,0 +1,11 @@ +CPack: Create package using WIX +CPack: Install projects +CPack: - Install project: CPackWiXGenerator \[Release\] +CPack: - Install component: applications +CPack: - Install component: applications2 +CPack: - Install component: extras +CPack: - Install component: headers +CPack: - Install component: libraries +CPack: Create package +CPack: - package: [^ +]*/Tests/RunCMake/CPack_WIX/4-AppWiX-build/MyLib-1\.0\.0-(win64|windows-arm64)\.msi generated\.
diff --git a/Tests/RunCMake/CPack_WIX/4-AppWiX-verify-stdout.txt b/Tests/RunCMake/CPack_WIX/4-AppWiX-verify-stdout.txt new file mode 100644 index 0000000..a379859 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/4-AppWiX-verify-stdout.txt
@@ -0,0 +1,34 @@ +-- MyLib-1\.0\.0-(win64|windows-arm64)\.msi +Component: 'CM_CP_applications.bin.my_libapp.exe' 'CM_DP_applications.bin' +Component: 'CM_SHORTCUT_applications' 'PROGRAM_MENU_FOLDER' +Component: 'CM_SHORTCUT_DESKTOP_applications' 'DesktopFolder' +Component: 'CM_CP_applications2.bin.my_other_app.exe' 'CM_DP_applications2.bin' +Component: 'CM_SHORTCUT_applications2' 'PROGRAM_MENU_FOLDER' +Component: 'CM_SHORTCUT_DESKTOP_applications2' 'DesktopFolder' +Component: 'CM_C_EMPTY_CM_DP_extras.extras.empty' 'CM_DP_extras.extras.empty' +Component: 'CM_CP_headers.include.file_with_spaces.h' 'CM_DP_headers.include' +Component: 'CM_CP_headers.include.mylib.h' 'CM_DP_headers.include' +Component: 'CM_CP_libraries.lib.mylib.lib' 'CM_DP_libraries.lib' +Directory: 'INSTALL_ROOT' 'ProgramFiles6432Folder' '[^']*\|CPack Component Example' +Directory: 'CM_DP_applications.bin' 'INSTALL_ROOT' 'bin' +Directory: 'CM_DP_applications2.bin' 'INSTALL_ROOT' 'bin' +Directory: 'CM_DP_extras.extras.empty' 'CM_DP_extras.extras' 'empty' +Directory: 'CM_DP_extras.extras' 'INSTALL_ROOT' 'extras' +Directory: 'CM_DP_headers.include' 'INSTALL_ROOT' 'include' +Directory: 'CM_DP_libraries.lib' 'INSTALL_ROOT' 'lib' +Directory: 'ProgramFiles6432Folder' 'ProgramFiles64Folder' '.' +Directory: 'PROGRAM_MENU_FOLDER' 'ProgramMenuFolder' 'MyLib' +Directory: 'ProgramMenuFolder' 'TARGETDIR' 'PMenu' +Directory: 'ProgramFiles64Folder' 'TARGETDIR' 'PFiles64' +Directory: 'TARGETDIR' '' 'SourceDir' +Directory: 'DesktopFolder' 'TARGETDIR' 'Desktop' +File: 'CM_FP_applications.bin.my_libapp.exe' 'CM_CP_applications.bin.my_libapp.exe' '[^']*\|my-libapp.exe' +File: 'CM_FP_applications2.bin.my_other_app.exe' 'CM_CP_applications2.bin.my_other_app.exe' '[^']*\|my-other-app.exe' +File: 'CM_FP_headers.include.file_with_spaces.h' 'CM_CP_headers.include.file_with_spaces.h' '[^']*\|file with spaces.h' +File: 'CM_FP_headers.include.mylib.h' 'CM_CP_headers.include.mylib.h' 'mylib.h' +File: 'CM_FP_libraries.lib.mylib.lib' 'CM_CP_libraries.lib.mylib.lib' 'mylib.lib' +Shortcut: 'CM_SP_applications.bin.my_libapp.exe' 'PROGRAM_MENU_FOLDER' '[^']*\|CPack WiX Test' 'CM_SHORTCUT_applications' +Shortcut: 'CM_DSP_applications.bin.my_libapp.exe' 'DesktopFolder' '[^']*\|CPack WiX Test' 'CM_SHORTCUT_DESKTOP_applications' +Shortcut: 'CM_SP_applications2.bin.my_other_app.exe' 'PROGRAM_MENU_FOLDER' '[^']*\|Second CPack WiX Test' 'CM_SHORTCUT_applications2' +Shortcut: 'CM_DSP_applications2.bin.my_other_app.exe' 'DesktopFolder' '[^']*\|Second CPack WiX Test' 'CM_SHORTCUT_DESKTOP_applications2' +--
diff --git a/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake b/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake new file mode 100644 index 0000000..816d949 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/RunCMakeTest.cmake
@@ -0,0 +1,24 @@ +include(RunCPack) + +set(env_PATH "$ENV{PATH}") + +set(RunCPack_GENERATORS WIX) +set(RunCPack_GLOB *.msi) +set(RunCPack_VERIFY powershell -ExecutionPolicy Bypass -File ${CMAKE_CURRENT_LIST_DIR}/print-msi.ps1) + +function(run_cpack_wix v) + set(RunCMake_TEST_OPTIONS -DCPACK_WIX_VERSION=${v}) + run_cpack(${v}-AppWiX SAMPLE AppWiX BUILD) +endfunction() + +if(CMake_TEST_CPACK_WIX3) + set(ENV{PATH} "${CMake_TEST_CPACK_WIX3};${env_PATH}") + run_cpack_wix(3) +endif() + +if(CMake_TEST_CPACK_WIX4) + set(ENV{PATH} "${CMake_TEST_CPACK_WIX4};${env_PATH}") + set(ENV{WIX_EXTENSIONS} "${CMake_TEST_CPACK_WIX4}") + run_cpack_wix(4) + unset(ENV{WIX_EXTENSIONS}) +endif()
diff --git a/Tests/RunCMake/CPack_WIX/cpack-check-common.cmake b/Tests/RunCMake/CPack_WIX/cpack-check-common.cmake new file mode 100644 index 0000000..2f16d71 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/cpack-check-common.cmake
@@ -0,0 +1,6 @@ +file(GLOB wix_log_file RELATIVE "${RunCMake_TEST_BINARY_DIR}" "${RunCMake_TEST_BINARY_DIR}/_CPack_Packages/*/WIX/wix.log") +if(wix_log_file) + file(READ "${RunCMake_TEST_BINARY_DIR}/${wix_log_file}" wix_log) + string(REPLACE "\n" "\n wix-log> " wix_log " wix-log> ${wix_log}") + set(RunCMake_TEST_FAILURE_MESSAGE "${wix_log_file}:\n${wix_log}") +endif()
diff --git a/Tests/RunCMake/CPack_WIX/print-msi.ps1 b/Tests/RunCMake/CPack_WIX/print-msi.ps1 new file mode 100644 index 0000000..01fdbc8 --- /dev/null +++ b/Tests/RunCMake/CPack_WIX/print-msi.ps1
@@ -0,0 +1,29 @@ +# https://learn.microsoft.com/en-us/windows/win32/msi/database-tables + +param ( + $file + ) + +function printTable { + param ( + $msi, + [string]$name, + [int[]]$columns = (1) + ) + + try { + $view = $msi.OpenView("select * from " + $name) + $view.Execute() + while ($record = $view.Fetch()) { + Write-Host ($name + ": " + ($columns | ForEach-Object {"'" + $record.StringData($_) + "'"})) + } + } catch {} +} + +$installer = New-Object -ComObject WindowsInstaller.Installer +$msi = $installer.OpenDatabase($file, 0) + +printTable -msi $msi -name "Component" -columns 1,3 +printTable -msi $msi -name "Directory" -columns 1,2,3 +printTable -msi $msi -name "File" -columns 1,2,3 +printTable -msi $msi -name "Shortcut" -columns 1,2,3,4
diff --git a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in index 5437800..e4a6f5f 100644 --- a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in +++ b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestCommandLine@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-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/FailDrop-TLSVerify-OFF-cmake-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stdout.txt new file mode 100644 index 0000000..be5d335 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSL_VERIFYPEER to off
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-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/FailDrop-TLSVerify-OFF-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stdout.txt new file mode 100644 index 0000000..be5d335 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSL_VERIFYPEER to off
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-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/FailDrop-TLSVerify-OFF-env-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stdout.txt new file mode 100644 index 0000000..be5d335 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSL_VERIFYPEER to off
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-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/FailDrop-TLSVerify-ON-cmake-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stdout.txt new file mode 100644 index 0000000..fa95148 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSL_VERIFYPEER to on
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-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/FailDrop-TLSVerify-ON-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stdout.txt new file mode 100644 index 0000000..fa95148 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSL_VERIFYPEER to on
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-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/FailDrop-TLSVerify-ON-env-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stdout.txt new file mode 100644 index 0000000..fa95148 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSL_VERIFYPEER to on
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
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 new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-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/FailDrop-TLSVersion-1.1-cmake-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stdout.txt new file mode 100644 index 0000000..e83d934 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSLVERSION to CURL_SSLVERSION_TLSv1_1
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-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/FailDrop-TLSVersion-1.1-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stdout.txt new file mode 100644 index 0000000..e83d934 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSLVERSION to CURL_SSLVERSION_TLSv1_1
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-result.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-result.txt new file mode 100644 index 0000000..d197c91 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-result.txt
@@ -0,0 +1 @@ +[^0]
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 new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-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/FailDrop-TLSVersion-1.1-env-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stdout.txt new file mode 100644 index 0000000..e83d934 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stdout.txt
@@ -0,0 +1 @@ + Set CURLOPT_SSLVERSION to CURL_SSLVERSION_TLSv1_1
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1.cmake new file mode 100644 index 0000000..e0368fc --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1.cmake
@@ -0,0 +1 @@ +include(FailDrop-common.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-common.cmake b/Tests/RunCMake/CTestCommandLine/FailDrop-common.cmake new file mode 100644 index 0000000..134d26d --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-common.cmake
@@ -0,0 +1,3 @@ +set(SUBMIT_URL "https://badhostname.invalid") +set(CTEST_SUBMIT_RETRY_COUNT 0 CACHE STRING "") +include(CTest)
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index a9f392b..190c6c1 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -1,6 +1,10 @@ include(RunCMake) include(RunCTest) +# Do not use any proxy for lookup of an invalid site. +# DNS failure by proxy looks different than DNS failure without proxy. +set(ENV{no_proxy} "$ENV{no_proxy},badhostname.invalid") + set(RunCMake_TEST_TIMEOUT 60) run_cmake_command(repeat-opt-bad1 @@ -433,6 +437,8 @@ RESOURCE_GROUPS \"2,threads:2,gpus:4;gpus:2,threads:4\" REQUIRED_FILES RequiredFileDoesNotExist _BACKTRACE_TRIPLES \"file1;1;add_test;file0;;\" + USER_DEFINED_A \"User defined property A value\" + USER_DEFINED_B \"User defined property B value\" ) add_test(ShowOnlyNotAvailable NOT_AVAILABLE) ") @@ -482,6 +488,32 @@ # Check the configuration type variable is passed run_ctest(check-configuration-type) +function(run_FailDrop case) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/FailDrop-${case}-build) + run_cmake_with_options(FailDrop-${case} ${ARGN}) + unset(ENV{CMAKE_TLS_VERIFY}) # Test that env variable is saved in ctest config file. + unset(ENV{CMAKE_TLS_VERSION}) # Test that env variable is saved in ctest config file. + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(FailDrop-${case}-ctest + ${CMAKE_CTEST_COMMAND} -M Experimental -T Submit -VV + ) +endfunction() +run_FailDrop(TLSVersion-1.1 -DCTEST_TLS_VERSION=1.1) +run_FailDrop(TLSVersion-1.1-cmake -DCMAKE_TLS_VERSION=1.1) # Test fallback to CMake variable. +set(ENV{CMAKE_TLS_VERSION} 1.1) # Test fallback to env variable. +run_FailDrop(TLSVersion-1.1-env) +unset(ENV{CMAKE_TLS_VERSION}) +run_FailDrop(TLSVerify-ON -DCTEST_TLS_VERIFY=ON) +run_FailDrop(TLSVerify-ON-cmake -DCMAKE_TLS_VERIFY=ON) # Test fallback to CMake variable. +set(ENV{CMAKE_TLS_VERIFY} 1) # Test fallback to env variable. +run_FailDrop(TLSVerify-ON-env) +unset(ENV{CMAKE_TLS_VERIFY}) +run_FailDrop(TLSVerify-OFF -DCTEST_TLS_VERIFY=OFF) +run_FailDrop(TLSVerify-OFF-cmake -DCMAKE_TLS_VERIFY=OFF) # Test fallback to CMake variable. +set(ENV{CMAKE_TLS_VERIFY} 0) # Test fallback to env variable. +run_FailDrop(TLSVerify-OFF-env) +unset(ENV{CMAKE_TLS_VERIFY}) + run_cmake_command(EmptyDirCoverage-ctest ${CMAKE_CTEST_COMMAND} -C Debug -M Experimental -T Coverage ) @@ -510,7 +542,7 @@ run_cmake_command(test-dir-invalid-arg ${CMAKE_CTEST_COMMAND} --test-dir) run_cmake_command(test-dir-non-existing-dir ${CMAKE_CTEST_COMMAND} --test-dir non-existing-dir) -function(run_testDir) +function(run_testDir testName testPreset) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/testDir) set(RunCMake_TEST_NO_CLEAN 1) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") @@ -520,9 +552,16 @@ add_test(Test1 \"${CMAKE_COMMAND}\" -E true) add_test(Test2 \"${CMAKE_COMMAND}\" -E true) ") - run_cmake_command(testDir ${CMAKE_CTEST_COMMAND} --test-dir "${RunCMake_TEST_BINARY_DIR}/sub") + if (testPreset) + set(presetCommandLine --preset=default) + configure_file( + ${RunCMake_SOURCE_DIR}/testDir-presets.json.in + ${RunCMake_TEST_BINARY_DIR}/CMakePresets.json) + endif() + run_cmake_command(${testName} ${CMAKE_CTEST_COMMAND} --test-dir "${RunCMake_TEST_BINARY_DIR}/sub" ${presetCommandLine}) endfunction() -run_testDir() +run_testDir(testDir 0) +run_testDir(testDir-preset 1) # Test --output-junit function(run_output_junit) @@ -543,6 +582,8 @@ endfunction() run_output_junit() +run_cmake_command(invalid-ctest-argument ${CMAKE_CTEST_COMMAND} --not-a-valid-ctest-argument) + if(WIN32) block() set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TimeoutSignalWindows)
diff --git a/Tests/RunCMake/CTestCommandLine/TestOutputSize-check.cmake b/Tests/RunCMake/CTestCommandLine/TestOutputSize-check.cmake index 918d242..53aa318 100644 --- a/Tests/RunCMake/CTestCommandLine/TestOutputSize-check.cmake +++ b/Tests/RunCMake/CTestCommandLine/TestOutputSize-check.cmake
@@ -1,6 +1,6 @@ file(GLOB test_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml") if(test_xml_file) - file(READ "${test_xml_file}" test_xml LIMIT 4096) + file(READ "${test_xml_file}" test_xml LIMIT 8192) if("${test_xml}" MATCHES [[(<Test Status="passed">.*</Test>).*(<Test Status="failed">.*</Test>)]]) set(test_passed "${CMAKE_MATCH_1}") set(test_failed "${CMAKE_MATCH_2}")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CTestCommandLine/invalid-ctest-argument-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CTestCommandLine/invalid-ctest-argument-result.txt
diff --git a/Tests/RunCMake/CTestCommandLine/invalid-ctest-argument-stderr.txt b/Tests/RunCMake/CTestCommandLine/invalid-ctest-argument-stderr.txt new file mode 100644 index 0000000..3304a26 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/invalid-ctest-argument-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error: Unknown argument: --not-a-valid-ctest-argument +CMake Error: Run 'ctest --help' for all supported options.
diff --git a/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py b/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py index b818650..f9667ae 100644 --- a/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py +++ b/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
@@ -144,13 +144,23 @@ assert p["name"] == "WORKING_DIRECTORY" assert p["value"].endswith("Tests/RunCMake/CTestCommandLine/ShowOnly") +def check_defined_properties(p_list): + for property_id, p in zip(["A", "B"], p_list): + assert is_dict(p) + assert sorted(p.keys()) == ["name", "value"] + assert is_string(p["name"]) + assert is_string(p["value"]) + assert p["name"] == "USER_DEFINED_" + property_id + assert p["value"] == "User defined property " + property_id + " value" + def check_properties(p): assert is_list(p) - assert len(p) == 4 + assert len(p) == 6 check_resource_groups_property(p[0]) check_reqfiles_property(p[1]) check_willfail_property(p[2]) check_workingdir_property(p[3]) + check_defined_properties(p[4:5]) def check_tests(t): assert is_list(t)
diff --git a/Tests/RunCMake/CTestCommandLine/testDir-presets.json.in b/Tests/RunCMake/CTestCommandLine/testDir-presets.json.in new file mode 100644 index 0000000..46391f8 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/testDir-presets.json.in
@@ -0,0 +1,20 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 22, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "binaryDir": "presetBinaryDir" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "default" + } + ] +}
diff --git a/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake b/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake index 42e13fc..3c3e88f 100644 --- a/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
@@ -167,6 +167,16 @@ endif() endfunction() +set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/invalid-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}/CTestTestfile.cmake" "add_test(true \"${CMAKE_COMMAND}\" -E true)\n") +run_cmake_command(invalid-nofile-ctest ${CMAKE_CTEST_COMMAND} --resource-spec-file "${RunCMake_BINARY_DIR}/noexist.json") +run_cmake_command(invalid-not-json-ctest ${CMAKE_CTEST_COMMAND} --resource-spec-file "${RunCMake_SOURCE_DIR}/invalid.json") +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + run_ctest_resource(lotsoftests 10 1 0) run_ctest_resource(checkfree1 2 0 1) run_ctest_resource(checkfree2 1 0 0)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt index 397ca38..0a927e7 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt
@@ -1,3 +1,5 @@ ^Error: a cycle exists in the test dependency graph for the test "GenerateSpecFile"\. -Please fix the cycle and run ctest again. -No tests were found!!!$ +Please fix the cycle and run ctest again\. +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt index 06ea90f..533b07c 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^All tests that have RESOURCE_GROUPS must include the resource spec generator fixture in their FIXTURES_REQUIRED -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt index 4e4c01c..d91d6dc 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^GENERATED_RESOURCE_SPEC_FILE test property cannot be used in conjunction with ResourceSpecFile option -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt index 273cb80..def7a8b 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^Only one test may define the GENERATED_RESOURCE_SPEC_FILE property -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt index 39ee275..3301318 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^Test that defines GENERATED_RESOURCE_SPEC_FILE must have exactly one FIXTURES_SETUP -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt index 06ea90f..a3c4d20 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^All tests that have RESOURCE_GROUPS must include the resource spec generator fixture in their FIXTURES_REQUIRED -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt index 39ee275..92a93bb 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^Test that defines GENERATED_RESOURCE_SPEC_FILE must have exactly one FIXTURES_SETUP -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt index 2c4dff8..6829ffe 100644 --- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt +++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt
@@ -1,2 +1,4 @@ ^GENERATED_RESOURCE_SPEC_FILE must be an absolute path -No tests were found!!!$ +CMake Error at [^ +]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s/test\.cmake:[0-9]+ \(message\): + Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-result.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-stderr.txt new file mode 100644 index 0000000..df1135e --- /dev/null +++ b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-stderr.txt
@@ -0,0 +1,8 @@ +^Could not read/parse resource spec file [^ +]*/Tests/RunCMake/CTestResourceAllocation/noexist\.json:[ ] +File not found: [^ +]*/Tests/RunCMake/CTestResourceAllocation/noexist.json +Errors while running CTest +Output from these tests are in: [^ +]*/Tests/RunCMake/CTestResourceAllocation/invalid-build/Testing/Temporary/LastTest\.log +Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-result.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-result.txt
@@ -0,0 +1 @@ +8
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-stderr.txt new file mode 100644 index 0000000..5bd9d97 --- /dev/null +++ b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-stderr.txt
@@ -0,0 +1,15 @@ +^Could not read/parse resource spec file [^ +]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json:[ ] +JSON Parse Error: [^ +]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json: +\* Line 1, Column 1 + Syntax error: value, object or array expected\. +(\* Line 1, Column 2 + Extra non-whitespace after JSON value\. +|\* Line 1, Column 1 + A valid JSON document must be either an array or an object value\. +) +Errors while running CTest +Output from these tests are in: [^ +]*/Tests/RunCMake/CTestResourceAllocation/invalid-build/Testing/Temporary/LastTest\.log +Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid.json b/Tests/RunCMake/CTestResourceAllocation/invalid.json new file mode 100644 index 0000000..4c861f8 --- /dev/null +++ b/Tests/RunCMake/CTestResourceAllocation/invalid.json
@@ -0,0 +1 @@ +This is not a valid JSON file!
diff --git a/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake b/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake index d68775a..c754930 100644 --- a/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake +++ b/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake
@@ -1,3 +1,6 @@ +# 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.cmake b/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake index 201598e..a994266 100644 --- a/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake +++ b/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake
@@ -1,3 +1,6 @@ +# 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/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CXXModules/CXXImportStdConfig-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CXXModules/CXXImportStdConfig-result.txt
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdConfig-stderr.txt b/Tests/RunCMake/CXXModules/CXXImportStdConfig-stderr.txt new file mode 100644 index 0000000..268099f --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdConfig-stderr.txt
@@ -0,0 +1,3 @@ +CMake Error in CMakeLists.txt: + The "CXX_MODULE_STD" property on the target "nocxx23target" contains a + context-sensitive condition that is not supported.
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdConfig.cmake b/Tests/RunCMake/CXXModules/CXXImportStdConfig.cmake new file mode 100644 index 0000000..0867082 --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdConfig.cmake
@@ -0,0 +1,10 @@ +enable_language(CXX) + +set(CMAKE_CXX_MODULE_STD "$<CONFIG:Release>") + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CXXModules/CXXImportStdHeadTarget-result.txt
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget-stderr.txt b/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget-stderr.txt new file mode 100644 index 0000000..a2cdb63 --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget-stderr.txt
@@ -0,0 +1,9 @@ +CMake Error: + Error evaluating generator expression: + + \$<TARGET_PROPERTY:use_std_in_consumed> + + \$<TARGET_PROPERTY:prop> may only be used with binary targets. It may not + be used with add_custom_command or add_custom_target. Specify the target + to read a property from using the \$<TARGET_PROPERTY:tgt,prop> signature + instead.
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget.cmake b/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget.cmake new file mode 100644 index 0000000..3eb2c68 --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdHeadTarget.cmake
@@ -0,0 +1,10 @@ +enable_language(CXX) + +set(CMAKE_CXX_MODULE_STD "$<TARGET_PROPERTY:use_std_in_consumed>") + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdInvalidGenex.cmake b/Tests/RunCMake/CXXModules/CXXImportStdInvalidGenex.cmake new file mode 100644 index 0000000..86230a4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdInvalidGenex.cmake
@@ -0,0 +1,11 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +set(CMAKE_CXX_MODULE_STD "$<STREQUAL:") + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage-result.txt
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage-stderr.txt b/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage-stderr.txt new file mode 100644 index 0000000..21148a2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage-stderr.txt
@@ -0,0 +1,7 @@ +CMake Error: + Error evaluating generator expression: + + \$<LINK_LANGUAGE:CXX> + + \$<LINK_LANGUAGE:...> may only be used with binary targets to specify link + libraries, link directories, link options and link depends.
diff --git a/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage.cmake b/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage.cmake new file mode 100644 index 0000000..e9b20c7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/CXXImportStdLinkLanguage.cmake
@@ -0,0 +1,10 @@ +enable_language(CXX) + +set(CMAKE_CXX_MODULE_STD "$<LINK_LANGUAGE:CXX>") + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake index cac1777..64d69f3 100644 --- a/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake +++ b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake
@@ -1,6 +1,9 @@ # 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 612b01b..e648e8c 100644 --- a/Tests/RunCMake/CXXModules/Inspect.cmake +++ b/Tests/RunCMake/CXXModules/Inspect.cmake
@@ -15,11 +15,28 @@ 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}\") set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\") set(forced_cxx_standard \"${forced_cxx_standard}\") +set(have_cxx23_import_std \"${have_cxx23_import_std}\") set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\") set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\") set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\")
diff --git a/Tests/RunCMake/CXXModules/NoCXX20.cmake b/Tests/RunCMake/CXXModules/NoCXX20.cmake index b7372e8..9710728 100644 --- a/Tests/RunCMake/CXXModules/NoCXX20.cmake +++ b/Tests/RunCMake/CXXModules/NoCXX20.cmake
@@ -1,3 +1,6 @@ +# Block making C++ `import std` targets. +add_library(__CMAKE::CXX23 IMPORTED INTERFACE) + enable_language(CXX) add_library(nocxx20)
diff --git a/Tests/RunCMake/CXXModules/NoCXX23TargetNotRequired.cmake b/Tests/RunCMake/CXXModules/NoCXX23TargetNotRequired.cmake new file mode 100644 index 0000000..2d9bffa --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX23TargetNotRequired.cmake
@@ -0,0 +1,11 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +set(CMAKE_CXX_MODULE_STD 0) + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CXXModules/NoCXX23TargetRequired-result.txt
diff --git a/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt new file mode 100644 index 0000000..866fa55 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt
@@ -0,0 +1,9 @@ +CMake Error in CMakeLists.txt: + The "CXX_MODULE_STD" property on the target "nocxx23target" requires that + the "__CMAKE::CXX23" target exist, but it was not provided by the + toolchain. Reason: + + (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*) + + +CMake Generate step failed. Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX23TargetRequired.cmake b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired.cmake new file mode 100644 index 0000000..fac05e2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired.cmake
@@ -0,0 +1,11 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +set(CMAKE_CXX_MODULE_STD 1) + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CXXModules/NoCXX23TargetUnset.cmake b/Tests/RunCMake/CXXModules/NoCXX23TargetUnset.cmake new file mode 100644 index 0000000..8f6a656 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX23TargetUnset.cmake
@@ -0,0 +1,13 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +# TODO(cxxmodules): Add instances of this test which test the policy +# of the property's unset behavior. +# set(CMAKE_CXX_MODULE_STD …) + +add_library(nocxx23target) +target_sources(nocxx23target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx23target PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CXXModules/NoCXX26TargetNotRequired.cmake b/Tests/RunCMake/CXXModules/NoCXX26TargetNotRequired.cmake new file mode 100644 index 0000000..b91fa9b --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX26TargetNotRequired.cmake
@@ -0,0 +1,11 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +set(CMAKE_CXX_MODULE_STD 0) + +add_library(nocxx26target) +target_sources(nocxx26target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx26target PRIVATE cxx_std_26)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CXXModules/NoCXX26TargetRequired-result.txt
diff --git a/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt new file mode 100644 index 0000000..6e14f1e --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt
@@ -0,0 +1,9 @@ +CMake Error in CMakeLists.txt: + The "CXX_MODULE_STD" property on the target "nocxx26target" requires that + the "__CMAKE::CXX26" target exist, but it was not provided by the + toolchain. Reason: + + (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*) + + +CMake Generate step failed. Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX26TargetRequired.cmake b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired.cmake new file mode 100644 index 0000000..79dcb79 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired.cmake
@@ -0,0 +1,11 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +set(CMAKE_CXX_MODULE_STD 1) + +add_library(nocxx26target) +target_sources(nocxx26target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx26target PRIVATE cxx_std_26)
diff --git a/Tests/RunCMake/CXXModules/NoCXX26TargetUnset.cmake b/Tests/RunCMake/CXXModules/NoCXX26TargetUnset.cmake new file mode 100644 index 0000000..85108ff --- /dev/null +++ b/Tests/RunCMake/CXXModules/NoCXX26TargetUnset.cmake
@@ -0,0 +1,13 @@ +enable_language(CXX) +set(CMAKE_CXX_SCANDEP_SOURCE "echo") + +# TODO(cxxmodules): Add instances of this test which test the policy +# of the property's unset behavior. +# set(CMAKE_CXX_MODULE_STD …) + +add_library(nocxx26target) +target_sources(nocxx26target + PRIVATE + FILE_SET fs TYPE CXX_MODULES FILES + sources/module.cxx) +target_compile_features(nocxx26target PRIVATE cxx_std_26)
diff --git a/Tests/RunCMake/CXXModules/NoScanningVariable.cmake b/Tests/RunCMake/CXXModules/NoScanningVariable.cmake index 4bb6a70..950f1f3 100644 --- a/Tests/RunCMake/CXXModules/NoScanningVariable.cmake +++ b/Tests/RunCMake/CXXModules/NoScanningVariable.cmake
@@ -1,6 +1,9 @@ # 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 23c32c0..8b7bc86 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
@@ -83,6 +83,27 @@ run_cmake("NotCXXSource${fileset_type}") endforeach () +if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + run_cmake(CXXImportStdConfig) + run_cmake(CXXImportStdHeadTarget) + run_cmake(CXXImportStdLinkLanguage) + run_cmake(CXXImportStdInvalidGenex) +endif () + +if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND + NOT have_cxx23_import_std) + run_cmake(NoCXX23TargetUnset) + run_cmake(NoCXX23TargetNotRequired) + run_cmake(NoCXX23TargetRequired) +endif () + +if ("cxx_std_26" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND + NOT have_cxx26_import_std) + run_cmake(NoCXX26TargetUnset) + run_cmake(NoCXX26TargetNotRequired) + run_cmake(NoCXX26TargetRequired) +endif () + run_cmake(InstallBMI) run_cmake(InstallBMIGenericArgs) run_cmake(InstallBMIIgnore) @@ -176,6 +197,7 @@ # - `partitions`: module partitions are supported # - `internal_partitions`: internal module partitions are supported # - `bmionly`: the compiler supports BMI-only builds +# - `import_std23`: the compiler supports `import std` for C++23 # # Generator-based: # - `compile_commands`: the generator supports `compile_commands.json` @@ -222,6 +244,31 @@ run_cxx_module_test(same-src-name) run_cxx_module_test(scan_properties) run_cxx_module_test(target-objects) + + if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND + "import_std23" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(import-std) + set(RunCMake_CXXModules_NO_TEST 1) + run_cxx_module_test(import-std-no-std-property) + unset(RunCMake_CXXModules_NO_TEST) + run_cxx_module_test(import-std-export-no-std-build) + set(RunCMake_CXXModules_INSTALL 1) + run_cxx_module_test(import-std-export-no-std-install) + unset(RunCMake_CXXModules_INSTALL) + + if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION) + run_cxx_module_test(import-std-not-in-export-build) + run_cxx_module_test(import-std-transitive import-std-transitive-not-in-export-build "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/import-std-not-in-export-build-build") + + set(RunCMake_CXXModules_INSTALL 1) + run_cxx_module_test(import-std-not-in-export-install) + unset(RunCMake_CXXModules_INSTALL) + run_cxx_module_test(import-std-transitive import-std-transitive-not-in-export-install "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/import-std-not-in-export-install-install") + + run_cxx_module_test(import-std-transitive import-std-transitive-export-no-std-build "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/import-std-export-no-std-build-build" -DEXPORT_NO_STD=1) + run_cxx_module_test(import-std-transitive import-std-transitive-export-no-std-install "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/import-std-export-no-std-install-install" -DEXPORT_NO_STD=1) + endif () + endif () endif () # Tests which require compile commands support.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/CMakeLists.txt new file mode 100644 index 0000000..89350ef --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/CMakeLists.txt
@@ -0,0 +1,59 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_import_std_export_no_std CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +set(CMAKE_NO_STD 1) + +add_library(import_std_export_no_std) +target_sources(import_std_export_no_std + PRIVATE + uses-std.cxx + PUBLIC + FILE_SET use_std TYPE CXX_MODULES FILES + impl-uses-std.cxx) +target_compile_features(import_std_export_no_std PUBLIC cxx_std_23) +set_property(TARGET import_std_export_no_std + PROPERTY + CXX_MODULE_STD "$<BOOL:$<BUILD_LOCAL_INTERFACE:1>>") + +add_executable(main + main.cxx) +target_link_libraries(main PRIVATE import_std_export_no_std) + +install(TARGETS import_std_export_no_std + EXPORT export + ARCHIVE DESTINATION "lib" + FILE_SET use_std DESTINATION "lib/cxx/miu") +export(EXPORT export + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/import_std_export_no_std-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/import_std_export_no_std-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/import_std_export_no_std-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") + +add_test(NAME main COMMAND main) + +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 import_std_export_no_std_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dimport_std_export_no_std_DIR=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/impl-uses-std.cxx b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/impl-uses-std.cxx new file mode 100644 index 0000000..ee19332 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/impl-uses-std.cxx
@@ -0,0 +1,3 @@ +export module uses_std; + +export int f();
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/main.cxx new file mode 100644 index 0000000..a6dd8d8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/main.cxx
@@ -0,0 +1,6 @@ +import uses_std; + +int main(int argc, char* argv[]) +{ + return f(); +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/test/CMakeLists.txt new file mode 100644 index 0000000..e37f174 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/test/CMakeLists.txt
@@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_library NONE) + +find_package(import_std_export_no_std REQUIRED) + +if (NOT TARGET CXXModules::import_std_export_no_std) + message(FATAL_ERROR + "Missing imported target") +endif () + +function (check_property expected property) + get_property(actual TARGET CXXModules::import_std_export_no_std + PROPERTY "${property}") + if (NOT DEFINED actual) + if (NOT expected STREQUAL "<UNDEF>") + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: NOT-DEFINED") + endif () + elseif (NOT actual STREQUAL expected) + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: ${actual}") + endif () +endfunction () + +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES") +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS") +check_property("cxx_std_23" "IMPORTED_CXX_MODULES_COMPILE_FEATURES") +check_property("" "IMPORTED_CXX_MODULES_LINK_LIBRARIES") +check_property("<UNDEF>" "INTERFACE_LINK_LIBRARIES") +check_property("$<BOOL:>" "CXX_MODULE_STD") + +# Extract the export-dependent targets from the export file. +file(STRINGS "${import_std_export_no_std_DIR}/import_std_export_no_std-targets.cmake" usage_dependent_targets + REGEX "foreach._target ") +# Rudimentary argument splitting. +string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}") +# Remove exported "target" names. +list(FILTER usage_dependent_targets EXCLUDE REGEX "CXXModules::") +# Strip quotes. +string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") + +if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export requires the '__CMAKE::CXX23' target") +endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/uses-std.cxx b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/uses-std.cxx new file mode 100644 index 0000000..bd91093 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-build/uses-std.cxx
@@ -0,0 +1,8 @@ +module uses_std; + +import std; + +int f() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/CMakeLists.txt new file mode 100644 index 0000000..62d9d91 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/CMakeLists.txt
@@ -0,0 +1,61 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_import_std_export_no_std CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(import_std_export_no_std) +target_sources(import_std_export_no_std + PRIVATE + uses-std.cxx + PUBLIC + FILE_SET use_std TYPE CXX_MODULES FILES + impl-uses-std.cxx) +target_compile_features(import_std_export_no_std PUBLIC cxx_std_23) +set_property(TARGET import_std_export_no_std + PROPERTY + CXX_MODULE_STD "$<BOOL:$<BUILD_LOCAL_INTERFACE:1>>") + +add_executable(main + main.cxx) +target_link_libraries(main PRIVATE import_std_export_no_std) + +install(TARGETS import_std_export_no_std + EXPORT export + ARCHIVE DESTINATION "lib" + FILE_SET use_std DESTINATION "lib/cxx/miu") +install( + EXPORT export + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/import_std_export_no_std" + FILE "import_std_export_no_std-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/import_std_export_no_std-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/import_std_export_no_std-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/import_std_export_no_std-config.cmake" + DESTINATION "lib/cmake/import_std_export_no_std") + +add_test(NAME main COMMAND main) + +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 import_std_export_no_std_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dimport_std_export_no_std_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/import_std_export_no_std" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/impl-uses-std.cxx b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/impl-uses-std.cxx new file mode 100644 index 0000000..ee19332 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/impl-uses-std.cxx
@@ -0,0 +1,3 @@ +export module uses_std; + +export int f();
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/main.cxx new file mode 100644 index 0000000..a6dd8d8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/main.cxx
@@ -0,0 +1,6 @@ +import uses_std; + +int main(int argc, char* argv[]) +{ + return f(); +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/test/CMakeLists.txt new file mode 100644 index 0000000..e37f174 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/test/CMakeLists.txt
@@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_library NONE) + +find_package(import_std_export_no_std REQUIRED) + +if (NOT TARGET CXXModules::import_std_export_no_std) + message(FATAL_ERROR + "Missing imported target") +endif () + +function (check_property expected property) + get_property(actual TARGET CXXModules::import_std_export_no_std + PROPERTY "${property}") + if (NOT DEFINED actual) + if (NOT expected STREQUAL "<UNDEF>") + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: NOT-DEFINED") + endif () + elseif (NOT actual STREQUAL expected) + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: ${actual}") + endif () +endfunction () + +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES") +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS") +check_property("cxx_std_23" "IMPORTED_CXX_MODULES_COMPILE_FEATURES") +check_property("" "IMPORTED_CXX_MODULES_LINK_LIBRARIES") +check_property("<UNDEF>" "INTERFACE_LINK_LIBRARIES") +check_property("$<BOOL:>" "CXX_MODULE_STD") + +# Extract the export-dependent targets from the export file. +file(STRINGS "${import_std_export_no_std_DIR}/import_std_export_no_std-targets.cmake" usage_dependent_targets + REGEX "foreach._target ") +# Rudimentary argument splitting. +string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}") +# Remove exported "target" names. +list(FILTER usage_dependent_targets EXCLUDE REGEX "CXXModules::") +# Strip quotes. +string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") + +if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export requires the '__CMAKE::CXX23' target") +endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/uses-std.cxx b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/uses-std.cxx new file mode 100644 index 0000000..6b7e814 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-export-no-std-install/uses-std.cxx
@@ -0,0 +1,9 @@ +module uses_std; +import std; + +int f() +{ + std::string str = "hello!"; + std::cout << "program: " << str << std::endl; + return 0; +}
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-build-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/CXXModules/examples/import-std-no-std-property-build-result.txt
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-build-stdout.txt b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-build-stdout.txt new file mode 100644 index 0000000..d473333 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-build-stdout.txt
@@ -0,0 +1 @@ +((Clang)?module 'std' not found|(MSVC)?could not find module 'std')
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-no-std-property/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property/CMakeLists.txt new file mode 100644 index 0000000..a605e95 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property/CMakeLists.txt
@@ -0,0 +1,13 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_import_std_no_std_property CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +set(CMAKE_CXX_MODULE_STD 0) + +add_executable(main + main.cxx) +target_compile_features(main PRIVATE cxx_std_23)
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-no-std-property/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property/main.cxx new file mode 100644 index 0000000..fee84f1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-no-std-property/main.cxx
@@ -0,0 +1,6 @@ +import std; + +int main(int argc, char* argv[]) +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/CMakeLists.txt new file mode 100644 index 0000000..3112002 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/CMakeLists.txt
@@ -0,0 +1,54 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_import_std_not_in_export CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +set(CMAKE_CXX_MODULE_STD 1) + +add_library(import_std_not_in_export) +target_sources(import_std_not_in_export + PUBLIC + FILE_SET use_std TYPE CXX_MODULES FILES + uses-std.cxx) +target_compile_features(import_std_not_in_export PUBLIC cxx_std_23) + +add_executable(main + main.cxx) +target_link_libraries(main PRIVATE import_std_not_in_export) + +install(TARGETS import_std_not_in_export + EXPORT export + ARCHIVE DESTINATION "lib" + FILE_SET use_std DESTINATION "lib/cxx/miu") +export(EXPORT export + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/import_std_not_in_export-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/import_std_not_in_export-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/import_std_not_in_export-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") + +add_test(NAME main COMMAND main) + +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 import_std_not_in_export_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dimport_std_not_in_export_DIR=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/main.cxx new file mode 100644 index 0000000..a6dd8d8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/main.cxx
@@ -0,0 +1,6 @@ +import uses_std; + +int main(int argc, char* argv[]) +{ + return f(); +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/test/CMakeLists.txt new file mode 100644 index 0000000..4e994d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/test/CMakeLists.txt
@@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_library NONE) + +find_package(import_std_not_in_export REQUIRED) + +if (NOT TARGET CXXModules::import_std_not_in_export) + message(FATAL_ERROR + "Missing imported target") +endif () + +function (check_property expected property) + get_property(actual TARGET CXXModules::import_std_not_in_export + PROPERTY "${property}") + if (NOT DEFINED actual) + if (NOT expected STREQUAL "<UNDEF>") + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: NOT-DEFINED") + endif () + elseif (NOT actual STREQUAL expected) + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: ${actual}") + endif () +endfunction () + +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES") +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS") +check_property("cxx_std_23" "IMPORTED_CXX_MODULES_COMPILE_FEATURES") +check_property("" "IMPORTED_CXX_MODULES_LINK_LIBRARIES") +check_property("<UNDEF>" "INTERFACE_LINK_LIBRARIES") +check_property("1" "CXX_MODULE_STD") + +# Extract the export-dependent targets from the export file. +file(STRINGS "${import_std_not_in_export_DIR}/import_std_not_in_export-targets.cmake" usage_dependent_targets + REGEX "foreach._target ") +# Rudimentary argument splitting. +string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}") +# Remove exported "target" names. +list(FILTER usage_dependent_targets EXCLUDE REGEX "CXXModules::") +# Strip quotes. +string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") + +if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export requires the '__CMAKE::CXX23' target") +endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/uses-std.cxx b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/uses-std.cxx new file mode 100644 index 0000000..aa45dd1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-build/uses-std.cxx
@@ -0,0 +1,7 @@ +export module uses_std; +import std; + +export int f() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/CMakeLists.txt new file mode 100644 index 0000000..788ea84 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/CMakeLists.txt
@@ -0,0 +1,58 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_import_std_not_in_export CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +set(CMAKE_CXX_MODULE_STD 1) + +add_library(import_std_not_in_export) +target_sources(import_std_not_in_export + PUBLIC + FILE_SET use_std TYPE CXX_MODULES FILES + uses-std.cxx) +target_compile_features(import_std_not_in_export PUBLIC cxx_std_23) + +add_executable(main + main.cxx) +target_link_libraries(main PRIVATE import_std_not_in_export) + +install(TARGETS import_std_not_in_export + EXPORT export + ARCHIVE DESTINATION "lib" + FILE_SET use_std DESTINATION "lib/cxx/miu") +install( + EXPORT export + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/import_std_not_in_export" + FILE "import_std_not_in_export-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/import_std_not_in_export-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/import_std_not_in_export-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/import_std_not_in_export-config.cmake" + DESTINATION "lib/cmake/import_std_not_in_export") + +add_test(NAME main COMMAND main) + +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 import_std_not_in_export_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dimport_std_not_in_export_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/import_std_not_in_export" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/main.cxx new file mode 100644 index 0000000..a6dd8d8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/main.cxx
@@ -0,0 +1,6 @@ +import uses_std; + +int main(int argc, char* argv[]) +{ + return f(); +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/test/CMakeLists.txt new file mode 100644 index 0000000..4e994d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/test/CMakeLists.txt
@@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_library NONE) + +find_package(import_std_not_in_export REQUIRED) + +if (NOT TARGET CXXModules::import_std_not_in_export) + message(FATAL_ERROR + "Missing imported target") +endif () + +function (check_property expected property) + get_property(actual TARGET CXXModules::import_std_not_in_export + PROPERTY "${property}") + if (NOT DEFINED actual) + if (NOT expected STREQUAL "<UNDEF>") + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: NOT-DEFINED") + endif () + elseif (NOT actual STREQUAL expected) + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: ${actual}") + endif () +endfunction () + +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES") +check_property("<UNDEF>" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS") +check_property("cxx_std_23" "IMPORTED_CXX_MODULES_COMPILE_FEATURES") +check_property("" "IMPORTED_CXX_MODULES_LINK_LIBRARIES") +check_property("<UNDEF>" "INTERFACE_LINK_LIBRARIES") +check_property("1" "CXX_MODULE_STD") + +# Extract the export-dependent targets from the export file. +file(STRINGS "${import_std_not_in_export_DIR}/import_std_not_in_export-targets.cmake" usage_dependent_targets + REGEX "foreach._target ") +# Rudimentary argument splitting. +string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}") +# Remove exported "target" names. +list(FILTER usage_dependent_targets EXCLUDE REGEX "CXXModules::") +# Strip quotes. +string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") + +if ("__CMAKE::CXX23" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export requires the '__CMAKE::CXX23' target") +endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/uses-std.cxx b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/uses-std.cxx new file mode 100644 index 0000000..aa45dd1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-not-in-export-install/uses-std.cxx
@@ -0,0 +1,7 @@ +export module uses_std; +import std; + +export int f() +{ + return 0; +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-transitive-not-in-export-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-transitive-not-in-export-build-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-transitive-not-in-export-build-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-transitive-not-in-export-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-std-transitive-not-in-export-install-stderr.txt new file mode 100644 index 0000000..3589448 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-transitive-not-in-export-install-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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_create_cxx_import_std\) + .*/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.
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-transitive/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std-transitive/CMakeLists.txt new file mode 100644 index 0000000..2894d67 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-transitive/CMakeLists.txt
@@ -0,0 +1,28 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) + +if (EXPORT_NO_STD) + # Block making C++ `import std` targets. + add_library(__CMAKE::CXX23 IMPORTED INTERFACE) +endif () + +project(cxx_modules_import_std_transitive CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +if (EXPORT_NO_STD) + set(package "import_std_export_no_std") +else () + set(package "import_std_not_in_export") +endif () +find_package("${package}" REQUIRED) + +set(CMAKE_CXX_MODULE_STD 0) + +add_executable(main + main.cxx) +target_link_libraries(main PRIVATE "CXXModules::${package}") + +add_test(NAME main COMMAND main)
diff --git a/Tests/RunCMake/CXXModules/examples/import-std-transitive/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std-transitive/main.cxx new file mode 100644 index 0000000..a6dd8d8 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std-transitive/main.cxx
@@ -0,0 +1,6 @@ +import uses_std; + +int main(int argc, char* argv[]) +{ + return f(); +}
diff --git a/Tests/RunCMake/CXXModules/examples/import-std/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-std/CMakeLists.txt new file mode 100644 index 0000000..bffcd66 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std/CMakeLists.txt
@@ -0,0 +1,15 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_import_std CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +set(CMAKE_CXX_MODULE_STD 1) + +add_executable(main + main.cxx) +target_compile_features(main PRIVATE cxx_std_23) + +add_test(NAME main COMMAND main)
diff --git a/Tests/RunCMake/CXXModules/examples/import-std/main.cxx b/Tests/RunCMake/CXXModules/examples/import-std/main.cxx new file mode 100644 index 0000000..6dafd01 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-std/main.cxx
@@ -0,0 +1,10 @@ +import std; + +int main(int argc, char* argv[]) +{ + if (argc > 0 && argv[0]) { + std::string argv0 = argv[0]; + std::cout << "program: " << argv0 << std::endl; + } + return 0; +}
diff --git a/Tests/RunCMake/CXXModules/examples/vs-without-flags/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/vs-without-flags/CMakeLists.txt index 0d18a66..c5958df 100644 --- a/Tests/RunCMake/CXXModules/examples/vs-without-flags/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/vs-without-flags/CMakeLists.txt
@@ -3,7 +3,7 @@ include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") -set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_SCAN_FOR_MODULES ON)
diff --git a/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake b/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake index 2fb159e..1dcf0ef 100644 --- a/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake
@@ -12,13 +12,8 @@ run_cmake(cmp0069-is-old) if(_CMAKE_C_IPO_SUPPORTED_BY_CMAKE - AND _CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER - AND NOT RunCMake_GENERATOR MATCHES "^Visual Studio 9 ") + AND _CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER) run_cmake(CMP0138-WARN) run_cmake(CMP0138-OLD) run_cmake(CMP0138-NEW) endif() - -if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ") - run_cmake(not-supported-by-generator) -endif()
diff --git a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-result.txt b/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-result.txt +++ /dev/null
@@ -1 +0,0 @@ -1
diff --git a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-stderr.txt b/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-stderr.txt deleted file mode 100644 index a2aa58c..0000000 --- a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-stderr.txt +++ /dev/null
@@ -1,6 +0,0 @@ -^CMake Error at .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(message\): - IPO is not supported \(CMake doesn't support IPO for current generator\)\. -Call Stack \(most recent call first\): - .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(_ipo_not_supported\) - not-supported-by-generator\.cmake:[0-9]+ \(check_ipo_supported\) - CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator.cmake b/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator.cmake deleted file mode 100644 index c32af6e..0000000 --- a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator.cmake +++ /dev/null
@@ -1,6 +0,0 @@ -project(${RunCMake_TEST} LANGUAGES C) - -set(_CMAKE_C_IPO_SUPPORTED_BY_CMAKE YES) -set(_CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES) - -check_ipo_supported()
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/CommandLine/C_target-commands-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/CommandLine/C_target-commands-result.txt
diff --git a/Tests/RunCMake/CommandLine/C_target-commands-stderr.txt b/Tests/RunCMake/CommandLine/C_target-commands-stderr.txt new file mode 100644 index 0000000..8e811a6 --- /dev/null +++ b/Tests/RunCMake/CommandLine/C_target-commands-stderr.txt
@@ -0,0 +1,44 @@ +^CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_sources\): + Cannot specify sources for target "DoesNotExist" which is not built by this + project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_compile_definitions\): + Cannot specify compile definitions for target "DoesNotExist" which is not + built by this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_compile_features\): + Cannot specify compile features for target "DoesNotExist" which is not + built by this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_compile_options\): + Cannot specify compile options for target "DoesNotExist" which is not built + by this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_include_directories\): + Cannot specify include directories for target "DoesNotExist" which is not + built by this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_precompile_headers\): + Cannot specify precompile headers for target "DoesNotExist" which is not + built by this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_link_directories\): + Cannot specify link directories for target "DoesNotExist" which is not + built by this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_link_options\): + Cannot specify link options for target "DoesNotExist" which is not built by + this project\. ++ +CMake Error at [^ +]*C_target-commands_initial-cache\.cmake:[0-9]+ \(target_link_libraries\): + Cannot specify link libraries for target "DoesNotExist" which is not built + by this project\.$
diff --git a/Tests/RunCMake/CommandLine/C_target-commands-stdout.txt b/Tests/RunCMake/CommandLine/C_target-commands-stdout.txt new file mode 100644 index 0000000..beaa2c6 --- /dev/null +++ b/Tests/RunCMake/CommandLine/C_target-commands-stdout.txt
@@ -0,0 +1 @@ +loading initial cache file \.\./C_target-commands_initial-cache\.cmake
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/C_target-commands.cmake similarity index 100% rename from Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake rename to Tests/RunCMake/CommandLine/C_target-commands.cmake
diff --git a/Tests/RunCMake/CommandLine/C_target-commands_initial-cache.cmake b/Tests/RunCMake/CommandLine/C_target-commands_initial-cache.cmake new file mode 100644 index 0000000..a453334 --- /dev/null +++ b/Tests/RunCMake/CommandLine/C_target-commands_initial-cache.cmake
@@ -0,0 +1,9 @@ +target_sources(DoesNotExist PRIVATE) +target_compile_definitions(DoesNotExist PRIVATE) +target_compile_features(DoesNotExist PRIVATE) +target_compile_options(DoesNotExist PRIVATE) +target_include_directories(DoesNotExist PRIVATE) +target_precompile_headers(DoesNotExist PRIVATE) +target_link_directories(DoesNotExist PRIVATE) +target_link_options(DoesNotExist PRIVATE) +target_link_libraries(DoesNotExist PRIVATE)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt deleted file mode 100644 index c3329a0..0000000 --- a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt +++ /dev/null
@@ -1,5 +0,0 @@ -^CMake Warning: - The "Visual Studio 9 2008" generator is deprecated and will be removed in a - future version of CMake. - - Add CMAKE_WARN_VS9=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake deleted file mode 100644 index e69de29..0000000 --- a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake +++ /dev/null
diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index 2bbe1c8..0c2a951 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":7}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$ +^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":7}]},{"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":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
diff --git a/Tests/RunCMake/CommandLine/Envgen-A-platform-stderr-vs9.txt b/Tests/RunCMake/CommandLine/Envgen-A-platform-stderr-vs9.txt deleted file mode 100644 index 4eae6aa..0000000 --- a/Tests/RunCMake/CommandLine/Envgen-A-platform-stderr-vs9.txt +++ /dev/null
@@ -1,2 +0,0 @@ -^CMake Error at CMakeLists.+ - No CMAKE_C_COMPILER could be found.
diff --git a/Tests/RunCMake/CommandLine/Envgen-platform-invalid-stderr-vs9.txt b/Tests/RunCMake/CommandLine/Envgen-platform-invalid-stderr-vs9.txt deleted file mode 100644 index 4eae6aa..0000000 --- a/Tests/RunCMake/CommandLine/Envgen-platform-invalid-stderr-vs9.txt +++ /dev/null
@@ -1,2 +0,0 @@ -^CMake Error at CMakeLists.+ - No CMAKE_C_COMPILER could be found.
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 7b34773..7a5a334 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -393,16 +393,10 @@ # Envvar shouldn't affect existing build tree run_cmake_command(Envgen-platform-existing ${CMAKE_COMMAND} -E chdir .. ${CMAKE_COMMAND} --build Envgen-build) - if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ") - set(RunCMake-stderr-file "Envgen-platform-invalid-stderr-vs9.txt") - endif() run_cmake_command(Envgen-platform-invalid ${CMAKE_COMMAND} ${source_dir}) unset(RunCMake-stderr-file) # Command line -G implies -A"" run_cmake_command(Envgen-G-implicit-platform ${CMAKE_COMMAND} -G "${RunCMake_GENERATOR}" ${source_dir}) - if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ") - set(RunCMake-stderr-file "Envgen-A-platform-stderr-vs9.txt") - endif() run_cmake_command(Envgen-A-platform ${CMAKE_COMMAND} -A "fromcli" ${source_dir}) unset(RunCMake-stderr-file) unset(ENV{CMAKE_GENERATOR_PLATFORM}) @@ -903,6 +897,8 @@ file(COPY ${RunCMake_SOURCE_DIR}/C_basic_initial-cache.txt DESTINATION ${RunCMake_BINARY_DIR}) run_cmake_with_options(C_basic -C ../C_basic_initial-cache.txt) run_cmake_with_options(C_basic_fullpath -C ${RunCMake_BINARY_DIR}/C_basic_initial-cache.txt) +file(COPY ${RunCMake_SOURCE_DIR}/C_target-commands_initial-cache.cmake DESTINATION ${RunCMake_BINARY_DIR}) +run_cmake_command(C_target-commands ${CMAKE_COMMAND} -S ${RunCMake_SOURCE_DIR} -DRunCMake_TEST=C_target-commands -C ../C_target-commands_initial-cache.cmake) set(RunCMake_TEST_OPTIONS "-DFOO=-DBAR:BOOL=BAZ") @@ -1124,13 +1120,6 @@ run_cmake(ProfilingTest) unset(RunCMake_TEST_OPTIONS) -if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 2008") - run_cmake_with_options(DeprecateVS9-WARN-ON -DCMAKE_WARN_VS9=ON) - unset(ENV{CMAKE_WARN_VS9}) - run_cmake(DeprecateVS9-WARN-ON) - run_cmake_with_options(DeprecateVS9-WARN-OFF -DCMAKE_WARN_VS9=OFF) -endif() - if(RunCMake_GENERATOR MATCHES "^Visual Studio 12 2013") run_cmake_with_options(DeprecateVS12-WARN-ON -DCMAKE_WARN_VS12=ON) unset(ENV{CMAKE_WARN_VS12})
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt index 320c2ba..4b3774f 100644 --- a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt +++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
@@ -1,3 +1,14 @@ +^CMake Deprecation Warning at CMP0128WarnMatch-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0128 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ CMake Warning \(dev\) 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
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt index 068cba9..37a0767 100644 --- a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt +++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
@@ -1,3 +1,14 @@ +^CMake Deprecation Warning at CMP0128WarnUnset-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0128 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ CMake Warning \(dev\) 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
diff --git a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake index 731a303..487284b 100644 --- a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake +++ b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
@@ -53,6 +53,7 @@ set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test}-build") run_cmake(${test}) set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . ${ARGN}) endfunction()
diff --git a/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake b/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake index f3360c7..1cfa27e 100644 --- a/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake +++ b/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake
@@ -40,6 +40,6 @@ endforeach() endmacro() -info(C 90 99 11 17 23) -info(CXX 98 11 14 17 20 23) +info(C 90 99 11 17 23 26) +info(CXX 98 11 14 17 20 23 26) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/Configure/RunCMakeTest.cmake b/Tests/RunCMake/Configure/RunCMakeTest.cmake index 842a005..00d3272 100644 --- a/Tests/RunCMake/Configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/Configure/RunCMakeTest.cmake
@@ -4,51 +4,50 @@ run_cmake(CopyFileABI) run_cmake(CustomTargetAfterError) -# Use a single build tree for a few tests without cleaning. -set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RerunCMake-build) -set(RunCMake_TEST_NO_CLEAN 1) -file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") -file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") -set(input "${RunCMake_TEST_BINARY_DIR}/CustomCMakeInput.txt") -set(stamp "${RunCMake_TEST_BINARY_DIR}/CustomCMakeStamp.txt") -set(depend "${RunCMake_TEST_BINARY_DIR}/CustomCMakeDepend.txt") -set(output "${RunCMake_TEST_BINARY_DIR}/CustomCMakeOutput.txt") -set(error "${RunCMake_TEST_BINARY_DIR}/CustomCMakeError.txt") -file(WRITE "${input}" "1") -file(WRITE "${depend}" "1") -run_cmake(RerunCMake) -execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) # handle 1s resolution -file(WRITE "${input}" "2") -run_cmake_command(RerunCMake-build1 ${CMAKE_COMMAND} --build .) -file(WRITE "${depend}" "2") -run_cmake_command(RerunCMake-build2 ${CMAKE_COMMAND} --build .) -execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) # handle 1s resolution -file(WRITE "${depend}" "3") -file(WRITE "${error}" "3") -set(RunCMake_TEST_OUTPUT_MERGE 1) -run_cmake_command(RerunCMake-build3 ${CMAKE_COMMAND} --build .) -if(MSVC_IDE) - # Make sure that for Visual Studio the error occurs from within the build - # system. - file(REMOVE "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/generate.stamp.list") - file(WRITE "${error}" "4") - # With Visual Studio the error must be on stdout, otherwise the error was not - # emitted by ZERO_CHECK. - set(RunCMake_TEST_OUTPUT_MERGE 0) - run_cmake_command(RerunCMake-build4 ${CMAKE_COMMAND} --build .) -endif() -unset(RunCMake_TEST_OUTPUT_MERGE) -unset(RunCMake_TEST_BINARY_DIR) -unset(RunCMake_TEST_NO_CLEAN) +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RerunCMake-build) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + set(input "${RunCMake_TEST_BINARY_DIR}/CustomCMakeInput.txt") + set(stamp "${RunCMake_TEST_BINARY_DIR}/CustomCMakeStamp.txt") + set(depend "${RunCMake_TEST_BINARY_DIR}/CustomCMakeDepend.txt") + set(output "${RunCMake_TEST_BINARY_DIR}/CustomCMakeOutput.txt") + set(error "${RunCMake_TEST_BINARY_DIR}/CustomCMakeError.txt") + file(WRITE "${input}" "1") + file(WRITE "${depend}" "1") + run_cmake(RerunCMake) + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) # handle 1s resolution + file(WRITE "${input}" "2") + run_cmake_command(RerunCMake-build1 ${CMAKE_COMMAND} --build .) + file(WRITE "${depend}" "2") + run_cmake_command(RerunCMake-build2 ${CMAKE_COMMAND} --build .) + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) # handle 1s resolution + file(WRITE "${depend}" "3") + file(WRITE "${error}" "3") + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(RerunCMake-build3 ${CMAKE_COMMAND} --build .) + if(MSVC_IDE) + # Make sure that for Visual Studio the error occurs from within the build + # system. + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/generate.stamp.list") + file(WRITE "${error}" "4") + # With Visual Studio the error must be on stdout, otherwise the error was not + # emitted by ZERO_CHECK. + set(RunCMake_TEST_OUTPUT_MERGE 0) + run_cmake_command(RerunCMake-build4 ${CMAKE_COMMAND} --build .) + endif() +endblock() -# Use a single build tree for a few tests without cleaning. -set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RemoveCache-build) -set(RunCMake_TEST_NO_CLEAN 1) -file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") -file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") -run_cmake(RemoveCache) -file(REMOVE "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") -run_cmake(RemoveCache) +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RemoveCache-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-step1") + run_cmake(RemoveCache) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-step2") + run_cmake(RemoveCache) +endblock() if(NOT RunCMake_GENERATOR MATCHES "^Ninja Multi-Config$") run_cmake(NoCMAKE_CROSS_CONFIGS)
diff --git a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt index 2fc7d29..72c4b81 100644 --- a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Error at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\): +^CMake Error at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): No download info given for 'MyProj' and its source directory: .*/Tests/RunCMake/ExternalProject/NoOptions-build/MyProj-prefix/src/MyProj
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake index ffaa46c..98e3996 100644 --- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -18,6 +18,7 @@ run_cmake(NoOptions) run_cmake(SourceEmpty) run_cmake(SourceMissing) +run_cmake(SourceDirExisting) run_cmake(CMAKE_CACHE_ARGS) run_cmake(CMAKE_CACHE_DEFAULT_ARGS) run_cmake(CMAKE_CACHE_mix) @@ -210,7 +211,7 @@ file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-configure.cmake" [[message(FATAL_ERROR "once: configure should not run again")]]) file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-build.cmake" [[message(FATAL_ERROR "once: build should not run again")]]) file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-install.cmake" [[message(FATAL_ERROR "once: install should not run again")]]) - if(NOT RunCMake_GENERATOR MATCHES "^(Xcode|Visual Studio 9 )") + if(NOT RunCMake_GENERATOR STREQUAL "Xcode") # The Xcode and VS 9 build systems decide to run this every time. file(WRITE "${RunCMake_TEST_BINARY_DIR}/always-configure.cmake" [[message(FATAL_ERROR "always: configure should not run again")]]) endif() @@ -244,9 +245,7 @@ run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-rebuild ${CMAKE_COMMAND} --build . ${BUILD_CONFIG}) endfunction() -if(NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 ") - __ep_test_CONFIGURE_HANDLED_BY_BUILD() -endif() +__ep_test_CONFIGURE_HANDLED_BY_BUILD() find_package(Git QUIET) if(GIT_EXECUTABLE) @@ -254,5 +253,9 @@ # on some platforms. It may go to stdout or stderr, so force it to be merged. set(RunCMake_TEST_OUTPUT_MERGE TRUE) run_cmake(FetchGitRefs) - set(RunCMake_TEST_OUTPUT_MERGE FALSE) + unset(RunCMake_TEST_OUTPUT_MERGE) + + run_cmake(TLSVersionBadArg) + run_cmake(TLSVersionBadVar) + run_cmake(TLSVersionBadEnv) endif()
diff --git a/Tests/RunCMake/ExternalProject/SourceDirExisting.cmake b/Tests/RunCMake/ExternalProject/SourceDirExisting.cmake new file mode 100644 index 0000000..761bb8e --- /dev/null +++ b/Tests/RunCMake/ExternalProject/SourceDirExisting.cmake
@@ -0,0 +1,16 @@ +# We're providing a pre-existing source directory. Make sure we don't trigger +# an error if the undocumented but used-in-the-wild CMAKE_DISABLE_SOURCE_CHANGES +# variable is set. +set(CMAKE_DISABLE_SOURCE_CHANGES TRUE) + +include(ExternalProject) + +ExternalProject_Add(source_dir_existing + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/Foo" + DOWNLOAD_COMMAND "${CMAKE_COMMAND}" -E echo "Download command executed" + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + TEST_COMMAND "" + INSTALL_COMMAND "" +)
diff --git a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt index 07c6e87..8789dde 100644 --- a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt +++ b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Error at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\): +^CMake Error at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): No download info given for 'MyProj' and its source directory: .*/Tests/RunCMake/ExternalProject/SourceEmpty-build/SourceEmpty
diff --git a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt index 373f6e3..9ff2681 100644 --- a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt +++ b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt
@@ -1,4 +1,4 @@ -^CMake Error at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\): +^CMake Error at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): No download info given for 'MyProj' and its source directory: .*/Tests/RunCMake/ExternalProject/SourceMissing-build/SourceMissing
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadArg-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/ExternalProject/TLSVersionBadArg-result.txt
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt new file mode 100644 index 0000000..f0973ba --- /dev/null +++ b/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt
@@ -0,0 +1,10 @@ +^CMake Error at [^ +]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): + TLS_VERSION 'bad-arg' not known +Call Stack \(most recent call first\): + [^ +]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(_ep_get_tls_version\) + [^ +]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_add_download_command\) + TLSVersionBadArg\.cmake:[0-9]+ \(ExternalProject_Add\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadArg.cmake b/Tests/RunCMake/ExternalProject/TLSVersionBadArg.cmake new file mode 100644 index 0000000..d212982 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/TLSVersionBadArg.cmake
@@ -0,0 +1,4 @@ +include(ExternalProject) +set(ENV{CMAKE_TLS_VERSION} bad-env) +set(CMAKE_TLS_VERSION bad-var) +ExternalProject_Add(MyProj GIT_REPOSITORY "fake" TLS_VERSION bad-arg)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/ExternalProject/TLSVersionBadEnv-result.txt
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt new file mode 100644 index 0000000..4069159 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt
@@ -0,0 +1,10 @@ +^CMake Error at [^ +]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): + ENV{CMAKE_TLS_VERSION} 'bad-env' not known +Call Stack \(most recent call first\): + [^ +]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(_ep_get_tls_version\) + [^ +]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_add_download_command\) + TLSVersionBadEnv\.cmake:[0-9]+ \(ExternalProject_Add\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadEnv.cmake b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv.cmake new file mode 100644 index 0000000..8018642 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv.cmake
@@ -0,0 +1,3 @@ +include(ExternalProject) +set(ENV{CMAKE_TLS_VERSION} bad-env) +ExternalProject_Add(MyProj GIT_REPOSITORY "fake")
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadVar-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/ExternalProject/TLSVersionBadVar-result.txt
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt new file mode 100644 index 0000000..a5f7d64 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt
@@ -0,0 +1,10 @@ +^CMake Error at [^ +]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\): + CMAKE_TLS_VERSION 'bad-var' not known +Call Stack \(most recent call first\): + [^ +]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(_ep_get_tls_version\) + [^ +]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_add_download_command\) + TLSVersionBadVar\.cmake:[0-9]+ \(ExternalProject_Add\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadVar.cmake b/Tests/RunCMake/ExternalProject/TLSVersionBadVar.cmake new file mode 100644 index 0000000..f52dd2e --- /dev/null +++ b/Tests/RunCMake/ExternalProject/TLSVersionBadVar.cmake
@@ -0,0 +1,4 @@ +include(ExternalProject) +set(ENV{CMAKE_TLS_VERSION} bad-env) +set(CMAKE_TLS_VERSION bad-var) +ExternalProject_Add(MyProj GIT_REPOSITORY "fake")
diff --git a/Tests/RunCMake/FPHSA/BadFoundVar-stderr.txt b/Tests/RunCMake/FPHSA/BadFoundVar-stderr.txt index 4c739d8..3ae1ba7 100644 --- a/Tests/RunCMake/FPHSA/BadFoundVar-stderr.txt +++ b/Tests/RunCMake/FPHSA/BadFoundVar-stderr.txt
@@ -2,6 +2,6 @@ The argument for FOUND_VAR is "badfoundvar_FOUND", but only "BadFoundVar_FOUND" and "BADFOUNDVAR_FOUND" are valid names. Call Stack \(most recent call first\): - FindBadFoundVar.cmake:5 \(find_package_handle_standard_args\) - BadFoundVar.cmake:3 \(find_package\) - CMakeLists.txt:3 \(include\) + FindBadFoundVar.cmake:[0-9]+ \(find_package_handle_standard_args\) + BadFoundVar.cmake:[0-9]+ \(find_package\) + CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FPHSA/BeforeProject-Error-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/FPHSA/BeforeProject-Error-result.txt
diff --git a/Tests/RunCMake/FPHSA/BeforeProject-Error-stderr.txt b/Tests/RunCMake/FPHSA/BeforeProject-Error-stderr.txt new file mode 100644 index 0000000..c8e53fc --- /dev/null +++ b/Tests/RunCMake/FPHSA/BeforeProject-Error-stderr.txt
@@ -0,0 +1,12 @@ +^CMake Error at [^ +]*/Modules/FindPackageHandleStandardArgs\.cmake:[0-9]+ \(message\): + Could NOT find BeforeProject \(missing: SOME_VAR\) + + Hint: The project\(\) command has not yet been called\. It sets up + system-specific search paths\. +Call Stack \(most recent call first\): + [^ +]*/Modules/FindPackageHandleStandardArgs\.cmake:[0-9]+ \(_FPHSA_FAILURE_MESSAGE\) + FindBeforeProject\.cmake:[0-9]+ \(find_package_handle_standard_args\) + BeforeProject-Error\.cmake:[0-9]+ \(find_package\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/FPHSA/BeforeProject-Error.cmake b/Tests/RunCMake/FPHSA/BeforeProject-Error.cmake new file mode 100644 index 0000000..53a8073 --- /dev/null +++ b/Tests/RunCMake/FPHSA/BeforeProject-Error.cmake
@@ -0,0 +1,2 @@ +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") +find_package(BeforeProject REQUIRED)
diff --git a/Tests/RunCMake/FPHSA/BeforeProject-Missing-stdout.txt b/Tests/RunCMake/FPHSA/BeforeProject-Missing-stdout.txt new file mode 100644 index 0000000..05df0b0 --- /dev/null +++ b/Tests/RunCMake/FPHSA/BeforeProject-Missing-stdout.txt
@@ -0,0 +1,2 @@ +-- Could NOT find BeforeProject \(missing: SOME_VAR\)[ ]* +Hint: The project\(\) command has not yet been called\. It sets up system-specific search paths\.
diff --git a/Tests/RunCMake/FPHSA/BeforeProject-Missing.cmake b/Tests/RunCMake/FPHSA/BeforeProject-Missing.cmake new file mode 100644 index 0000000..8d44ca9 --- /dev/null +++ b/Tests/RunCMake/FPHSA/BeforeProject-Missing.cmake
@@ -0,0 +1,2 @@ +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") +find_package(BeforeProject)
diff --git a/Tests/RunCMake/FPHSA/CMakeLists.txt b/Tests/RunCMake/FPHSA/CMakeLists.txt index 93ee9df..dc34259 100644 --- a/Tests/RunCMake/FPHSA/CMakeLists.txt +++ b/Tests/RunCMake/FPHSA/CMakeLists.txt
@@ -1,3 +1,8 @@ cmake_minimum_required(VERSION 3.5) +if(RunCMake_TEST MATCHES "^BeforeProject") + include(${RunCMake_TEST}.cmake) + project(${RunCMake_TEST} NONE) + return() +endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FPHSA/FindBeforeProject.cmake b/Tests/RunCMake/FPHSA/FindBeforeProject.cmake new file mode 100644 index 0000000..6bf49f1 --- /dev/null +++ b/Tests/RunCMake/FPHSA/FindBeforeProject.cmake
@@ -0,0 +1,3 @@ +set(SOME_VAR FALSE) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BeforeProject REQUIRED_VARS SOME_VAR)
diff --git a/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt b/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt index 722b50b..8ee6ec1 100644 --- a/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt +++ b/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt
@@ -5,9 +5,9 @@ `find_package` result variables \(e.g., `_FOUND`\) to follow a certain pattern. Call Stack \(most recent call first\): - FindNameMismatch.cmake:3 \(find_package_handle_standard_args\) - NameMismatch.cmake:3 \(find_package\) - CMakeLists.txt:3 \(include\) + 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. CMake Warning \(dev\) at .*/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\): @@ -17,7 +17,7 @@ `find_package` result variables \(e.g., `_FOUND`\) to follow a certain pattern. Call Stack \(most recent call first\): - FindNameMismatchOld.cmake:3 \(find_package_handle_standard_args\) - NameMismatch.cmake:4 \(find_package\) - CMakeLists.txt:3 \(include\) + 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.
diff --git a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake index 3b095a6..be9b127 100644 --- a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake +++ b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
@@ -1,6 +1,8 @@ include(RunCMake) run_cmake(BadFoundVar) +run_cmake(BeforeProject-Error) +run_cmake(BeforeProject-Missing) run_cmake(NameMismatch) # The pseudo module will "find" a package with the given version. Check if the
diff --git a/Tests/RunCMake/FetchContent/CMakeLists.txt b/Tests/RunCMake/FetchContent/CMakeLists.txt index 3cc2e43..4f2de6c 100644 --- a/Tests/RunCMake/FetchContent/CMakeLists.txt +++ b/Tests/RunCMake/FetchContent/CMakeLists.txt
@@ -1,7 +1,9 @@ cmake_minimum_required(VERSION 3.9) project(${RunCMake_TEST} NONE) -# Tests assume no previous downloads in the output directory -file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/_deps) - +if(CMP0168 STREQUAL "NEW") + cmake_policy(SET CMP0168 NEW) +else() + cmake_policy(SET CMP0168 OLD) +endif() include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FetchContent/DirOverrides.cmake b/Tests/RunCMake/FetchContent/DirOverrides.cmake index ad61a00..db407cf 100644 --- a/Tests/RunCMake/FetchContent/DirOverrides.cmake +++ b/Tests/RunCMake/FetchContent/DirOverrides.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # Test using saved details
diff --git a/Tests/RunCMake/FetchContent/DirOverridesDisconnected.cmake b/Tests/RunCMake/FetchContent/DirOverridesDisconnected.cmake index 768a82e..2e9d7ed 100644 --- a/Tests/RunCMake/FetchContent/DirOverridesDisconnected.cmake +++ b/Tests/RunCMake/FetchContent/DirOverridesDisconnected.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # Test using saved details. We are re-using a SOURCE_DIR from a previous test
diff --git a/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake b/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake index 0731b43..1728d11 100644 --- a/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake +++ b/Tests/RunCMake/FetchContent/DirectIgnoresDetails.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) FetchContent_Declare(
diff --git a/Tests/RunCMake/FetchContent/DisableSourceChanges.cmake b/Tests/RunCMake/FetchContent/DisableSourceChanges.cmake new file mode 100644 index 0000000..a144005 --- /dev/null +++ b/Tests/RunCMake/FetchContent/DisableSourceChanges.cmake
@@ -0,0 +1,18 @@ +cmake_policy(SET CMP0168 NEW) + +# Undocumented variable used to catch attempts to write to anywhere under the +# source directory that isn't under the build directory. In order for this +# code path to be checked for direct population mode, we need a non-empty +# download, update, or patch command so that the population code path is used. +# Custom commands might not write to the source directory and instead just +# print messages or other non-modifying tasks, like is done here. +set(CMAKE_DISABLE_SOURCE_CHANGES TRUE) + +include(FetchContent) + +FetchContent_Declare( + WithProject + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/WithProject # This exists + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed" +) +FetchContent_MakeAvailable(WithProject)
diff --git a/Tests/RunCMake/FetchContent/DownloadFile.cmake b/Tests/RunCMake/FetchContent/DownloadFile.cmake index 741b6d3..b55daae 100644 --- a/Tests/RunCMake/FetchContent/DownloadFile.cmake +++ b/Tests/RunCMake/FetchContent/DownloadFile.cmake
@@ -1,8 +1,14 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) +# The file hash depends on the line endings used by git +file(MD5 ${CMAKE_CURRENT_LIST_DIR}/dummyFile.txt md5_hash) + FetchContent_Declare( t1 URL ${CMAKE_CURRENT_LIST_DIR}/dummyFile.txt + URL_HASH MD5=${md5_hash} DOWNLOAD_NO_EXTRACT YES )
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FetchContent/DownloadTwice-direct-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/FetchContent/DownloadTwice-direct-result.txt
diff --git a/Tests/RunCMake/FetchContent/DownloadTwice-direct-stderr.txt b/Tests/RunCMake/FetchContent/DownloadTwice-direct-stderr.txt new file mode 100644 index 0000000..e793902 --- /dev/null +++ b/Tests/RunCMake/FetchContent/DownloadTwice-direct-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): + Content t1 already populated in
diff --git a/Tests/RunCMake/FetchContent/DownloadTwice.cmake b/Tests/RunCMake/FetchContent/DownloadTwice.cmake index 6863c30..2fa08c4 100644 --- a/Tests/RunCMake/FetchContent/DownloadTwice.cmake +++ b/Tests/RunCMake/FetchContent/DownloadTwice.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) FetchContent_Declare(
diff --git a/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake b/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake index 208b12d..723d63e 100644 --- a/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake +++ b/Tests/RunCMake/FetchContent/FirstDetailsWin.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # Need to see the download command output
diff --git a/Tests/RunCMake/FetchContent/GetProperties.cmake b/Tests/RunCMake/FetchContent/GetProperties.cmake index 61c99fe..a51d6ce 100644 --- a/Tests/RunCMake/FetchContent/GetProperties.cmake +++ b/Tests/RunCMake/FetchContent/GetProperties.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # First confirm properties are empty even before declare
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-result.txt
diff --git a/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-stderr.txt b/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-stderr.txt new file mode 100644 index 0000000..9c3fc27 --- /dev/null +++ b/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\): + No content details recorded for NoDetails
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-result.txt
diff --git a/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-stderr.txt b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-stderr.txt new file mode 100644 index 0000000..7ecb06b --- /dev/null +++ b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-stderr.txt
@@ -0,0 +1,2 @@ + *Manually specified source directory is missing: ++ *FETCHCONTENT_SOURCE_DIR_WITHPROJECT --> .*/ADirThatDoesNotExist
diff --git a/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake index 0e24c1a..82e5c46 100644 --- a/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake +++ b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake
@@ -1,3 +1,4 @@ +message(STATUS "FETCHCONTENT_SOURCE_DIR_WITHPROJECT = ${FETCHCONTENT_SOURCE_DIR_WITHPROJECT}") include(FetchContent) FetchContent_Declare(
diff --git a/Tests/RunCMake/FetchContent/ManualSourceDirectoryRelative-direct-stderr.txt b/Tests/RunCMake/FetchContent/ManualSourceDirectoryRelative-direct-stderr.txt new file mode 100644 index 0000000..3defcb4 --- /dev/null +++ b/Tests/RunCMake/FetchContent/ManualSourceDirectoryRelative-direct-stderr.txt
@@ -0,0 +1,3 @@ + *Relative source directory specified. This is not safe, as it depends on + *the calling directory scope. ++ *FETCHCONTENT_SOURCE_DIR_WITHPROJECT --> WithProject
diff --git a/Tests/RunCMake/FetchContent/MissingDetails.cmake b/Tests/RunCMake/FetchContent/MissingDetails.cmake index ba8d121..998f251 100644 --- a/Tests/RunCMake/FetchContent/MissingDetails.cmake +++ b/Tests/RunCMake/FetchContent/MissingDetails.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) FetchContent_Populate(t1)
diff --git a/Tests/RunCMake/FetchContent/PreserveEmptyArgs.cmake b/Tests/RunCMake/FetchContent/PreserveEmptyArgs.cmake index 4f35448..6da6c42 100644 --- a/Tests/RunCMake/FetchContent/PreserveEmptyArgs.cmake +++ b/Tests/RunCMake/FetchContent/PreserveEmptyArgs.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # Need to see the download command output
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake index 0f443a7..9eb40b9 100644 --- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake +++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -2,81 +2,116 @@ unset(RunCMake_TEST_NO_CLEAN) -run_cmake(MissingDetails) -run_cmake(DirectIgnoresDetails) -run_cmake(FirstDetailsWin) -run_cmake(DownloadTwice) -run_cmake(DownloadFile) -run_cmake(IgnoreToolchainFile) -run_cmake(SameGenerator) -run_cmake(System) -run_cmake(VarDefinitions) -run_cmake(VarPassthroughs) -run_cmake(GetProperties) -run_cmake(UsesTerminalOverride) -run_cmake(MakeAvailable) -run_cmake(MakeAvailableTwice) -run_cmake(MakeAvailableUndeclared) -run_cmake(VerifyHeaderSet) +function(run_cmake_with_cmp0168 name) + run_cmake_with_options("${name}" -D CMP0168=OLD ${ARGN}) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-direct") + run_cmake_with_options("${name}" -D CMP0168=NEW ${ARGN}) +endfunction() -run_cmake_with_options(FindDependencyExport +# Won't get to the part where CMP0168 matters +run_cmake_with_options(MissingDetails -D CMP0168=NEW) + +# These are testing specific aspects of the sub-build +run_cmake_with_options(SameGenerator -D CMP0168=OLD) +run_cmake_with_options(VarPassthroughs -D CMP0168=OLD) + +run_cmake_with_cmp0168(DirectIgnoresDetails) +run_cmake_with_cmp0168(FirstDetailsWin) +block(SCOPE_FOR VARIABLES) + # Reuse this test to also verify that "cmake --fresh" re-executes the steps + # when using the direct mode + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/FirstDetailsWin-direct-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-direct-fresh") + run_cmake_with_options(FirstDetailsWin -D CMP0168=NEW --fresh) +endblock() +run_cmake_with_cmp0168(DownloadTwice) +run_cmake_with_cmp0168(DownloadFile) +run_cmake_with_cmp0168(IgnoreToolchainFile) +run_cmake_with_cmp0168(System) +run_cmake_with_cmp0168(VarDefinitions) +run_cmake_with_cmp0168(GetProperties) +run_cmake_with_cmp0168(UsesTerminalOverride) +run_cmake_with_cmp0168(MakeAvailable) +run_cmake_with_cmp0168(MakeAvailableTwice) +run_cmake_with_cmp0168(MakeAvailableUndeclared) +run_cmake_with_cmp0168(VerifyHeaderSet) + +run_cmake_with_cmp0168(FindDependencyExport -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${CMAKE_CURRENT_LIST_DIR}/FindDependencyExportDP.cmake" ) -run_cmake_with_options(ManualSourceDirectory +run_cmake_with_cmp0168(ManualSourceDirectory -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT=${CMAKE_CURRENT_LIST_DIR}/WithProject" ) -run_cmake_with_options(ManualSourceDirectoryMissing +run_cmake_with_cmp0168(ManualSourceDirectoryMissing -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT=${CMAKE_CURRENT_LIST_DIR}/ADirThatDoesNotExist" ) # Need to use :STRING to prevent CMake from automatically converting it to an # absolute path -run_cmake_with_options(ManualSourceDirectoryRelative +run_cmake_with_cmp0168(ManualSourceDirectoryRelative -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT:STRING=WithProject" ) -function(run_FetchContent_DirOverrides) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides-build) +function(run_FetchContent_DirOverrides cmp0168) + if(cmp0168 STREQUAL "NEW") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-direct") + endif() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides${RunCMake_TEST_VARIANT_DESCRIPTION}-build) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - run_cmake(DirOverrides) + run_cmake_with_options(DirOverrides -D CMP0168=${cmp0168}) set(RunCMake_TEST_NO_CLEAN 1) run_cmake_with_options(DirOverridesDisconnected + -D CMP0168=${cmp0168} -D FETCHCONTENT_FULLY_DISCONNECTED=YES ) endfunction() -run_FetchContent_DirOverrides() +run_FetchContent_DirOverrides(OLD) +run_FetchContent_DirOverrides(NEW) set(RunCMake_TEST_OUTPUT_MERGE 1) -run_cmake(PreserveEmptyArgs) +run_cmake_with_cmp0168(PreserveEmptyArgs) set(RunCMake_TEST_OUTPUT_MERGE 0) -# We need to pass through CMAKE_GENERATOR and CMAKE_MAKE_PROGRAM -# to ensure the test can run on machines where the build tool -# isn't on the PATH. Some build slaves explicitly test with such -# an arrangement (e.g. to test with spaces in the path). We also -# pass through the platform and toolset for completeness, even -# though we don't build anything, just in case this somehow affects -# the way the build tool is invoked. -run_cmake_command(ScriptMode - ${CMAKE_COMMAND} - -DCMAKE_GENERATOR=${RunCMake_GENERATOR} - -DCMAKE_GENERATOR_PLATFORM=${RunCMake_GENERATOR_PLATFORM} - -DCMAKE_GENERATOR_TOOLSET=${RunCMake_GENERATOR_TOOLSET} - -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM} - -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake -) - function(run_FetchContent_ExcludeFromAll) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - run_cmake(ExcludeFromAll) + # We're testing FetchContent_MakeAvailable()'s add_subdirectory() behavior, + # so it doesn't matter if we use OLD or NEW for CMP0168, but NEW is faster. + run_cmake(ExcludeFromAll -D CMP0168=NEW) set(RunCMake_TEST_NO_CLEAN 1) run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build .) endfunction() run_FetchContent_ExcludeFromAll() + +# Script mode testing requires more care for CMP0168 set to OLD. +# We need to pass through CMAKE_GENERATOR and CMAKE_MAKE_PROGRAM +# to ensure the test can run on machines where the build tool +# isn't on the PATH. Some build machines explicitly test with such +# an arrangement (e.g. to test with spaces in the path). We also +# pass through the platform and toolset for completeness, even +# though we don't build anything, just in case this somehow affects +# the way the build tool is invoked. +run_cmake_command(ScriptMode + ${CMAKE_COMMAND} + -DCMP0168=OLD + -DCMAKE_GENERATOR=${RunCMake_GENERATOR} + -DCMAKE_GENERATOR_PLATFORM=${RunCMake_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${RunCMake_GENERATOR_TOOLSET} + -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM} + -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake +) +# CMP0168 NEW doesn't need a build tool or generator, so don't set them. +run_cmake_command(ScriptMode-direct + ${CMAKE_COMMAND} + -DCMP0168=NEW + -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake +) + +run_cmake(DisableSourceChanges)
diff --git a/Tests/RunCMake/FetchContent/SameGenerator.cmake b/Tests/RunCMake/FetchContent/SameGenerator.cmake index 58204ef..d4588a0 100644 --- a/Tests/RunCMake/FetchContent/SameGenerator.cmake +++ b/Tests/RunCMake/FetchContent/SameGenerator.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) FetchContent_Declare(
diff --git a/Tests/RunCMake/FetchContent/ScriptMode.cmake b/Tests/RunCMake/FetchContent/ScriptMode.cmake index 0a93d62..41b624a 100644 --- a/Tests/RunCMake/FetchContent/ScriptMode.cmake +++ b/Tests/RunCMake/FetchContent/ScriptMode.cmake
@@ -1,3 +1,6 @@ +cmake_minimum_required(VERSION 3.29) +cmake_policy(SET CMP0168 ${CMP0168}) + include(FetchContent) file(WRITE tmpFile.txt "Generated contents, not important") @@ -11,6 +14,11 @@ if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t1-src/done1.txt) message(FATAL_ERROR "Default SOURCE_DIR doesn't contain done1.txt") endif() +if(CMP0168 STREQUAL "NEW" AND EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t1-subbuild) + message(FATAL_ERROR "t1 sub-build used when expected direct population") +elseif(CMP0168 STREQUAL "OLD" AND NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t1-subbuild) + message(FATAL_ERROR "t1 used direct population when a sub-build was expected") +endif() FetchContent_Populate( t2 @@ -22,6 +30,11 @@ if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/mysrc/done2.txt) message(FATAL_ERROR "Specified SOURCE_DIR doesn't contain done2.txt") endif() +if(CMP0168 STREQUAL "NEW" AND EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t2-subbuild) + message(FATAL_ERROR "t2 sub-build used when expected direct population") +elseif(CMP0168 STREQUAL "OLD" AND NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t2-subbuild) + message(FATAL_ERROR "t2 used direct population when a sub-build was expected") +endif() FetchContent_Populate( t3 @@ -33,3 +46,8 @@ if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/myrelsrc/done3.txt) message(FATAL_ERROR "Relative SOURCE_DIR doesn't contain done3.txt") endif() +if(CMP0168 STREQUAL "NEW" AND EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t3-subbuild) + message(FATAL_ERROR "t3 sub-build used when expected direct population") +elseif(CMP0168 STREQUAL "OLD" AND NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/t3-subbuild) + message(FATAL_ERROR "t3 used direct population when a sub-build was expected") +endif()
diff --git a/Tests/RunCMake/FetchContent/UsesTerminalOverride.cmake b/Tests/RunCMake/FetchContent/UsesTerminalOverride.cmake index 99d9719..14839b4 100644 --- a/Tests/RunCMake/FetchContent/UsesTerminalOverride.cmake +++ b/Tests/RunCMake/FetchContent/UsesTerminalOverride.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) set(FETCHCONTENT_QUIET NO)
diff --git a/Tests/RunCMake/FetchContent/VarDefinitions.cmake b/Tests/RunCMake/FetchContent/VarDefinitions.cmake index 4d2a929..0c304a2 100644 --- a/Tests/RunCMake/FetchContent/VarDefinitions.cmake +++ b/Tests/RunCMake/FetchContent/VarDefinitions.cmake
@@ -3,6 +3,8 @@ unset(FETCHCONTENT_QUIET CACHE) unset(FETCHCONTENT_BASE_DIR CACHE) +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # Each of the cache entries should be defined and have the
diff --git a/Tests/RunCMake/FetchContent/VarPassthroughs.cmake b/Tests/RunCMake/FetchContent/VarPassthroughs.cmake index ad743d8..284f5b8 100644 --- a/Tests/RunCMake/FetchContent/VarPassthroughs.cmake +++ b/Tests/RunCMake/FetchContent/VarPassthroughs.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) set(CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY AAAA) @@ -5,6 +7,7 @@ set(CMAKE_TLS_CAINFO CCCC) set(CMAKE_NETRC DDDD) set(CMAKE_NETRC_FILE EEEE) +set(CMAKE_TLS_VERSION FFFF) FetchContent_Declare(PassThrough DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed" @@ -21,6 +24,10 @@ message(FATAL_ERROR "Missing CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY") endif() +if(NOT contents MATCHES "CMAKE_TLS_VERSION \\[==\\[FFFF\\]==\\]") + message(FATAL_ERROR "Missing CMAKE_TLS_VERSION") +endif() + if(NOT contents MATCHES "CMAKE_TLS_VERIFY \\[==\\[BBBB\\]==\\]") message(FATAL_ERROR "Missing CMAKE_TLS_VERIFY") endif()
diff --git a/Tests/RunCMake/FetchContent_find_package/MissingDetails.cmake b/Tests/RunCMake/FetchContent_find_package/MissingDetails.cmake index ba8d121..998f251 100644 --- a/Tests/RunCMake/FetchContent_find_package/MissingDetails.cmake +++ b/Tests/RunCMake/FetchContent_find_package/MissingDetails.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) FetchContent_Populate(t1)
diff --git a/Tests/RunCMake/FetchContent_find_package/PreserveEmptyArgs.cmake b/Tests/RunCMake/FetchContent_find_package/PreserveEmptyArgs.cmake index 4f35448..6da6c42 100644 --- a/Tests/RunCMake/FetchContent_find_package/PreserveEmptyArgs.cmake +++ b/Tests/RunCMake/FetchContent_find_package/PreserveEmptyArgs.cmake
@@ -1,3 +1,5 @@ +cmake_policy(SET CMP0169 OLD) + include(FetchContent) # Need to see the download command output
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py index 49dfe87..9410c7e 100644 --- a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
@@ -3,7 +3,7 @@ def check_objects(o): assert is_list(o) assert len(o) == 1 - check_index_object(o[0], "cmakeFiles", 1, 0, check_object_cmakeFiles) + check_index_object(o[0], "cmakeFiles", 1, 1, check_object_cmakeFiles) def check_input(actual, expected): assert is_dict(actual) @@ -23,8 +23,27 @@ assert sorted(actual.keys()) == sorted(expected_keys) +def check_glob_dependent(actual, expected): + assert is_dict(actual) + + if "followSymlinks" in expected: + assert is_bool(actual["followSymlinks"], expected["followSymlinks"]) + + if "listDirectories" in expected: + assert is_bool(actual["listDirectories"], expected["listDirectories"]) + + if "recurse" in expected: + assert is_bool(actual["recurse"], expected["recurse"]) + + if "relative" in expected: + assert matches(actual["relative"], expected["relative"]) + + check_list_match(lambda a, e: matches(a, e), actual["paths"], expected["paths"], allow_extra=True) + + assert sorted(actual.keys()) == sorted(expected.keys()) + def check_object_cmakeFiles(o): - assert sorted(o.keys()) == ["inputs", "kind", "paths", "version"] + assert sorted(o.keys()) == ["globsDependent", "inputs", "kind", "paths", "version"] # The "kind" and "version" members are handled by check_index_object. assert is_dict(o["paths"]) assert sorted(o["paths"].keys()) == ["build", "source"] @@ -82,12 +101,33 @@ }, ] + expected_globs = [ + { + "expression": "^.*/Tests/RunCMake/FileAPI/dir/\\*$", + "paths": [ + "^.*/Tests/RunCMake/FileAPI/dir/dir$", + "^.*/Tests/RunCMake/FileAPI/dir/dirtest\\.cmake$" + ], + "listDirectories": True, + }, + { + "expression": "^.*/Tests/RunCMake/FileAPI/dir/\\*\\.cmake$", + "paths": [ + "^dir/dirtest\\.cmake$" + ], + "followSymlinks": True, + "recurse": True, + "relative": "^.*/Tests/RunCMake/FileAPI$" + } + ] + inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"] if inSource: for e in expected: e["path"] = e["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) check_list_match(lambda a, e: matches(a["path"], e["path"]), o["inputs"], expected, check=check_input, allow_extra=True) + check_list_match(lambda a, e: matches(a["expression"], e["expression"]), o["globsDependent"], expected_globs, check=check_glob_dependent, allow_extra=True) assert is_dict(index) assert sorted(index.keys()) == ["cmake", "objects", "reply"]
diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake index 4d4d757..b98a283 100644 --- a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
@@ -5,4 +5,14 @@ include("${CMAKE_CURRENT_BINARY_DIR}/generated.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../FileAPIDummyFile.cmake") +file(GLOB var + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dir/*") + +file(GLOB_RECURSE var + FOLLOW_SYMLINKS + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dir/*.cmake") + add_subdirectory(dir)
diff --git a/Tests/RunCMake/FindBoost/CMakeLists.txt b/Tests/RunCMake/FindBoost/CMakeLists.txt index d3137f6..fe9e3ef 100644 --- a/Tests/RunCMake/FindBoost/CMakeLists.txt +++ b/Tests/RunCMake/FindBoost/CMakeLists.txt
@@ -1,3 +1,4 @@ cmake_minimum_required(VERSION 3.9) +cmake_policy(SET CMP0167 OLD) # This test covers FindBoost project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE-stdout.txt index 5f211eb..670bdda 100644 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE-stdout.txt +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE-stdout.txt
@@ -1 +1,2 @@ --- g_ir_scanner: .*/g-ir-scanner +-- g_ir_scanner: [^ +]*g-ir-scanner
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_MULTIPLE_VALUES.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_MULTIPLE_VALUES.cmake new file mode 100644 index 0000000..19ee908 --- /dev/null +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_MULTIPLE_VALUES.cmake
@@ -0,0 +1,31 @@ +# Prepare environment and variables +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) +if(WIN32) + set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}\\pc-bletch") +else() + set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/pc-bletch") +endif() + +find_package(PkgConfig REQUIRED) +pkg_check_modules(BLETCH QUIET bletch) + +if (NOT BLETCH_FOUND) + message(FATAL_ERROR "Failed to find embedded package bletch via CMAKE_PREFIX_PATH") +endif () + +set(expected_value "item1;item2;item3;item with spaces") +pkg_get_variable(bletchvar1 bletch multiple_values1) +pkg_get_variable(bletchvar2 bletch multiple_values2) + +string(FIND "${bletchvar1}" ";" IS_VARIABLE_A_LIST1) +string(FIND "${bletchvar2}" ";" IS_VARIABLE_A_LIST2) + +if (IS_VARIABLE_A_LIST1 EQUAL -1 OR IS_VARIABLE_A_LIST2 EQUAL -1) + message(FATAL_ERROR "Failed to fetch variable multiple_values from embedded package bletch as a list") +endif() + +if (NOT (bletchvar1 STREQUAL expected_value AND bletchvar2 STREQUAL expected_value)) + message(NOTICE "multiple_values1=${bletchvar1} and expected_value=${expected_value}") + message(NOTICE "multiple_values2=${bletchvar2} and expected_value=${expected_value}") + message(FATAL_ERROR "Failed to fetch variable multiple_values from embedded package bletch with escaped spaces") +endif()
diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake index 7af425a..3466636 100644 --- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
@@ -54,6 +54,7 @@ run_cmake(FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES) run_cmake(FindPkgConfig_GET_VARIABLE_PREFIX_PATH) run_cmake(FindPkgConfig_GET_VARIABLE_PKGCONFIG_PATH) + run_cmake(FindPkgConfig_GET_VARIABLE_MULTIPLE_VALUES) run_cmake(FindPkgConfig_cache_variables) run_cmake(FindPkgConfig_IMPORTED_TARGET) run_cmake(FindPkgConfig_VERSION_OPERATORS)
diff --git a/Tests/RunCMake/FindPkgConfig/pc-bletch/lib/pkgconfig/bletch.pc b/Tests/RunCMake/FindPkgConfig/pc-bletch/lib/pkgconfig/bletch.pc index 04d2c1b..87c3613 100644 --- a/Tests/RunCMake/FindPkgConfig/pc-bletch/lib/pkgconfig/bletch.pc +++ b/Tests/RunCMake/FindPkgConfig/pc-bletch/lib/pkgconfig/bletch.pc
@@ -4,9 +4,11 @@ includedir=${prefix}/include jackpot=bletch-lives +multiple_values1="item1 item2 item3 item\ with\ spaces" +multiple_values2='item1 item2 item3 item\ with\ spaces' Name: Bletch -Description: Dummy packaget to test variable support +Description: Dummy package to test variable support Version: 1.0 Libs: -L${libdir} -lbletch Cflags: -Dbletch_version=1
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake index 2ad45ba..fdad38d 100644 --- a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
@@ -35,3 +35,13 @@ run_cmake(override-features3) run_cmake(override-features4) run_cmake(override-features5) + +# testing feature attributes specification +run_cmake(bad-feature-attributes1) +run_cmake(bad-feature-attributes2) +run_cmake(bad-feature-attributes3) +run_cmake(bad-feature-attributes4) +run_cmake(bad-feature-attributes5) +run_cmake(unsupported-library_type) +run_cmake(cyclic-override) +run_cmake(multiple-override)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1-stderr.txt new file mode 100644 index 0000000..5923654 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error: + Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_ATTRIBUTES': + + BAD_ATTRIBUTE=XXX
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1.cmake new file mode 100644 index 0000000..dc743a8 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes1.cmake
@@ -0,0 +1,10 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature_ATTRIBUTES BAD_ATTRIBUTE=XXX) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2-stderr.txt new file mode 100644 index 0000000..5923654 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error: + Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_ATTRIBUTES': + + BAD_ATTRIBUTE=XXX
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2.cmake new file mode 100644 index 0000000..3012308 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes2.cmake
@@ -0,0 +1,10 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature_ATTRIBUTES LIBRARY_TYPE=STATIC BAD_ATTRIBUTE=XXX DEDUPLICATION=YES) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3-stderr.txt new file mode 100644 index 0000000..a0b0a75 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error: + Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_ATTRIBUTES': + + LIBRARY_TYPE=STATIC,BAD_TYPE
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3.cmake new file mode 100644 index 0000000..8887934 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes3.cmake
@@ -0,0 +1,10 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature_ATTRIBUTES LIBRARY_TYPE=STATIC,BAD_TYPE) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4-stderr.txt new file mode 100644 index 0000000..a0b0a75 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error: + Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_ATTRIBUTES': + + LIBRARY_TYPE=STATIC,BAD_TYPE
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4.cmake new file mode 100644 index 0000000..3edf268 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes4.cmake
@@ -0,0 +1,10 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature_ATTRIBUTES DEDUPLICATION=YES LIBRARY_TYPE=STATIC,BAD_TYPE) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5-stderr.txt new file mode 100644 index 0000000..34c1143 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error: + Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_ATTRIBUTES': + + DEDUPLICATION=YES,NO
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5.cmake new file mode 100644 index 0000000..5af99cd --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-attributes5.cmake
@@ -0,0 +1,10 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature_ATTRIBUTES DEDUPLICATION=YES,NO) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override-stderr.txt new file mode 100644 index 0000000..4476271 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override-stderr.txt
@@ -0,0 +1,6 @@ +CMake Error at cyclic-override.cmake:[0-9]+ \(add_library\): + Impossible to link target 'lib' because the link item 'dep' is specified + with the features 'feature2' and 'feature1', and both have an 'OVERRIDE' + attribute that overrides the other. Such cycles are not allowed. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override.cmake new file mode 100644 index 0000000..69b670b --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/cyclic-override.cmake
@@ -0,0 +1,14 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature1 "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature1_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature1_ATTRIBUTES OVERRIDE=feature2) + +set(CMAKE_LINK_LIBRARY_USING_feature2 "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature2_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature2_ATTRIBUTES OVERRIDE=feature1) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature1,dep>" "$<LINK_LIBRARY:feature2,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt index f9a99af..8f43a7f 100644 --- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
@@ -1,6 +1,6 @@ CMake Warning \(dev\) 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 + '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE_LIBRARY 'front'. Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) @@ -8,7 +8,14 @@ CMake Warning \(dev\) 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'. + '\$<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. + +CMake Warning \(dev\) 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.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake index a888bb8..b5e52b5 100644 --- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
@@ -2,13 +2,14 @@ set(CMAKE_C_LINK_LIBRARY_USING_feat_SUPPORTED TRUE) set(CMAKE_C_LINK_LIBRARY_USING_feat "<LIBRARY>") +set(CMAKE_C_LINK_LIBRARY_feat_ATTRIBUTES "LIBRARY_TYPE=STATIC") add_library(dep OBJECT empty.c) add_library(lib SHARED empty.c) add_library(front INTERFACE) -target_link_libraries(front INTERFACE lib) +target_link_libraries(front INTERFACE "$<LINK_LIBRARY:feat,lib>") add_library(lib2 SHARED empty.c)
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/multiple-override.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/multiple-override.cmake new file mode 100644 index 0000000..97d85d0 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/multiple-override.cmake
@@ -0,0 +1,14 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature1 "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature1_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature1_ATTRIBUTES OVERRIDE=feature2) + +set(CMAKE_LINK_LIBRARY_USING_feature2 "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature2_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature2_ATTRIBUTES OVERRIDE=feature3) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature1,dep>" "$<LINK_LIBRARY:feature2,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt new file mode 100644 index 0000000..a95648b --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type-stderr.txt
@@ -0,0 +1,6 @@ +CMake Warning \(dev\) 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.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type.cmake new file mode 100644 index 0000000..29aba56 --- /dev/null +++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/unsupported-library_type.cmake
@@ -0,0 +1,10 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>") +set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE) +set(CMAKE_LINK_LIBRARY_feature_ATTRIBUTES LIBRARY_TYPE=STATIC) + +add_library(dep SHARED empty.c) + +add_library(lib SHARED empty.c) +target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt index 32d92d8..5791993 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt
@@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.17) if(RunCMake_TEST STREQUAL "LOCATION") cmake_minimum_required(VERSION 2.8.12) # Leave CMP0026 unset. endif() project(${RunCMake_TEST} NONE) -include(${RunCMake_TEST}.cmake) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake index b613ad1..c278831 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
@@ -13,6 +13,10 @@ run_cmake(LinkImplementationCycle6) run_cmake(LOCATION) run_cmake(SOURCES) +run_cmake(TransitiveBuild) +run_cmake(TransitiveLink-CMP0166-OLD) +run_cmake(TransitiveLink-CMP0166-NEW) +run_cmake(Unset) block() run_cmake(Scope)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake new file mode 100644 index 0000000..65adfaf --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild-check.cmake
@@ -0,0 +1,25 @@ +set(expect [[ +# file\(GENERATE\) produced: +main INCLUDE_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1' +main SYSTEM_INCLUDE_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/sys1' +main COMPILE_DEFINITIONS: 'DEFM;DEF1' +main COMPILE_FEATURES: 'cxx_std_20;cxx_std_11' +main COMPILE_OPTIONS: '-optM;-opt1' +main PRECOMPILE_HEADERS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h' +main SOURCES: 'main.c;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c' +main AUTOMOC_MACRO_NAMES: 'MOCM;MOC1' +main AUTOUIC_OPTIONS: '-uicM;-uic1' +]]) + +string(REGEX REPLACE "\r\n" "\n" expect "${expect}") +string(REGEX REPLACE "\n+$" "" expect "${expect}") + +file(READ "${RunCMake_TEST_BINARY_DIR}/out.txt" actual) +string(REGEX REPLACE "\r\n" "\n" actual "${actual}") +string(REGEX REPLACE "\n+$" "" actual "${actual}") + +if(NOT actual MATCHES "^${expect}$") + string(REPLACE "\n" "\n expect> " expect " expect> ${expect}") + string(REPLACE "\n" "\n actual> " actual " actual> ${actual}") + message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}") +endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake new file mode 100644 index 0000000..8f8eb96 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveBuild.cmake
@@ -0,0 +1,66 @@ +enable_language(C) +set(CMAKE_PCH_EXTENSION "") # suppress cmake_pch from SOURCES + +add_library(foo1 STATIC empty.c) +target_link_libraries(foo1 PRIVATE foo2 foo3) +target_include_directories(foo1 INTERFACE dir1) +target_compile_definitions(foo1 INTERFACE DEF1) +target_compile_features(foo1 INTERFACE cxx_std_11) +target_compile_options(foo1 INTERFACE -opt1) +target_precompile_headers(foo1 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty1.h") +target_sources(foo1 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty1.c") +set_target_properties(foo1 PROPERTIES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys1" + INTERFACE_AUTOMOC_MACRO_NAMES "MOC1" + INTERFACE_AUTOUIC_OPTIONS "-uic1" + ) + +add_library(foo2 STATIC empty.c) +target_include_directories(foo2 INTERFACE dir2) +target_compile_definitions(foo2 INTERFACE DEF2) +target_compile_features(foo2 INTERFACE cxx_std_14) +target_compile_options(foo2 INTERFACE -opt2) +target_precompile_headers(foo2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty2.h") +target_sources(foo2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/empty2.c") +set_target_properties(foo2 PROPERTIES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys2" + INTERFACE_AUTOMOC_MACRO_NAMES "MOC2" + INTERFACE_AUTOUIC_OPTIONS "-uic2" + ) + +add_library(foo3 STATIC empty.c) +target_include_directories(foo3 PRIVATE dir3) +target_compile_definitions(foo3 PRIVATE DEF3) +target_compile_features(foo3 PRIVATE cxx_std_17) +target_compile_options(foo3 PRIVATE -opt3) +target_precompile_headers(foo3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty3.h") +target_sources(foo3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty3.c") +set_target_properties(foo3 PROPERTIES + SYSTEM_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/sys3" + AUTOMOC_MACRO_NAMES "MOC3" + AUTOUIC_OPTIONS "-uic3" + ) + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo1) +target_include_directories(main PRIVATE dirM) +target_compile_definitions(main PRIVATE DEFM) +target_compile_features(main PRIVATE cxx_std_20) +target_compile_options(main PRIVATE -optM) +target_precompile_headers(main PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/empty.h") +set_target_properties(main PROPERTIES + AUTOMOC_MACRO_NAMES "MOCM" + AUTOUIC_OPTIONS "-uicM" + ) + +file(GENERATE OUTPUT out.txt CONTENT "# file(GENERATE) produced: +main INCLUDE_DIRECTORIES: '$<TARGET_PROPERTY:main,INCLUDE_DIRECTORIES>' +main SYSTEM_INCLUDE_DIRECTORIES: '$<TARGET_PROPERTY:main,SYSTEM_INCLUDE_DIRECTORIES>' +main COMPILE_DEFINITIONS: '$<TARGET_PROPERTY:main,COMPILE_DEFINITIONS>' +main COMPILE_FEATURES: '$<TARGET_PROPERTY:main,COMPILE_FEATURES>' +main COMPILE_OPTIONS: '$<TARGET_PROPERTY:main,COMPILE_OPTIONS>' +main PRECOMPILE_HEADERS: '$<TARGET_PROPERTY:main,PRECOMPILE_HEADERS>' +main SOURCES: '$<TARGET_PROPERTY:main,SOURCES>' +main AUTOMOC_MACRO_NAMES: '$<TARGET_PROPERTY:main,AUTOMOC_MACRO_NAMES>' +main AUTOUIC_OPTIONS: '$<TARGET_PROPERTY:main,AUTOUIC_OPTIONS>' +")
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake new file mode 100644 index 0000000..7ea95b8 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-check.cmake
@@ -0,0 +1,8 @@ +set(expect [[ +# file\(GENERATE\) produced: +main LINK_LIBRARIES: 'foo1' # not transitive +main LINK_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir2' +main LINK_OPTIONS: '-optM;-opt1;-opt2' +main LINK_DEPENDS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/depM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/dep1;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW-build/dep2' +]]) +include(${CMAKE_CURRENT_LIST_DIR}/TransitiveLink-check-common.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake new file mode 100644 index 0000000..658dd84 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-NEW.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0166 NEW) +include(TransitiveLink-common.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake new file mode 100644 index 0000000..2d430c2 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-check.cmake
@@ -0,0 +1,8 @@ +set(expect [[ +# file\(GENERATE\) produced: +main LINK_LIBRARIES: 'foo1' # not transitive +main LINK_DIRECTORIES: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dirM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/dir1' +main LINK_OPTIONS: '-optM;-opt1' +main LINK_DEPENDS: '[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-build/depM;[^';]*/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD-build/dep1' +]]) +include(${CMAKE_CURRENT_LIST_DIR}/TransitiveLink-check-common.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake new file mode 100644 index 0000000..a4a4599 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-CMP0166-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0166 OLD) +include(TransitiveLink-common.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake new file mode 100644 index 0000000..42e63bc --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-check-common.cmake
@@ -0,0 +1,12 @@ +string(REGEX REPLACE "\r\n" "\n" expect "${expect}") +string(REGEX REPLACE "\n+$" "" expect "${expect}") + +file(READ "${RunCMake_TEST_BINARY_DIR}/out.txt" actual) +string(REGEX REPLACE "\r\n" "\n" actual "${actual}") +string(REGEX REPLACE "\n+$" "" actual "${actual}") + +if(NOT actual MATCHES "^${expect}$") + string(REPLACE "\n" "\n expect> " expect " expect> ${expect}") + string(REPLACE "\n" "\n actual> " actual " actual> ${actual}") + message(FATAL_ERROR "Expected file(GENERATE) output:\n${expect}\ndoes not match actual output:\n${actual}") +endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake new file mode 100644 index 0000000..c120366 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/TransitiveLink-common.cmake
@@ -0,0 +1,42 @@ +enable_language(C) + +add_library(foo1 STATIC empty.c) +target_link_libraries(foo1 PRIVATE foo2 foo3) +target_link_directories(foo1 INTERFACE dir1) +target_link_options(foo1 INTERFACE -opt1) +set_target_properties(foo1 PROPERTIES + INTERFACE_LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep1" + ) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep1" "") + +add_library(foo2 STATIC empty.c) +target_link_directories(foo2 INTERFACE dir2) +target_link_options(foo2 INTERFACE -opt2) +set_target_properties(foo2 PROPERTIES + INTERFACE_LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep2" + ) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep2" "") + +add_library(foo3 STATIC empty.c) +target_link_directories(foo3 PRIVATE dir3) +target_link_options(foo3 PRIVATE -opt3) +set_target_properties(foo3 PROPERTIES + LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dep3" + ) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep3" "") + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo1) +target_link_directories(main PRIVATE dirM) +target_link_options(main PRIVATE -optM) +set_target_properties(main PROPERTIES + LINK_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/depM" + ) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/depM" "") + +file(GENERATE OUTPUT out.txt CONTENT "# file(GENERATE) produced: +main LINK_LIBRARIES: '$<TARGET_PROPERTY:main,LINK_LIBRARIES>' # not transitive +main LINK_DIRECTORIES: '$<TARGET_PROPERTY:main,LINK_DIRECTORIES>' +main LINK_OPTIONS: '$<TARGET_PROPERTY:main,LINK_OPTIONS>' +main LINK_DEPENDS: '$<TARGET_PROPERTY:main,LINK_DEPENDS>' +")
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset-check.cmake new file mode 100644 index 0000000..59910f1 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset-check.cmake
@@ -0,0 +1,4 @@ +file(READ ${RunCMake_TEST_BINARY_DIR}/out.txt out) +if(NOT out STREQUAL "'' ''") + set(RunCMake_TEST_FAILED "PROPERTY_THAT_IS_NOT_SET did not evaluate as empty:\n ${out}") +endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset.cmake new file mode 100644 index 0000000..dfe24a2 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset.cmake
@@ -0,0 +1,3 @@ +add_library(iface INTERFACE) +file(GENERATE OUTPUT out.txt CONTENT + "'$<TARGET_PROPERTY:iface,PROPERTY_THAT_IS_NOT_SET>' '$<TARGET_PROPERTY:PROPERTY_THAT_IS_NOT_SET>'" TARGET iface)
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/RunCMake/GenEx-TARGET_PROPERTY/empty.h
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c similarity index 100% copy from Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake copy to Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.c
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/RunCMake/GenEx-TARGET_PROPERTY/empty1.h
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/RunCMake/GenEx-TARGET_PROPERTY/empty2.h
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h similarity index 100% copy from Tests/CPackWiXGenerator/file with spaces.h copy to Tests/RunCMake/GenEx-TARGET_PROPERTY/empty3.h
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c b/Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/main.c
@@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +}
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h index dac4fda..e0e423d 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h index b6e2a4a..cfc1187 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h index 0b1dcba..e2c132d 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h index 5e3ac9f..b54eef5 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Empty/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h index 3ba2d2e..6013dcd 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h index 3c7e093..7a5a573 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h index 11f8042..388dbc9 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h index b6e2a4a..cfc1187 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h index 1481acd..97b988d 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h index b6e2a4a..cfc1187 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/UNIX_DeprecatedOnly/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h index 3ba2d2e..6013dcd 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h index 3c7e093..7a5a573 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h index 3ba2d2e..6013dcd 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h index 3c7e093..7a5a573 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h index bf9163e..e64c45f 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libshared_export.h
@@ -33,6 +33,7 @@ # define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSHARED_NO_DEPRECATED # define LIBSHARED_NO_DEPRECATED
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h index 5e3ac9f..b54eef5 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/WinEmpty/libstatic_export.h
@@ -33,6 +33,7 @@ # define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED #endif +/* NOLINTNEXTLINE(readability-avoid-unconditional-preprocessor-if) */ #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef LIBSTATIC_NO_DEPRECATED # define LIBSTATIC_NO_DEPRECATED
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-result.txt similarity index 100% rename from Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt rename to Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt new file mode 100644 index 0000000..4cf5945 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt
@@ -0,0 +1,9 @@ +CMake Error at NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT.cmake:1 \(add_custom_command\): + Error evaluating generator expression: + + \$<CXX_COMPILER_FRONTEND_VARIANT> + + \$<CXX_COMPILER_FRONTEND_VARIANT> may only be used with binary targets. It + may not be used with add_custom_command or add_custom_target. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT.cmake new file mode 100644 index 0000000..08ce956 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT.cmake
@@ -0,0 +1,4 @@ +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/copied_file.cpp" + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" "${CMAKE_CURRENT_BINARY_DIR}/copied_file$<CXX_COMPILER_FRONTEND_VARIANT>.cpp" +) +add_custom_target(drive DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/copied_file.cpp")
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-result.txt similarity index 100% copy from Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt copy to Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt new file mode 100644 index 0000000..c7e43a3 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt
@@ -0,0 +1,9 @@ +CMake Error at NonValidTarget-C_COMPILER_FRONTEND_VARIANT.cmake:1 \(add_custom_command\): + Error evaluating generator expression: + + \$<C_COMPILER_FRONTEND_VARIANT> + + \$<C_COMPILER_FRONTEND_VARIANT> may only be used with binary targets. It + may not be used with add_custom_command or add_custom_target. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT.cmake new file mode 100644 index 0000000..c90f960 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT.cmake
@@ -0,0 +1,4 @@ +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/copied_file.cpp" + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" "${CMAKE_CURRENT_BINARY_DIR}/copied_file$<C_COMPILER_FRONTEND_VARIANT>.cpp" +) +add_custom_target(drive DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/copied_file.cpp")
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-result.txt similarity index 100% copy from Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt copy to Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt new file mode 100644 index 0000000..8641de9 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt
@@ -0,0 +1,9 @@ +CMake Error at NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT.cmake:1 \(add_custom_command\): + Error evaluating generator expression: + + \$<Fortran_COMPILER_FRONTEND_VARIANT> + + \$<Fortran_COMPILER_FRONTEND_VARIANT> may only be used with binary targets. + It may not be used with add_custom_command or add_custom_target. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT.cmake new file mode 100644 index 0000000..4312af4 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT.cmake
@@ -0,0 +1,4 @@ +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/copied_file.cpp" + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" "${CMAKE_CURRENT_BINARY_DIR}/copied_file$<Fortran_COMPILER_FRONTEND_VARIANT>.cpp" +) +add_custom_target(drive DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/copied_file.cpp")
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index 2a0c893..8ba70aa 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
@@ -20,6 +20,9 @@ run_cmake(NonValidTarget-C_COMPILER_VERSION) run_cmake(NonValidTarget-CXX_COMPILER_VERSION) run_cmake(NonValidTarget-Fortran_COMPILER_VERSION) +run_cmake(NonValidTarget-C_COMPILER_FRONTEND_VARIANT) +run_cmake(NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT) +run_cmake(NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT) run_cmake(NonValidTarget-TARGET_PROPERTY) run_cmake(NonValidTarget-TARGET_POLICY) run_cmake(COMPILE_ONLY-not-compiling)
diff --git a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake index d8965f7..b1c758a 100644 --- a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
@@ -3,7 +3,7 @@ set(RunCMake_GENERATOR_PLATFORM "") run_cmake(NoPlatform) -if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio ([89]|1[0124567])( 20[0-9][0-9])?$") +if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio [0-9]+( 20[0-9][0-9])?$") set(RunCMake_GENERATOR_PLATFORM "x64") run_cmake(x64Platform) else() @@ -17,7 +17,7 @@ run_cmake(TwoPlatforms) unset(RunCMake_TEST_OPTIONS) -if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio ([89]|1[0124567])( 20[0-9][0-9])?$") +if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio [0-9]+( 20[0-9][0-9])?$") set(RunCMake_TEST_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/TestPlatform-toolchain.cmake) run_cmake(TestPlatformToolchain) unset(RunCMake_TEST_OPTIONS)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index 6e064a7..08cc274 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -134,7 +134,7 @@ ${CMAKE_CTEST_COMMAND} -C Debug -V - --no-label-sumary + --no-label-summary ) endfunction() @@ -197,7 +197,7 @@ ${CMAKE_CTEST_COMMAND} -C Debug -R discovery_timeout_test - --no-label-sumary + --no-label-summary ) endfunction() @@ -362,10 +362,7 @@ run_GoogleTestXML(${DISCOVERY_MODE}) message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...") run_GoogleTest_discovery_timeout(${DISCOVERY_MODE}) - if(# VS 9 does not rebuild if POST_BUILD command changes. - NOT "${DISCOVERY_MODE};${RunCMake_GENERATOR}" MATCHES "^POST_BUILD;Visual Studio 9") - run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE}) - endif() + run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE}) run_GoogleTest_discovery_test_list(${DISCOVERY_MODE}) run_GoogleTest_discovery_test_list_scoped(${DISCOVERY_MODE}) run_GoogleTest_discovery_flush_script(${DISCOVERY_MODE})
diff --git a/Tests/RunCMake/IAR/RunCMakeTest.cmake b/Tests/RunCMake/IAR/RunCMakeTest.cmake index 2049740..2fefa6a 100644 --- a/Tests/RunCMake/IAR/RunCMakeTest.cmake +++ b/Tests/RunCMake/IAR/RunCMakeTest.cmake
@@ -8,6 +8,13 @@ endif() endif() +function(run_toolchain case) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + run_cmake_with_options(${case} ${ARGN}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build .) +endfunction() + foreach(_iar_toolchain IN LISTS _iar_toolchains) message(STATUS "Found IAR toolchain: ${_iar_toolchain}") cmake_path(GET _iar_toolchain PARENT_PATH BIN_DIR) @@ -15,16 +22,22 @@ cmake_path(GET TOOLKIT_DIR FILENAME ARCH) # Sets the minimal requirements for linking each target architecture - if(ARCH STREQUAL rl78) - set(LINK_OPTS -"--config_def _STACK_SIZE=256 \ ---config_def _NEAR_HEAP_SIZE=0x400 \ ---config_def _FAR_HEAP_SIZE=4096 \ ---config_def _HUGE_HEAP_SIZE=0 \ ---config_def _NEAR_CONST_LOCATION_START=0x2000 \ ---config_def _NEAR_CONST_LOCATION_SIZE=0x6F00 \ ---define_symbol _NEAR_CONST_LOCATION=0 \ ---config ${TOOLKIT_DIR}/config/lnkrl78_s3.icf" ) + if(ARCH STREQUAL "avr") + string(CONCAT LINK_OPTS + "-I${TOOLKIT_DIR}/lib " + "-f ${TOOLKIT_DIR}/src/template/lnk3s.xcl " + ) + elseif(ARCH STREQUAL "rl78") + string(CONCAT LINK_OPTS + "--config_def _STACK_SIZE=256 " + "--config_def _NEAR_HEAP_SIZE=0x400 " + "--config_def _FAR_HEAP_SIZE=4096 " + "--config_def _HUGE_HEAP_SIZE=0 " + "--config_def _NEAR_CONST_LOCATION_START=0x2000 " + "--config_def _NEAR_CONST_LOCATION_SIZE=0x6F00 " + "--define_symbol _NEAR_CONST_LOCATION=0 " + "--config ${TOOLKIT_DIR}/config/lnkrl78_s3.icf " + ) else() set(LINK_OPTS "") endif() @@ -35,30 +48,26 @@ PATHS ${BIN_DIR} REQUIRED ) - set(RunCMake_TEST_OPTIONS + run_toolchain(iar-c -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_C_COMPILER=${_iar_toolchain} -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS} ) - run_cmake(iar-c) - set(RunCMake_TEST_OPTIONS + run_toolchain(iar-cxx -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_CXX_COMPILER=${_iar_toolchain} -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS} ) - run_cmake(iar-cxx) - set(RunCMake_TEST_OPTIONS + run_toolchain(iar-asm -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_ASM_COMPILER=${IAR_ASSEMBLER} - ) - run_cmake(iar-asm) + ) - set(RunCMake_TEST_OPTIONS + run_toolchain(iar-lib -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_C_COMPILER=${_iar_toolchain} -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS} - ) - run_cmake(iar-lib) + ) endforeach()
diff --git a/Tests/RunCMake/IAR/iar-asm.cmake b/Tests/RunCMake/IAR/iar-asm.cmake index 7ff64f6..1c19182 100644 --- a/Tests/RunCMake/IAR/iar-asm.cmake +++ b/Tests/RunCMake/IAR/iar-asm.cmake
@@ -1,5 +1,4 @@ enable_language(ASM) -add_executable(exec-asm) -target_sources(exec-asm PRIVATE module.asm) +add_executable(exec-asm module.asm) target_link_options(exec-asm PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/iar-c.cmake b/Tests/RunCMake/IAR/iar-c.cmake index a36f096..5e7e740 100644 --- a/Tests/RunCMake/IAR/iar-c.cmake +++ b/Tests/RunCMake/IAR/iar-c.cmake
@@ -1,5 +1,5 @@ enable_language(C) -add_executable(exec-c) -target_sources(exec-c PRIVATE module.c) +add_executable(exec-c module.c) +target_compile_options(exec-c PRIVATE -e) target_link_options(exec-c PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/iar-cxx.cmake b/Tests/RunCMake/IAR/iar-cxx.cmake index 6b005b5..6056d32 100644 --- a/Tests/RunCMake/IAR/iar-cxx.cmake +++ b/Tests/RunCMake/IAR/iar-cxx.cmake
@@ -1,5 +1,5 @@ enable_language(CXX) -add_executable(exec-cxx) -target_sources(exec-cxx PRIVATE module.cxx) +add_executable(exec-cxx module.cxx) +target_compile_options(exec-cxx PRIVATE -e) target_link_options(exec-cxx PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/iar-lib.cmake b/Tests/RunCMake/IAR/iar-lib.cmake index 78b3136..17e6c95 100644 --- a/Tests/RunCMake/IAR/iar-lib.cmake +++ b/Tests/RunCMake/IAR/iar-lib.cmake
@@ -1,10 +1,9 @@ enable_language(C) -add_library(iar-test-lib) -target_sources(iar-test-lib PRIVATE libmod.c) +add_library(iar-test-lib libmod.c) -add_executable(exec-lib-c) -target_sources(exec-lib-c PRIVATE module.c) +add_executable(exec-lib-c module.c) +target_compile_options(exec-lib-c PRIVATE -e) target_compile_definitions(exec-lib-c PRIVATE __USE_LIBFUN) -target_link_libraries(exec-lib-c LINK_PUBLIC iar-test-lib) +target_link_libraries(exec-lib-c PRIVATE iar-test-lib) target_link_options(exec-lib-c PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/InstallParallel/CMakeLists.txt b/Tests/RunCMake/InstallParallel/CMakeLists.txt new file mode 100644 index 0000000..94e43ba --- /dev/null +++ b/Tests/RunCMake/InstallParallel/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/InstallParallel/RunCMakeTest.cmake b/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake new file mode 100644 index 0000000..ae3f112 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake
@@ -0,0 +1,17 @@ +include(RunCMake) + +function(install_test test parallel install_target check_script) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-install) + set(RunCMake_TEST_OPTIONS -DINSTALL_PARALLEL=${parallel}) + if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + run_cmake(install) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${test}-install ${CMAKE_COMMAND} --build . --config Debug -t ${install_target}) + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_SOURCE_DIR}) + run_cmake_command(verify-parallel ${CMAKE_COMMAND} -P ${check_script} ${RunCMake_TEST_BINARY_DIR}/.ninja_log) +endfunction() + +install_test(parallel 1 install/parallel check-parallel.cmake) +install_test(no-parallel 0 install check-single.cmake)
diff --git a/Tests/RunCMake/InstallParallel/check-parallel.cmake b/Tests/RunCMake/InstallParallel/check-parallel.cmake new file mode 100644 index 0000000..4e4cf52 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/check-parallel.cmake
@@ -0,0 +1,15 @@ +include(read-ninja-install.cmake) + +foreach(line ${lines}) + string(REPLACE "\t" ";" line ${line}) + list(GET line 0 start) + list(GET line 1 end) + list(APPEND start_times ${start}) + list(APPEND end_times ${end}) +endforeach() +list(GET start_times 1 start_2) +list(GET end_times 0 end_1) + +if (NOT start_2 LESS end_1) + message(FATAL_ERROR "Install is not parallel") +endif()
diff --git a/Tests/RunCMake/InstallParallel/check-single.cmake b/Tests/RunCMake/InstallParallel/check-single.cmake new file mode 100644 index 0000000..79c4d5c --- /dev/null +++ b/Tests/RunCMake/InstallParallel/check-single.cmake
@@ -0,0 +1,5 @@ +include(read-ninja-install.cmake) +list(LENGTH lines len) +if (NOT ${len} STREQUAL "1") + message(FATAL_ERROR "Expected single installation call") +endif()
diff --git a/Tests/RunCMake/InstallParallel/install.cmake b/Tests/RunCMake/InstallParallel/install.cmake new file mode 100644 index 0000000..54b5078 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/install.cmake
@@ -0,0 +1,6 @@ +install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +if (INSTALL_PARALLEL) + set_property(GLOBAL PROPERTY INSTALL_PARALLEL ON) +endif() +add_subdirectory(subdir-1) +add_subdirectory(subdir-2)
diff --git a/Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt b/Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt new file mode 100644 index 0000000..8f69a04 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt
@@ -0,0 +1,5 @@ +installing:.* +installing:.* +installing:.* +installing:.* +installing:.*
diff --git a/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt b/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt new file mode 100644 index 0000000..e0d2a56 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt
@@ -0,0 +1,15 @@ +\[1\/5\] Installing only the local directory... +\-\- Install configuration: \"Debug\" +installing:.* +\[2\/5\] Installing only the local directory... +\-\- Install configuration: \"Debug\" +installing:.* +\[3\/5\] Installing only the local directory... +\-\- Install configuration: \"Debug\" +installing:.* +\[4\/5\] Installing only the local directory... +\-\- Install configuration: \"Debug\" +installing:.* +\[5\/5\] Installing only the local directory... +\-\- Install configuration: \"Debug\" +installing:.*
diff --git a/Tests/RunCMake/InstallParallel/read-ninja-install.cmake b/Tests/RunCMake/InstallParallel/read-ninja-install.cmake new file mode 100644 index 0000000..731c5eb --- /dev/null +++ b/Tests/RunCMake/InstallParallel/read-ninja-install.cmake
@@ -0,0 +1,4 @@ +set(ninja_log ${CMAKE_ARGV3}) +file(STRINGS ${ninja_log} lines) +list(POP_FRONT lines) +list(FILTER lines INCLUDE REGEX ".*install.*util")
diff --git a/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt new file mode 100644 index 0000000..6b235c4 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt
@@ -0,0 +1,3 @@ +install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +add_subdirectory(subdir-3) +add_subdirectory(subdir-4)
diff --git a/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt new file mode 100644 index 0000000..dd7eac3 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt
@@ -0,0 +1 @@ +install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
diff --git a/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt new file mode 100644 index 0000000..dd7eac3 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt
@@ -0,0 +1 @@ +install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
diff --git a/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt new file mode 100644 index 0000000..dd7eac3 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt
@@ -0,0 +1 @@ +install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})")
diff --git a/Tests/RunCMake/LanguageStandards/CMakeLists.txt b/Tests/RunCMake/LanguageStandards/CMakeLists.txt new file mode 100644 index 0000000..94e43ba --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/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/LanguageStandards/Inspect.cmake b/Tests/RunCMake/LanguageStandards/Inspect.cmake new file mode 100644 index 0000000..b0f2e60 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/Inspect.cmake
@@ -0,0 +1,14 @@ +enable_language(C) +enable_language(CXX) + +set(info "") +foreach(var + CMAKE_C_STANDARD_DEFAULT + CMAKE_CXX_STANDARD_DEFAULT + ) + if(DEFINED ${var}) + string(APPEND info "set(${var} \"${${var}}\")\n") + endif() +endforeach() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/LanguageStandards/RunCMakeTest.cmake b/Tests/RunCMake/LanguageStandards/RunCMakeTest.cmake new file mode 100644 index 0000000..bc9dfd4 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/RunCMakeTest.cmake
@@ -0,0 +1,32 @@ +include(RunCMake) + +# Detect information from the toolchain: +# - CMAKE_C_STANDARD_DEFAULT +# - CMAKE_CXX_STANDARD_DEFAULT +run_cmake(Inspect) +include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") + +function(run_StdLatest lang) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/StdLatest-${lang}-build) + run_cmake(StdLatest-${lang}) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(StdLatest-${lang}-build ${CMAKE_COMMAND} --build . --config Debug) +endfunction() + +if(NOT CMake_NO_C_STANDARD AND DEFINED CMAKE_C_STANDARD_DEFAULT) + run_StdLatest(C) +endif() +if(NOT CMake_NO_CXX_STANDARD AND DEFINED CMAKE_CXX_STANDARD_DEFAULT) + run_StdLatest(CXX) +endif() +if(CMake_TEST_CUDA) + run_StdLatest(CUDA) +endif() +if(CMake_TEST_HIP) + run_StdLatest(HIP) +endif() +if(CMake_TEST_OBJC) + run_StdLatest(OBJC) + run_StdLatest(OBJCXX) +endif()
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-C-stdout.txt b/Tests/RunCMake/LanguageStandards/StdLatest-C-stdout.txt new file mode 100644 index 0000000..57aad4d --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-C-stdout.txt
@@ -0,0 +1 @@ +-- CMAKE_C_STANDARD_LATEST='[0-9][0-9]'
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-C.c b/Tests/RunCMake/LanguageStandards/StdLatest-C.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-C.c
@@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +}
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-C.cmake b/Tests/RunCMake/LanguageStandards/StdLatest-C.cmake new file mode 100644 index 0000000..54f5a19 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-C.cmake
@@ -0,0 +1,4 @@ +enable_language(C) +message(STATUS "CMAKE_C_STANDARD_LATEST='${CMAKE_C_STANDARD_LATEST}'") +add_executable(StdLatest StdLatest-C.c) +target_compile_features(StdLatest PRIVATE c_std_${CMAKE_C_STANDARD_LATEST})
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-CUDA-stdout.txt b/Tests/RunCMake/LanguageStandards/StdLatest-CUDA-stdout.txt new file mode 100644 index 0000000..207541a --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-CUDA-stdout.txt
@@ -0,0 +1 @@ +-- CMAKE_CUDA_STANDARD_LATEST='[0-9][0-9]'
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-CUDA.cmake b/Tests/RunCMake/LanguageStandards/StdLatest-CUDA.cmake new file mode 100644 index 0000000..496abb0 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-CUDA.cmake
@@ -0,0 +1,4 @@ +enable_language(CUDA) +message(STATUS "CMAKE_CUDA_STANDARD_LATEST='${CMAKE_CUDA_STANDARD_LATEST}'") +add_executable(StdLatest StdLatest-CUDA.cu) +target_compile_features(StdLatest PRIVATE cuda_std_${CMAKE_CUDA_STANDARD_LATEST})
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-CUDA.cu b/Tests/RunCMake/LanguageStandards/StdLatest-CUDA.cu new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-CUDA.cu
@@ -0,0 +1,4 @@ +int main() +{ + return 0; +}
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-CXX-stdout.txt b/Tests/RunCMake/LanguageStandards/StdLatest-CXX-stdout.txt new file mode 100644 index 0000000..db83723 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-CXX-stdout.txt
@@ -0,0 +1 @@ +-- CMAKE_CXX_STANDARD_LATEST='[0-9][0-9]'
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-CXX.cmake b/Tests/RunCMake/LanguageStandards/StdLatest-CXX.cmake new file mode 100644 index 0000000..34aba55 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-CXX.cmake
@@ -0,0 +1,5 @@ +enable_language(CXX) +set(CMAKE_CXX_SCAN_FOR_MODULES OFF) # In case C++20 or higher is the latest. +message(STATUS "CMAKE_CXX_STANDARD_LATEST='${CMAKE_CXX_STANDARD_LATEST}'") +add_executable(StdLatest StdLatest-CXX.cxx) +target_compile_features(StdLatest PRIVATE cxx_std_${CMAKE_CXX_STANDARD_LATEST})
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-CXX.cxx b/Tests/RunCMake/LanguageStandards/StdLatest-CXX.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-CXX.cxx
@@ -0,0 +1,4 @@ +int main() +{ + return 0; +}
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-HIP-stdout.txt b/Tests/RunCMake/LanguageStandards/StdLatest-HIP-stdout.txt new file mode 100644 index 0000000..681ea9e --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-HIP-stdout.txt
@@ -0,0 +1 @@ +-- CMAKE_HIP_STANDARD_LATEST='[0-9][0-9]'
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-HIP.cmake b/Tests/RunCMake/LanguageStandards/StdLatest-HIP.cmake new file mode 100644 index 0000000..4eaa56c --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-HIP.cmake
@@ -0,0 +1,4 @@ +enable_language(HIP) +message(STATUS "CMAKE_HIP_STANDARD_LATEST='${CMAKE_HIP_STANDARD_LATEST}'") +add_executable(StdLatest StdLatest-HIP.hip) +target_compile_features(StdLatest PRIVATE hip_std_${CMAKE_HIP_STANDARD_LATEST})
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-HIP.hip b/Tests/RunCMake/LanguageStandards/StdLatest-HIP.hip new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-HIP.hip
@@ -0,0 +1,4 @@ +int main() +{ + return 0; +}
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-OBJC-stdout.txt b/Tests/RunCMake/LanguageStandards/StdLatest-OBJC-stdout.txt new file mode 100644 index 0000000..577c126 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-OBJC-stdout.txt
@@ -0,0 +1 @@ +-- CMAKE_OBJC_STANDARD_LATEST='[0-9][0-9]'
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-OBJC.cmake b/Tests/RunCMake/LanguageStandards/StdLatest-OBJC.cmake new file mode 100644 index 0000000..6d05ab6 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-OBJC.cmake
@@ -0,0 +1,4 @@ +enable_language(OBJC) +message(STATUS "CMAKE_OBJC_STANDARD_LATEST='${CMAKE_OBJC_STANDARD_LATEST}'") +add_executable(StdLatest StdLatest-OBJC.m) +set_property(TARGET StdLatest PROPERTY OBJC_STANDARD "${CMAKE_OBJC_STANDARD_LATEST}")
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-OBJC.m b/Tests/RunCMake/LanguageStandards/StdLatest-OBJC.m new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-OBJC.m
@@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +}
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX-stdout.txt b/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX-stdout.txt new file mode 100644 index 0000000..0c5567b --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX-stdout.txt
@@ -0,0 +1 @@ +-- CMAKE_OBJCXX_STANDARD_LATEST='[0-9][0-9]'
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX.cmake b/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX.cmake new file mode 100644 index 0000000..c7418d0 --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX.cmake
@@ -0,0 +1,4 @@ +enable_language(OBJCXX) +message(STATUS "CMAKE_OBJCXX_STANDARD_LATEST='${CMAKE_OBJCXX_STANDARD_LATEST}'") +add_executable(StdLatest StdLatest-OBJCXX.mm) +set_property(TARGET StdLatest PROPERTY OBJCXX_STANDARD "${CMAKE_OBJCXX_STANDARD_LATEST}")
diff --git a/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX.mm b/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX.mm new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/LanguageStandards/StdLatest-OBJCXX.mm
@@ -0,0 +1,4 @@ +int main() +{ + return 0; +}
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake index e79eb45..ac5f7f1 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake
@@ -1,7 +1,7 @@ enable_language(C) -set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER= UNICITY=ALL) +set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER= DEDUPLICATION=ALL) add_library(lib STATIC lib.c)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake index c9da734..63edfd3 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake
@@ -1,7 +1,7 @@ enable_language(C) -set(CMAKE_C_LINK_LIBRARIES_PROCESSING UNICITY=ALL ORDER) +set(CMAKE_C_LINK_LIBRARIES_PROCESSING DEDUPLICATION=ALL ORDER) add_library(lib STATIC lib.c)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake index a1311fe..0bd7c5f 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake
@@ -1,7 +1,7 @@ enable_language(C) -set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE UNICITY=ALL) +set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE DEDUPLICATION=ALL) add_library(lib STATIC lib.c)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt index 46d5513..256fd4e 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt
@@ -2,7 +2,7 @@ Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': WRONG=REVERSE - UNICITY=WRONG + DEDUPLICATION=WRONG Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake index 9d48f4f..ccb4dae 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake +++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake
@@ -1,7 +1,7 @@ enable_language(C) -set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE UNICITY=WRONG) +set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE DEDUPLICATION=WRONG) add_library(lib STATIC lib.c)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION-result.txt similarity index 100% rename from Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt rename to Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION-result.txt
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION-stderr.txt new file mode 100644 index 0000000..1138a31 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION-stderr.txt
@@ -0,0 +1,7 @@ +CMake Error at Invalid_DEDUPLICATION.cmake:[0-9]+ \(add_executable\): + Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': + + DEDUPLICATION=WRONG + +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION.cmake new file mode 100644 index 0000000..e5ff622 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_DEDUPLICATION.cmake
@@ -0,0 +1,9 @@ + +enable_language(C) + +set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=WRONG) + +add_library(lib STATIC lib.c) + +add_executable(main main.c) +target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake index 72a7e02..f0c7ee3 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake +++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake
@@ -1,7 +1,7 @@ enable_language(C) -set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=WRONG UNICITY=ALL) +set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=WRONG DEDUPLICATION=ALL) add_library(lib STATIC lib.c)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt deleted file mode 100644 index 4d759b7..0000000 --- a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt +++ /dev/null
@@ -1,7 +0,0 @@ -CMake Error at Invalid_UNICITY.cmake:[0-9]+ \(add_executable\): - Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING': - - UNICITY=WRONG - -Call Stack \(most recent call first\): - CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake deleted file mode 100644 index f423eef..0000000 --- a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake +++ /dev/null
@@ -1,9 +0,0 @@ - -enable_language(C) - -set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=WRONG) - -add_library(lib STATIC lib.c) - -add_executable(main main.c) -target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake b/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake index 763f48b..d4105b4 100644 --- a/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake +++ b/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake
@@ -1,7 +1,7 @@ include(RunCMake) run_cmake(Invalid_ORDER) -run_cmake(Invalid_UNICITY) +run_cmake(Invalid_DEDUPLICATION) run_cmake(InvalidConfiguration1) run_cmake(InvalidConfiguration2) run_cmake(InvalidConfiguration3)
diff --git a/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake index 0a3f01c..77ab874 100644 --- a/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake +++ b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
@@ -1,10 +1,5 @@ include(RunCMake) -if (RunCMake_GENERATOR MATCHES "Visual Studio 9 2008") - run_cmake(UnsupportedLinkerType) - return() -endif() - run_cmake(InvalidLinkerType1) run_cmake(InvalidLinkerType2) @@ -22,8 +17,9 @@ -DCMake_TEST_Swift=${CMake_TEST_Swift}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release --verbose ${ARGN}) - + unset(RunCMake_TEST_OUTPUT_MERGE) unset(RunCMake_TEST_BINARY_DIR) unset(RunCMake_TEST_NO_CLEAN) endmacro()
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt deleted file mode 100644 index 6473451..0000000 --- a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt +++ /dev/null
@@ -1,3 +0,0 @@ -CMake Error at UnsupportedLinkerType.cmake:[0-9]+ \(add_executable\): - 'LINKER_TYPE' property, specified on target 'main', is not supported by - this generator.
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake deleted file mode 100644 index 1b0703c..0000000 --- a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake +++ /dev/null
@@ -1,5 +0,0 @@ - -enable_language(C) - -set(CMAKE_LINKER_TYPE LDD) -add_executable(main main.c)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake index 9514c97..d50d403 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
@@ -26,8 +26,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows|AIX|SunOS)$|BSD" AND NOT CMAKE_C_COMPILER_ID MATCHES "^(Borland|Embarcadero|OpenWatcom|OrangeC|Watcom)$" AND NOT (CMAKE_C_COMPILER_ID MATCHES "^(Intel|IntelLLVM)$" AND CMAKE_SYSTEM_NAME STREQUAL "Windows") - AND NOT CMAKE_C_COMPILER_LINKER MATCHES "Visual Studio 9\\.0" - AND NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 " + AND NOT CMAKE_C_COMPILER_LINKER MATCHES "Microsoft Visual Studio 9\\.0/VC/bin" ) if(NOT CMAKE_C_COMPILER_LINKER OR NOT CMAKE_C_COMPILER_LINKER_ID OR NOT CMAKE_C_COMPILER_LINKER_VERSION) message(SEND_ERROR "C compiler's linker not identified:\n"
diff --git a/Tests/RunCMake/README.rst b/Tests/RunCMake/README.rst index d8cae8b..ea6db54 100644 --- a/Tests/RunCMake/README.rst +++ b/Tests/RunCMake/README.rst
@@ -1,37 +1,63 @@ -This directory contains tests that run CMake to configure a project -but do not actually build anything. To add a test: +CMake Tests/RunCMake Directory +****************************** + +This directory contains tests that run CMake and/or other tools while +precisely checking their return code and stdout/stderr content. +The RunCMake infrastructure is useful for testing error cases and +diagnostic output. + +See also `../README.rst`_ and the `CMake Source Code Guide`_. + +.. _`../README.rst`: ../README.rst +.. _`CMake Source Code Guide`: ../../Help/dev/source.rst +.. _`CMakeLists.txt`: CMakeLists.txt + +Adding a Test +============= + +To add a test: 1. Add a subdirectory named for the test, say ``<Test>/``. -2. In ``./CMakeLists.txt`` call ``add_RunCMake_test`` and pass the +2. In `CMakeLists.txt`_ call ``add_RunCMake_test`` and pass the test directory name ``<Test>``. 3. Create script ``<Test>/RunCMakeTest.cmake`` in the directory containing:: include(RunCMake) - run_cmake(SubTest1) + run_cmake(Case1) ... - run_cmake(SubTestN) + run_cmake(CaseN) - where ``SubTest1`` through ``SubTestN`` are sub-test names each - corresponding to an independent CMake run and project configuration. + where ``Case1`` through ``CaseN`` are case names each corresponding to + an independent CMake run and project configuration. One may also add calls of the form:: - run_cmake_command(SubTestI ${CMAKE_COMMAND} ...) + run_cmake_command(CaseI ${CMAKE_COMMAND} ...) to fully customize the test case command-line. Alternatively, if the test is to cover running ``ctest -S`` then use:: include(RunCTest) - run_ctest(SubTest1) + run_ctest(Case1) ... - run_ctest(SubTestN) + run_ctest(CaseN) and create ``test.cmake.in``, ``CTestConfig.cmake.in``, and ``CMakeLists.txt.in`` files to be configured for each case. + Alternatively, if the test is to cover running ``cpack -G`` then use:: + + include(RunCPack) + run_cpack(Sample1) + ... + run_cpack(SampleN) + + where ``Sample1`` through ``SampleN`` are sample project directories + in the ``RunCPack/`` directory adjacent to this file. + 4. Create file ``<Test>/CMakeLists.txt`` in the directory containing:: cmake_minimum_required(...) @@ -40,37 +66,194 @@ where ``${RunCMake_TEST}`` is literal. A value for ``RunCMake_TEST`` will be passed to CMake by the ``run_cmake`` macro when running each - sub-test. + case. -5. Create a ``<Test>/<SubTest>.cmake`` file for each sub-test named +5. Create a ``<Test>/<case>.cmake`` file for each case named above containing the actual test code. Optionally create files containing expected test results: - ``<SubTest>-result.txt`` + ``<case>-result.txt`` Regex matching expected process result, if not ``0`` - ``<SubTest>-stdout.txt`` + ``<case>-stdout.txt`` Regex matching expected stdout content - ``<SubTest>-stderr.txt`` + ``<case>-stderr.txt`` Regex matching expected stderr content, if not ``^$`` - ``<SubTest>-check.cmake`` + ``<case>-check.cmake`` Custom result check. - Note that when a specific platform expects differing stdout or stderr that - can be done by adding a platform specific output file. These follow the - naming convention of: - ``<SubTest>-stdout-<platform_lower_case>.txt`` - ``<SubTest>-stderr-<platform_lower_case>.txt`` + To specify platform-specific matches, create files of the form + ``<case>-{stdout,stderr}-<platform_lower_case>.txt``. Note that trailing newlines will be stripped from actual and expected test output before matching against the stdout and stderr expressions. - The code in ``<SubTest>-check.cmake`` may use variables + The code in ``<case>-check.cmake`` may use the `RunCMake Variables`_. + On failure the script must store a message in ``RunCMake_TEST_FAILED``. + The check script may optionally set ``RunCMake_TEST_FAILURE_MESSAGE`` + with additional text to be included in the message if the test fails. - ``RunCMake_TEST_SOURCE_DIR`` - Top of test source tree - ``RunCMake_TEST_BINARY_DIR`` - Top of test binary tree +RunCMake Commands +================= - and an failure must store a message in ``RunCMake_TEST_FAILED``. +A ``RunCMakeTest.cmake`` script, after ``include(RunCMake)``, may use +the following commands. + +``run_cmake(<case>)`` + Run CMake or another command and check expected results described by + ``<case>-{result,stdout,stderr}.txt`` and ``<case>-check.cmake``. + The command is executed by a call of the form:: + + execute_process( + COMMAND ${RunCMake_TEST_COMMAND} ${RunCMake_TEST_OPTIONS} + WORKING_DIRECTORY "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}" + [TIMEOUT "${RunCMake_TEST_TIMEOUT}"] + ... + ) + + Behavior may be customized by setting `RunCMake Variables`_ before + the call. + +``run_cmake_command(<case> <command> <args>...)`` + Sets ``RunCMake_TEST_COMMAND`` to ``<command>;<args>...`` + and calls ``run_cmake(<case>)``. + + This is useful to run an arbitrary command. + +``run_cmake_script(<case> <args>...)`` + Sets ``RunCMake_TEST_COMMAND`` to + ``${CMAKE_COMMAND};<args>...;-P;${RunCMake_SOURCE_DIR}/<case>.cmake`` + and calls ``run_cmake(<case>)``. + + This is useful to run CMake in script mode without configuring a project. + +``run_cmake_with_options(<case> <opts>...)`` + Sets ``RunCMake_TEST_OPTIONS`` to ``<opts>...`` + and calls ``run_cmake(<case>)``. + +``run_cmake_with_raw_args(<case> "<args>")`` + Calls ``run_cmake(<case>)`` with the underlying ``execute_process()`` + call extended with the content of ``<args>`` treated as literal source + code of CMake language command arguments:: + + execute_process( + COMMAND ${RunCMake_TEST_COMMAND} ${RunCMake_TEST_OPTIONS} <args> + ... + ) + + This is useful to pass arguments to the test command that cannot be + encoded in CMake language ``;``-separated lists. + +RunCMake Variables +================== + +The behavior of `RunCMake Commands`_ such as ``run_cmake()`` may be +customized by setting the following variables before a call. + +``RunCMake_GENERATOR`` + CMake generator to use when configuring projects. + This provided to ``RunCMakeTest.cmake`` scripts automatically + when they are executed, based on the CMake generator used to + configure the test suite. + + For some generators, additional variables are also provided: + + ``RunCMake_GENERATOR_PLATFORM`` + Specifies the ``CMAKE_GENERATOR_PLATFORM``. + + ``RunCMake_GENERATOR_TOOLSET`` + Specifies the ``CMAKE_GENERATOR_TOOLSET``. + + ``RunCMake_GENERATOR_INSTANCE`` + Specifies the ``CMAKE_GENERATOR_INSTANCE``. + +``RunCMake_GENERATOR_IS_MULTI_CONFIG`` + Boolean value indicating whether ``${RunCMake_GENERATOR}`` is a + multi-config generator. + This provided to ``RunCMakeTest.cmake`` scripts automatically + when they are executed, based on the CMake generator used to + configure the test suite. + +``RunCMake_SOURCE_DIR`` + Absolute path to the ``Tests/RunCMake/<Test>`` directory in + the CMake source tree. This provided to ``RunCMakeTest.cmake`` + scripts automatically when they are executed. + +``RunCMake_BINARY_DIR`` + Absolute path to the ``Tests/RunCMake/<Test>`` directory in + the CMake binary tree. This provided to ``RunCMakeTest.cmake`` + scripts automatically when they are executed. + +``RunCMake_TEST_SOURCE_DIR`` + Absolute path to the individual test case's source tree. + If not set, defaults to ``${RunCMake_SOURCE_DIR}``. + +``RunCMake_TEST_BINARY_DIR`` + Absolute path to the individual test case's binary tree. + If not set, defaults to ``${RunCMake_BINARY_DIR}/<case>-build``. + +``RunCMake_TEST_NO_CLEAN`` + Boolean value indicating whether ``run_cmake(<case>)`` should remove the + ``${RunCMake_TEST_BINARY_DIR}`` directory before running the test case. + If not set, or if set to a false value, the directory is removed. + + This is useful to run `Multi-Step Test Cases`_. + +``RunCMake_TEST_COMMAND`` + The command for ``run_cmake(<case>)`` to execute. + If not set, defaults to running CMake to generate a project:: + + ${CMAKE_COMMAND} ${RunCMake_TEST_SOURCE_DIR} \ + -G ${RunCMake_GENERATOR} ... -DRunCMake_TEST=<case> + +``RunCMake_TEST_COMMAND_WORKING_DIRECTORY`` + The working directory in which ``run_cmake(<case>)`` to execute its command. + If not set, defaults to ``${RunCMake_TEST_BINARY_DIR}``. + +``RunCMake_TEST_OPTIONS`` + Additional command-line options for ``run_cmake(<case>)`` to pass to + CMake when configuring a project with a default ``RunCMake_TEST_COMMAND``. + If not set, defaults to empty. + If ``RunCMake_TEST_COMMAND`` is set, ``RunCMake_TEST_OPTIONS`` is forced + to empty. + +``RunCMake_TEST_OUTPUT_MERGE`` + Boolean value indicating whether ``run_cmake(<case>)`` should redirect + the test process's ``stderr`` into its ``stdout``. + +``RunCMake_TEST_TIMEOUT`` + Specify a timeout, in seconds, for ``run_cmake(<case>)`` to pass to its + underlying ``execute_process()`` call using the ``TIMEOUT`` option. + +Multi-Step Test Cases +===================== + +Normally each ``run_cmake(<case>)`` call corresponds to one standalone +test case with its own build tree. However, some test cases may require +multiple steps to be performed in a single build tree. This can be +achieved as follows:: + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/example-build) + run_cmake(example) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(example-build ${CMAKE_COMMAND} --build . --config Debug) + endblock() + +In this example, ``block() ... endblock()`` is used to isolate the +variable settings from later cases. A single build tree is used for +all cases inside the block. The first step cleans the build tree and +runs CMake to configure the case's project. The second step runs +``cmake --build`` to drive the generated build system and merges the +build tool's ``stderr`` into its ``stdout``. Note that each call uses +a unique case name so that expected results can be expressed individually. + +Running a Test +============== + +Each call to ``add_RunCMake_test(Example)`` in `CMakeLists.txt`_ creates +a test named ``RunCMake.Example`` that may be run with ``ctest``:: + + $ ctest -R "^RunCMake\.Example$" To speed up local testing, you can choose to run only a subset of ``run_cmake()`` tests in a ``RunCMakeTest.cmake`` script by using the @@ -80,7 +263,7 @@ $ RunCMake_TEST_FILTER="^example" ctest -R '^RunCMake\.Example$' -This will only run subtests in ``RunCMake.Example`` that start with +This will only run cases in ``RunCMake.Example`` that start with ``example``. To speed up the process of creating a new ``RunCMake`` test, you can run a
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index 9bc510b..ea3099f 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake
@@ -217,6 +217,7 @@ "|[^\n]*offset in archive not a multiple of 8" "|[^\n]*from Time Machine by path" "|[^\n]*Bullseye Testing Technology" + ${RunCMake_TEST_EXTRA_IGNORE_LINE_REGEX} ")[^\n]*\n)+" ) if(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION) @@ -247,6 +248,7 @@ endif() endforeach() unset(RunCMake_TEST_FAILED) + unset(RunCMake_TEST_FAILURE_MESSAGE) if(RunCMake-check-file AND EXISTS ${top_src}/${RunCMake-check-file}) include(${top_src}/${RunCMake-check-file}) else() @@ -277,6 +279,9 @@ string(APPEND msg "Actual ${o}:\n${actual_${o}}\n") endif() endforeach() + if(RunCMake_TEST_FAILURE_MESSAGE) + string(APPEND msg "${RunCMake_TEST_FAILURE_MESSAGE}") + endif() message(SEND_ERROR "${test}${RunCMake_TEST_VARIANT_DESCRIPTION} - FAILED:\n${msg}") else() message(STATUS "${test}${RunCMake_TEST_VARIANT_DESCRIPTION} - PASSED")
diff --git a/Tests/RunCMake/RunCPack.cmake b/Tests/RunCMake/RunCPack.cmake new file mode 100644 index 0000000..b21eb26 --- /dev/null +++ b/Tests/RunCMake/RunCPack.cmake
@@ -0,0 +1,112 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(RunCMake) + +#[[ +set(RunPack_GENERATORS ...) +run_cpack(<case> + # general options + [CONFIG <config>] # Build/package given configuration (default "Release"). + [GENERATORS <gen>...] # Tell cpack to use the given generator(s). + [SAMPLE <sample>] # Use RunCPack/<sample> project (default <case>). + + # build step + [BUILD] # Build the test project before packaging. + + # package,cpack-<gen> steps + [PACKAGE] # Run cpack via buildsystem "package" target. + [NO_CPACK] # Do not run cpack directly. + + # verify step + [NO_VERIFY] # Do not run verify step. + [GLOB <glob>...] # Match expected package files with globbing patterns. + [VERIFY <command>...] # Run custom verification command on each package file. + ) +#]] +function(run_cpack case) + cmake_parse_arguments(PARSE_ARGV 1 run_cpack + # Zero-value + "BUILD;PACKAGE;NO_CPACK;NO_VERIFY" + # One-value + "CONFIG;SAMPLE" + # Multi-value + "GENERATORS;GLOB;VERIFY" + ) + + if(DEFINED RunCPack_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments:\n ${RunCPack_UNPARSED_ARGUMENTS}") + endif() + if(DEFINED RunCPack_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "Keywords missing values:\n ${RunCPack_KEYWORDS_MISSING_VALUES}") + endif() + + if(run_cpack_GENERATORS) + set(RunCPack_GENERATORS "${run_cpack_GENERATORS}") + elseif(NOT RunCPack_GENERATORS) + message(FATAL_ERROR "RunCPack_GENERATORS not defined by caller!") + endif() + + if(run_cpack_CONFIG) + set(RunCPack_CONFIG "${run_cpack_CONFIG}") + elseif(NOT RunCPack_CONFIG) + set(RunCPack_CONFIG "Release") + endif() + + if(run_cpack_SAMPLE) + set(RunCPack_SAMPLE "${run_cpack_SAMPLE}") + else() + set(RunCPack_SAMPLE "${case}") + endif() + + if(run_cpack_GLOB) + set(RunCPack_GLOB "${run_cpack_GLOB}") + endif() + + if(run_cpack_VERIFY) + set(RunCPack_VERIFY ${run_cpack_VERIFY}) + endif() + + # Configure the sample project. + set(RunCMake_TEST_SOURCE_DIR ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/RunCPack/${RunCPack_SAMPLE}) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + string(APPEND RunCMake_TEST_RAW_ARGS " \"-DCPACK_GENERATOR=${RunCPack_GENERATORS}\"") + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${RunCPack_CONFIG}) + endif() + run_cmake(${case}-cmake) + unset(RunCMake_TEST_RAW_ARGS) + set(RunCMake_TEST_NO_CLEAN 1) + + # Optionally build the project. + if(run_cpack_BUILD) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(${case}-build + "${CMAKE_COMMAND}" --build . --config "${RunCPack_CONFIG}") + unset(RunCMake_TEST_OUTPUT_MERGE) + endif() + + # Optionally package through the build system. + if(run_cpack_PACKAGE) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(${case}-package + "${CMAKE_COMMAND}" --build . --config "${RunCPack_CONFIG}" --target package) + unset(RunCMake_TEST_OUTPUT_MERGE) + endif() + + # Run cpack with each generator. + if(NOT run_cpack_NO_CPACK) + foreach(RunCPack_GENERATOR IN LISTS RunCPack_GENERATORS) + run_cmake_command(${case}-cpack-${RunCPack_GENERATOR} + "${CMAKE_CPACK_COMMAND}" -C "${RunCPack_CONFIG}" -G "${RunCPack_GENERATOR}") + endforeach() + endif() + + # Verify the resulting package files. + if(NOT run_cpack_NO_VERIFY) + set(RunCMake_TEST_RAW_ARGS " \"-Dglob=${RunCPack_GLOB}\" \"-Dverify=${RunCPack_VERIFY}\" -P \"${CMAKE_CURRENT_FUNCTION_LIST_DIR}/RunCPack/verify.cmake\"") + run_cmake_command(${case}-verify + "${CMAKE_COMMAND}" -Ddir=${RunCMake_TEST_BINARY_DIR}) + unset(RunCMake_TEST_RAW_ARGS) + endif() +endfunction()
diff --git a/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt b/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt new file mode 100644 index 0000000..affd4d4 --- /dev/null +++ b/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt
@@ -0,0 +1,124 @@ +cmake_minimum_required(VERSION 3.5) + +project(CPackWiXGenerator) + +add_library(mylib mylib.cpp) + +add_executable(my-libapp mylibapp.cpp) +target_link_libraries(my-libapp mylib) + +add_executable(my-other-app myotherapp.cpp) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/empty) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/empty + DESTINATION extras + COMPONENT extras) + +install(TARGETS mylib + ARCHIVE + DESTINATION lib + COMPONENT libraries) + +install(TARGETS my-libapp + RUNTIME + DESTINATION bin + COMPONENT applications) + +install(TARGETS my-other-app + RUNTIME + DESTINATION bin + COMPONENT applications2) + +install(FILES mylib.h "file with spaces.h" + DESTINATION include + COMPONENT headers) + +set(CPACK_GENERATOR "WIX") + +set(CPACK_PACKAGE_NAME "MyLib") +set(CPACK_PACKAGE_VENDOR "CMake.org") +set(CPACK_PACKAGE_CONTACT "somebody@cmake.org") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY + "MyLib - CPack Component Installation Example") + +set(CPACK_PACKAGE_VERSION_MAJOR "1") +set(CPACK_PACKAGE_VERSION_MINOR "0") +set(CPACK_PACKAGE_VERSION_PATCH "0") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example") + +if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64") + set(CPACK_WIX_ARCHITECTURE arm64) + set(CPACK_SYSTEM_NAME windows-arm64) +endif() + +set(CPACK_WIX_UPGRADE_GUID "BF20CE5E-7F7C-401D-8F7C-AB45E8D170E6") +set(CPACK_WIX_UNINSTALL "1") + +# Support non-interactive sessions (like CI). +set(CPACK_WIX_LIGHT_EXTRA_FLAGS "-sval") + +set(CPACK_PACKAGE_EXECUTABLES + "my-libapp" "CPack WiX Test" + "my-other-app" "Second CPack WiX Test" +) + +set(CPACK_CREATE_DESKTOP_LINKS + "my-libapp" + "my-other-app" +) + +set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/patch.xml") + +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.txt") + +set(CPACK_WIX_PROPERTY_ARPCOMMENTS "My Custom ARPCOMMENTS") +set(CPACK_WIX_PROPERTY_ARPHELPLINK "https://cmake.org") + +include(CPack) + +cpack_add_install_type(Full DISPLAY_NAME "Everything") +cpack_add_install_type(Developer) + +cpack_add_component_group(Runtime) + +cpack_add_component_group(Development + EXPANDED + DESCRIPTION "All of the tools you'll ever need to develop software") + +cpack_add_component(extras + DISPLAY_NAME "Extras" + DESCRIPTION "Extras" + GROUP Runtime + INSTALL_TYPES Full) + +cpack_add_component(applications + REQUIRED + DISPLAY_NAME "MyLib Application" + DESCRIPTION "An extremely useful application that makes use of MyLib" + GROUP Runtime + INSTALL_TYPES Full) + +cpack_add_component(applications2 + DISPLAY_NAME "MyLib Extra Application" + DESCRIPTION "Another extremely useful application that makes use of MyLib" + GROUP Runtime + INSTALL_TYPES Full) + +cpack_add_component(documentation + DISPLAY_NAME "MyLib Documentation" + DESCRIPTION "The extensive suite of MyLib Application documentation files" + GROUP Runtime + INSTALL_TYPES Full) + +cpack_add_component(libraries + DISPLAY_NAME "Libraries" + DESCRIPTION "Static libraries used to build programs with MyLib" + GROUP Development + INSTALL_TYPES Developer Full) + +cpack_add_component(headers + DISPLAY_NAME "C++ Headers" + DESCRIPTION "C/C++ header files for use with MyLib" + GROUP Development + DEPENDS libraries + INSTALL_TYPES Developer Full)
diff --git a/Tests/CPackWiXGenerator/file with spaces.h b/Tests/RunCMake/RunCPack/AppWiX/file with spaces.h similarity index 100% rename from Tests/CPackWiXGenerator/file with spaces.h rename to Tests/RunCMake/RunCPack/AppWiX/file with spaces.h
diff --git a/Tests/CPackWiXGenerator/license.txt b/Tests/RunCMake/RunCPack/AppWiX/license.txt similarity index 100% rename from Tests/CPackWiXGenerator/license.txt rename to Tests/RunCMake/RunCPack/AppWiX/license.txt
diff --git a/Tests/CPackWiXGenerator/mylib.cpp b/Tests/RunCMake/RunCPack/AppWiX/mylib.cpp similarity index 100% rename from Tests/CPackWiXGenerator/mylib.cpp rename to Tests/RunCMake/RunCPack/AppWiX/mylib.cpp
diff --git a/Tests/CPackWiXGenerator/mylib.h b/Tests/RunCMake/RunCPack/AppWiX/mylib.h similarity index 100% rename from Tests/CPackWiXGenerator/mylib.h rename to Tests/RunCMake/RunCPack/AppWiX/mylib.h
diff --git a/Tests/CPackWiXGenerator/mylibapp.cpp b/Tests/RunCMake/RunCPack/AppWiX/mylibapp.cpp similarity index 100% rename from Tests/CPackWiXGenerator/mylibapp.cpp rename to Tests/RunCMake/RunCPack/AppWiX/mylibapp.cpp
diff --git a/Tests/CPackWiXGenerator/myotherapp.cpp b/Tests/RunCMake/RunCPack/AppWiX/myotherapp.cpp similarity index 100% rename from Tests/CPackWiXGenerator/myotherapp.cpp rename to Tests/RunCMake/RunCPack/AppWiX/myotherapp.cpp
diff --git a/Tests/CPackWiXGenerator/patch.xml b/Tests/RunCMake/RunCPack/AppWiX/patch.xml similarity index 100% rename from Tests/CPackWiXGenerator/patch.xml rename to Tests/RunCMake/RunCPack/AppWiX/patch.xml
diff --git a/Tests/RunCMake/RunCPack/NuGetLib/CMakeLists.txt b/Tests/RunCMake/RunCPack/NuGetLib/CMakeLists.txt new file mode 100644 index 0000000..55ab638 --- /dev/null +++ b/Tests/RunCMake/RunCPack/NuGetLib/CMakeLists.txt
@@ -0,0 +1,34 @@ +# Support for framework-specific dependencies were introduced in 3.30 +# TODO: update this version req to 3.30 +cmake_minimum_required(VERSION 3.29.20240531) +project(CPackNugetGenerator) + +install(FILES notactuallyalib.dll DESTINATION lib/net4) +install(FILES alsonotalib.dll DESTINATION lib/net6.0) + +# Create NuGet package +set(CPACK_GENERATOR NuGet) +set(CPACK_PACKAGE_NAME GeneratorTest) +set(CPACK_PACKAGE_VERSION "1.2.3") +set(CPACK_PACKAGE_VENDOR "ACME Inc") +set(CPACK_NUGET_PACKAGE_OWNERS "ACME Inc") +set(CPACK_PACKAGE_DESCRIPTION "A NuGet package for testing CMake's CPack NuGet generator") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A test NuGet package") +set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.example.com") +set(CPACK_NUGET_PACKAGE_REPOSITORY_URL "https://github.com/example/nugetlib.git") +set(CPACK_NUGET_PACKAGE_REPOSITORY_TYPE git) +set(CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION "MIT") + +# Set up dependencies +set(CPACK_NUGET_PACKAGE_TFMS "net4;net6.0") +set(CPACK_NUGET_PACKAGE_DEPENDENCIES_net4 "Foo;Bar") +# NB: If a version number is omitted, the dependency will not be created +set(CPACK_NUGET_PACKAGE_DEPENDENCIES_net4_Foo_VERSION "1.23") +set(CPACK_NUGET_PACKAGE_DEPENDENCIES_net4_Bar_VERSION "4.3.2") +# NB: General dependencies (not framework-specific) go in this variable +set(CPACK_NUGET_PACKAGE_DEPENDENCIES "Baz") +set(CPACK_NUGET_PACKAGE_DEPENDENCIES_Baz_VERSION "9.8.6") +# NB: Since "net6.0" was listed but no dependencies have been specified, an empty group +# will be added to the nuspec file for this framework. This can be used to address `NU5128`_. + +include(CPack)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/RunCPack/NuGetLib/alsonotalib.dll similarity index 100% copy from Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake copy to Tests/RunCMake/RunCPack/NuGetLib/alsonotalib.dll
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/RunCPack/NuGetLib/notactuallyalib.dll similarity index 100% copy from Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake copy to Tests/RunCMake/RunCPack/NuGetLib/notactuallyalib.dll
diff --git a/Tests/RunCMake/RunCPack/verify.cmake b/Tests/RunCMake/RunCPack/verify.cmake new file mode 100644 index 0000000..26a52bc --- /dev/null +++ b/Tests/RunCMake/RunCPack/verify.cmake
@@ -0,0 +1,14 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +if(glob) + list(TRANSFORM glob PREPEND "${dir}/") + file(GLOB files RELATIVE "${dir}" ${glob}) + foreach(file IN LISTS files) + message(STATUS "${file}") + if(verify) + execute_process(COMMAND ${verify} ${file} COMMAND_ERROR_IS_FATAL ANY) + endif() + endforeach() + message(STATUS "") +endif()
diff --git a/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt index d593b92..4fb69be 100644 --- a/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt +++ b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt
@@ -1 +1 @@ -swiftc(.exe)? .* -output-file-map CMakeFiles(/|\\)greetings_default.dir(//|\\\\)output-file-map.json .* +swiftc(.exe)? .* -output-file-map CMakeFiles(/|\\)greetings_default.dir(/|\\)(Debug)?(/|\\)output-file-map.json .*
diff --git a/Tests/RunCMake/Swift/CompileCommands-check.cmake b/Tests/RunCMake/Swift/CompileCommands-check.cmake index 6450745..e33a027 100644 --- a/Tests/RunCMake/Swift/CompileCommands-check.cmake +++ b/Tests/RunCMake/Swift/CompileCommands-check.cmake
@@ -11,13 +11,13 @@ "directory": ".*(/Tests/RunCMake/Swift/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\Swift\\\\CompileCommands-build)", "command": ".*swiftc .* (\\")?.*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")? (\\")?.*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?", "file": ".*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)", - "output": "CMakeFiles/CompileCommandLib.dir/E.swift.o|CMakeFiles\\\\CompileCommandLib.dir\\\\E.swift.obj" + "output": "CMakeFiles/CompileCommandLib.dir/(Debug(/|\\))?E.swift.o|CMakeFiles\\\\CompileCommandLib.dir\\\\E.swift.obj" }, { "directory": ".*(/Tests/RunCMake/Swift/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\Swift\\\\CompileCommands-build)", "command": ".*swiftc .* (\\")?.*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")? (\\")?.*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?", "file": ".*/Tests/RunCMake/Swift/L.swift", - "output": "CMakeFiles/CompileCommandLib.dir/L.swift.o|CMakeFiles\\\\CompileCommandLib.dir\\\\L.swift.obj" + "output": "CMakeFiles/CompileCommandLib.dir/(Debug(/|\\))?L.swift.o|CMakeFiles\\\\CompileCommandLib.dir\\\\L.swift.obj" } ]$]==] )
diff --git a/Tests/RunCMake/Swift/EnableSwift.cmake b/Tests/RunCMake/Swift/EnableSwift.cmake new file mode 100644 index 0000000..19f297a --- /dev/null +++ b/Tests/RunCMake/Swift/EnableSwift.cmake
@@ -0,0 +1 @@ +enable_language(Swift)
diff --git a/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt b/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt index ec396d7..adea1f1 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(/|\\)L.o(bj)?.swift.rsp -.*swiftc(.exe)? -emit-library -static -o (libL.a|L.lib) @CMakeFiles(/|\\)L.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/ForceResponseFile.cmake b/Tests/RunCMake/Swift/ForceResponseFile.cmake index 7fd4636..880ff72 100644 --- a/Tests/RunCMake/Swift/ForceResponseFile.cmake +++ b/Tests/RunCMake/Swift/ForceResponseFile.cmake
@@ -2,8 +2,8 @@ cmake_policy(SET CMP0157 NEW) endif() -if(NOT CMAKE_GENERATOR STREQUAL "Ninja") - message(SEND_ERROR "this test must use Ninja generator, found ${CMAKE_GENERATOR} ") +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)
diff --git a/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt b/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt index efa3336..0131378 100644 --- a/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt +++ b/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt
@@ -1,2 +1 @@ -ninja explain: A.swiftmodule is dirty -ninja explain: (libB.a|B.lib) is dirty +ninja explain: (Debug(/|\\))?A.swiftmodule is dirty
diff --git a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt index 8e5b97c..7bc8c75 100644 --- a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt +++ b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt
@@ -1,3 +1,3 @@ -.*Building Swift object A.swiftmodule CMakeFiles(/|\\)A.dir(/|\\)a.swift.o(bj)? -.*Building Swift object B.swiftmodule CMakeFiles(/|\\)B.dir(/|\\)b.swift.o(bj)? -FAILED: B.swiftmodule CMakeFiles(/|\\)B.dir(/|\\)b.swift.o(bj)? +.*Building Swift Module 'A' with 1 source +.*Building Swift Module 'B' with 1 source +FAILED: (Debug(/|\\))?B.swiftmodule CMakeFiles(/|\\)B.dir(/|\\)(Debug(/|\\))?b.swift.o(bj)?
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake index 3711efb..716075e 100644 --- a/Tests/RunCMake/Swift/RunCMakeTest.cmake +++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -1,108 +1,138 @@ include(RunCMake) -if(RunCMake_GENERATOR STREQUAL Xcode) - if(XCODE_VERSION VERSION_LESS 6.1) - run_cmake(XcodeTooOld) - elseif(CMake_TEST_Swift) - run_cmake(CMP0157-NEW) - run_cmake(CMP0157-OLD) - run_cmake(CMP0157-WARN) +# Early bailouts. +if(RunCMake_GENERATOR STREQUAL "Xcode" AND XCODE_VERSION VERSION_LESS 6.1) + run_cmake(XcodeTooOld) + return() +elseif(NOT CMake_TEST_Swift) + return() +elseif(NOT RunCMake_GENERATOR MATCHES "^Ninja|^Xcode$") + run_cmake(NotSupported) + return() +endif() + +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release") +endif() + +block() + run_cmake(CMP0157-NEW) + run_cmake(CMP0157-WARN) + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0157-OLD-build) + + run_cmake(CMP0157-OLD) + + if(RunCMake_GENERATOR MATCHES "Ninja.*") + set(RunCMake_TEST_NO_CLEAN 1) + # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command + run_cmake_command(CMP0157-OLD-build ${CMAKE_COMMAND} --build . -- -n -v) endif() -elseif(RunCMake_GENERATOR STREQUAL Ninja) - if(CMake_TEST_Swift) +endblock() + +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftSimple-build) + run_cmake(SwiftSimple) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND + # Older Xcode versions didn't support Swift static libraries. + NOT (RunCMake_GENERATOR STREQUAL "Xcode" AND XCODE_VERSION VERSION_LESS 9.0)) + # Check that .swiftmodule files get their own directories + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(SwiftSimple-build-Debug ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(SwiftSimple-build-Release ${CMAKE_COMMAND} --build . --config Release) + + # Will fail if either path doesn't exist. Passing -r because Xcode + # generates .swiftmodule directories. + run_cmake_command(SwiftSimple-verify ${CMAKE_COMMAND} -E + rm -r Debug/L.swiftmodule Release/L.swiftmodule) + endif() +endblock() + +if(RunCMake_GENERATOR MATCHES "Ninja") + block() if (CMAKE_SYSTEM_NAME MATCHES "Windows") run_cmake_with_options(Win32ExecutableDisallowed) else() run_cmake_with_options(Win32ExecutableIgnored) - set(RunCMake_TEST_OPTIONS -DCMAKE_SYSTEM_NAME=Darwin) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_SYSTEM_NAME=Darwin) run_cmake(SwiftMultiArch) - unset(RunCMake_TEST_OPTIONS) endif() + endblock() - # Test that a second build with no changes does nothing. - block() - run_cmake(NoWorkToDo) + # Test that a second build with no changes does nothing. + block() + run_cmake(NoWorkToDo) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/NoWorkToDo-build) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(NoWorkToDo-build ${CMAKE_COMMAND} --build .) + run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain) + file(WRITE ${RunCMake_TEST_BINARY_DIR}/hello.swift "//No-op change\n") + run_cmake_command(NoWorkToDo-norelink ${CMAKE_COMMAND} --build . -- -d explain) + run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain) + endblock() + + # Test that intermediate static libraries are rebuilt when the public + # interface of their dependency changes + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/IncrementalSwift-build) + # Since files are modified during test, the files are created in the cmake + # file into the build directory + run_cmake(IncrementalSwift) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(IncrementalSwift-first ${CMAKE_COMMAND} --build .) + + # Modify public interface of libA requiring rebuild of libB + file(WRITE ${RunCMake_TEST_BINARY_DIR}/a.swift + "public func callA() -> Float { return 32.0 }\n") + + # Note: We still expect this to fail, but instead of failure at link time, + # it should fail while re-compiling libB because the function changed + run_cmake_command(IncrementalSwift-second ${CMAKE_COMMAND} --build . -- -d explain) + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CompileCommands-build) + run_cmake(CompileCommands) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(CompileCommands-check ${CMAKE_COMMAND} --build .) + endblock() + + block() + # Try enabling Swift with a static-library try-compile + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/StaticLibTryCompile-build) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY) + run_cmake(EnableSwift) + endblock() + + block() + # Try enabling Swift with an executable try-compile + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExecutableTryCompile-build) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_TRY_COMPILE_TARGET_TYPE=EXECUTABLE) + run_cmake(EnableSwift) + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ForceResponseFile-build) + run_cmake(ForceResponseFile) + set(RunCMake_TEST_NO_CLEAN 1) + # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling + run_cmake_command(ForceResponseFile-check ${CMAKE_COMMAND} --build . -- -vn) + endblock() + + block() + if(CMAKE_SYSTEM_NAME MATCHES Windows) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ImportLibraryFlags-build) + run_cmake(ImportLibraryFlags) set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/NoWorkToDo-build) - set(RunCMake_TEST_OUTPUT_MERGE 1) - run_cmake_command(NoWorkToDo-build ${CMAKE_COMMAND} --build .) - run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain) - file(WRITE ${RunCMake_TEST_BINARY_DIR}/hello.swift "//No-op change\n") - run_cmake_command(NoWorkToDo-norelink ${CMAKE_COMMAND} --build . -- -d explain) - run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain) - endblock() + run_cmake_command(ImportLibraryFlags-check ${CMAKE_COMMAND} --build . -- -n -v) + endif() + endblock() - # Test that intermediate static libraries are rebuilt when the public - # interface of their dependency changes - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/IncrementalSwift-build) - # Since files are modified during test, the files are created in the cmake - # file into the build directory - run_cmake(IncrementalSwift) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(IncrementalSwift-first ${CMAKE_COMMAND} --build .) - - # Modify public interface of libA requiring rebuild of libB - file(WRITE ${RunCMake_TEST_BINARY_DIR}/a.swift - "public func callA() -> Float { return 32.0 }\n") - - # Note: We still expect this to fail, but instead of failure at link time, - # it should fail while re-compiling libB because the function changed - run_cmake_command(IncrementalSwift-second ${CMAKE_COMMAND} --build . -- -d explain) - endblock() - - block() - run_cmake(CMP0157-NEW) - run_cmake(CMP0157-WARN) - - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0157-OLD-build) - run_cmake(CMP0157-OLD) - set(RunCMake_TEST_NO_CLEAN 1) - # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command - run_cmake_command(CMP0157-OLD-build ${CMAKE_COMMAND} --build . -- -n -v) - endblock() - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CompileCommands-build) - run_cmake(CompileCommands) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(CompileCommands-check ${CMAKE_COMMAND} --build .) - endblock() - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ForceResponseFile-build) - run_cmake(ForceResponseFile) - set(RunCMake_TEST_NO_CLEAN 1) - # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling - run_cmake_command(ForceResponseFile-check ${CMAKE_COMMAND} --build . -- -vn) - endblock() - - block() - if(CMAKE_SYSTEM_NAME MATCHES Windows) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ImportLibraryFlags-build) - run_cmake(ImportLibraryFlags) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(ImportLibraryFlags-check ${CMAKE_COMMAND} --build . -- -n -v) - endif() - 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() - endif() -elseif(RunCMake_GENERATOR STREQUAL "Ninja Multi-Config") - if(CMake_TEST_Swift) - set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release") - run_cmake(SwiftSimple) - - run_cmake(CMP0157-NEW) - run_cmake(CMP0157-OLD) - run_cmake(CMP0157-WARN) - unset(RunCMake_TEST_OPTIONS) - endif() -else() - run_cmake(NotSupported) + 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() endif()
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 6b462d0..415f404 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -42,6 +42,7 @@ \* CMP0156 \* CMP0157 \* CMP0160 + \* CMP0162 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake index 689b35f..b5b195a 100644 --- a/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake +++ b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake
@@ -9,7 +9,7 @@ file(STRINGS "${vcProjectFile}" lines) foreach(line IN LISTS lines) - if(line MATCHES "^ *<RuntimeLibrary>([^<>]+)</RuntimeLibrary>") + if(line MATCHES "^ *<RuntimeLibrary>([^<>]*)</RuntimeLibrary>") set(rtl_actual "${CMAKE_MATCH_1}") if(NOT "${rtl_actual}" STREQUAL "${rtl_expect}") set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has RuntimeLibrary '${rtl_actual}', not '${rtl_expect}'.")
diff --git a/Tests/RunCMake/VS10Project/VsCustomProps-check.cmake b/Tests/RunCMake/VS10Project/VsCustomProps-check.cmake index 22a3df0..ad585d5 100644 --- a/Tests/RunCMake/VS10Project/VsCustomProps-check.cmake +++ b/Tests/RunCMake/VS10Project/VsCustomProps-check.cmake
@@ -1,25 +1,30 @@ -set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj") -if(NOT EXISTS "${vcProjectFile}") - set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.") - return() -endif() - -set(importFound FALSE) - -set(props_file "${RunCMake_SOURCE_DIR}/my.props") -file(TO_NATIVE_PATH "${props_file}" check_file) -file(STRINGS "${vcProjectFile}" lines) -foreach(line IN LISTS lines) - if(line MATCHES "^ *<Import Project=\"([^\"]+)\".*Label=\"([^\"]+)\".*$") - if("${CMAKE_MATCH_1}" STREQUAL "${check_file}" AND - "${CMAKE_MATCH_2}" STREQUAL "LocalAppDataPlatform") - message(STATUS "foo.vcxproj is importing ${check_file}") - set(importFound TRUE) - endif() +macro(check_custom_prop suffix) + set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj${suffix}") + if(NOT EXISTS "${vcProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.") + return() endif() -endforeach() -if(NOT importFound) - set(RunCMake_TEST_FAILED "Import of custom .props file not found.") - return() -endif() + set(importFound FALSE) + + set(props_file "${RunCMake_SOURCE_DIR}/my.props") + file(TO_NATIVE_PATH "${props_file}" check_file) + file(STRINGS "${vcProjectFile}" lines) + foreach(line IN LISTS lines) + if(line MATCHES "^ *<Import Project=\"([^\"]+)\".*Label=\"([^\"]+)\".*$") + if("${CMAKE_MATCH_1}" STREQUAL "${check_file}" AND + "${CMAKE_MATCH_2}" STREQUAL "LocalAppDataPlatform") + message(STATUS "foo.vcxproj${suffix} is importing ${check_file}") + set(importFound TRUE) + endif() + endif() + endforeach() + + if(NOT importFound) + set(RunCMake_TEST_FAILED "Import of custom .props file not found.") + return() + endif() +endmacro() + +check_custom_prop("") +check_custom_prop(".filters")
diff --git a/Tests/RunCMake/VS10Project/VsCustomProps.cmake b/Tests/RunCMake/VS10Project/VsCustomProps.cmake index fbbcfcf..d6c905f 100644 --- a/Tests/RunCMake/VS10Project/VsCustomProps.cmake +++ b/Tests/RunCMake/VS10Project/VsCustomProps.cmake
@@ -4,4 +4,5 @@ set(props_file "${CMAKE_CURRENT_SOURCE_DIR}/my.props") set_target_properties(foo PROPERTIES - VS_USER_PROPS "${props_file}") + VS_USER_PROPS "${props_file}" + VS_FILTER_PROPS "${props_file}")
diff --git a/Tests/RunCMake/VS10Project/VsJustMyCode-check.cmake b/Tests/RunCMake/VS10Project/VsJustMyCode-check.cmake index 7119976..e76d931 100644 --- a/Tests/RunCMake/VS10Project/VsJustMyCode-check.cmake +++ b/Tests/RunCMake/VS10Project/VsJustMyCode-check.cmake
@@ -9,7 +9,7 @@ file(STRINGS "${vcProjectFile}" lines) foreach(line IN LISTS lines) - if(line MATCHES "^ *<SupportJustMyCode>([^<>]+)</SupportJustMyCode>") + if(line MATCHES "^ *<SupportJustMyCode>([^<>]*)</SupportJustMyCode>") set(jmc_actual "${CMAKE_MATCH_1}") if(NOT "${jmc_actual}" STREQUAL "${jmc_expect}") set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has <SupportJustMyCode> '${jmc_actual}', not '${jmc_expect}'.")
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-check.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-check.cmake new file mode 100644 index 0000000..2ab8960 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-check.cmake
@@ -0,0 +1,8 @@ +include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake) + +UseDebugLibraries_check(default "true" "false") +UseDebugLibraries_check(defaultCLR "true" "false") +UseDebugLibraries_check(defaultUtil "true" "false") +UseDebugLibraries_check(defaultRTL "true" "false") +UseDebugLibraries_check(ALL_BUILD "true" "false") +UseDebugLibraries_check(ZERO_CHECK "true" "false")
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt new file mode 100644 index 0000000..9d1b69e --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD-stderr.txt
@@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0091-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0091 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD.cmake new file mode 100644 index 0000000..d8142ee --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMP0091-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0091 OLD) +include(Default-CMP0162-NEW.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMakeLists.txt b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMakeLists.txt new file mode 100644 index 0000000..392312d --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.29) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-NEW-check.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-NEW-check.cmake new file mode 100644 index 0000000..b5e6fae --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-NEW-check.cmake
@@ -0,0 +1,8 @@ +include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake) + +UseDebugLibraries_check(default "true" "false") +UseDebugLibraries_check(defaultCLR "true" "false") +UseDebugLibraries_check(defaultUtil "true" "false") +UseDebugLibraries_check(defaultRTL "false" "false") +UseDebugLibraries_check(ALL_BUILD "true" "false") +UseDebugLibraries_check(ZERO_CHECK "true" "false")
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-NEW.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-NEW.cmake new file mode 100644 index 0000000..be6869d --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-NEW.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0162 NEW) +include(Default-common.cmake)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-WARN-check.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-WARN-check.cmake new file mode 100644 index 0000000..673144e --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-WARN-check.cmake
@@ -0,0 +1,8 @@ +include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake) + +UseDebugLibraries_check(default "" "") +UseDebugLibraries_check(defaultCLR "" "") +UseDebugLibraries_check(defaultUtil "" "") +UseDebugLibraries_check(defaultRTL "" "") +UseDebugLibraries_check(ALL_BUILD "" "") +UseDebugLibraries_check(ZERO_CHECK "" "")
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-WARN.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-WARN.cmake new file mode 100644 index 0000000..7696944 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-CMP0162-WARN.cmake
@@ -0,0 +1,2 @@ +# Leave CMP0162 unset. +include(Default-common.cmake)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-common.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-common.cmake new file mode 100644 index 0000000..6afcfb6 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Default-common.cmake
@@ -0,0 +1,10 @@ +set(CMAKE_CONFIGURATION_TYPES Debug Release) +enable_language(CXX) + +# Test several generator code paths covering different target types. +add_library(default empty.cxx) +add_library(defaultCLR empty.cxx) +set_property(TARGET defaultCLR PROPERTY COMMON_LANGUAGE_RUNTIME "") +add_library(defaultRTL empty.cxx) +set_property(TARGET defaultRTL PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") +add_custom_target(defaultUtil)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-NEW-check.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-NEW-check.cmake new file mode 100644 index 0000000..832a7f3 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-NEW-check.cmake
@@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Explicit-check-common.cmake)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-NEW.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-NEW.cmake new file mode 100644 index 0000000..a5f8d4a --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-NEW.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0162 NEW) +include(Explicit-common.cmake)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-OLD-check.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-OLD-check.cmake new file mode 100644 index 0000000..832a7f3 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-OLD-check.cmake
@@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Explicit-check-common.cmake)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-OLD.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-OLD.cmake new file mode 100644 index 0000000..1e60c52 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-CMP0162-OLD.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0162 OLD) +include(Explicit-common.cmake)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-check-common.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-check-common.cmake new file mode 100644 index 0000000..385b798 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-check-common.cmake
@@ -0,0 +1,10 @@ +include(${CMAKE_CURRENT_LIST_DIR}/check-common.cmake) + +UseDebugLibraries_check(empty "" "") +UseDebugLibraries_check(emptyCLR "" "") +UseDebugLibraries_check(emptyUtil "" "") +UseDebugLibraries_check(genex "true" "false") +UseDebugLibraries_check(genexCLR "true" "false") +UseDebugLibraries_check(genexUtil "true" "false") +UseDebugLibraries_check(ALL_BUILD "false" "false") +UseDebugLibraries_check(ZERO_CHECK "false" "false")
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-common.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-common.cmake new file mode 100644 index 0000000..3433745 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/Explicit-common.cmake
@@ -0,0 +1,20 @@ +set(CMAKE_CONFIGURATION_TYPES Debug Release) +enable_language(CXX) + +# An empty string suppresses generation of the setting. +set(CMAKE_VS_USE_DEBUG_LIBRARIES "") +add_library(empty empty.cxx) +add_library(emptyCLR empty.cxx) +set_property(TARGET emptyCLR PROPERTY COMMON_LANGUAGE_RUNTIME "") +add_custom_target(emptyUtil) + +# A generator expression can encode per-config values. +set(CMAKE_VS_USE_DEBUG_LIBRARIES "$<CONFIG:Debug>") +add_library(genex empty.cxx) +add_library(genexCLR empty.cxx) +set_property(TARGET genexCLR PROPERTY COMMON_LANGUAGE_RUNTIME "") +add_custom_target(genexUtil) + +# The last setting in the top-level directcory affects +# the builtin targets like ALL_BUILD and ZERO_CHECK. +set(CMAKE_VS_USE_DEBUG_LIBRARIES 0)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/RunCMakeTest.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/RunCMakeTest.cmake new file mode 100644 index 0000000..57402eb --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/RunCMakeTest.cmake
@@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.29) +include(RunCMake) + +run_cmake(CMP0091-OLD) +run_cmake(Default-CMP0162-NEW) +run_cmake(Default-CMP0162-WARN) +run_cmake(Explicit-CMP0162-NEW) +run_cmake(Explicit-CMP0162-OLD)
diff --git a/Tests/RunCMake/VS10ProjectUseDebugLibraries/check-common.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/check-common.cmake new file mode 100644 index 0000000..311c8a7 --- /dev/null +++ b/Tests/RunCMake/VS10ProjectUseDebugLibraries/check-common.cmake
@@ -0,0 +1,42 @@ +cmake_policy(SET CMP0140 NEW) +function(UseDebugLibraries_check tgt udl_expect_debug udl_expect_release) + set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj") + if(NOT EXISTS "${vcProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.") + return() + endif() + + set(have_udl_debug 0) + set(have_udl_release 0) + set(inConfig "") + + file(STRINGS "${vcProjectFile}" lines) + foreach(line IN LISTS lines) + if(line MATCHES [[^ *<PropertyGroup Condition="'\$\(Configuration\)\|\$\(Platform\)'=='([^"|]+)\|[^"]+" Label="Configuration">.*$]]) + string(TOLOWER "${CMAKE_MATCH_1}" inConfig) + elseif(inConfig) + if(line MATCHES "^ *</PropertyGroup>.*$") + set(inConfig "") + elseif(line MATCHES "^ *<UseDebugLibraries>([^<>]+)</UseDebugLibraries>") + set(udl_actual "${CMAKE_MATCH_1}") + set(have_udl_${inConfig} 1) + if (NOT "${udl_expect_${inConfig}}" STREQUAL "") + if(NOT "${udl_actual}" STREQUAL "${udl_expect_${inConfig}}") + string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has ${inConfig} UseDebugLibraries '${udl_actual}', not '${udl_expect_${inConfig}}'.\n") + endif() + else() + string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has ${inConfig} UseDebugLibraries '${udl_actual}', but should not have one.\n") + endif() + unset(udl_actual) + endif() + endif() + endforeach() + + if(NOT have_udl_debug AND NOT "${udl_expect_debug}" STREQUAL "") + string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a debug UseDebugLibraries field, but should have one.\n") + endif() + if(NOT have_udl_release AND NOT "${udl_expect_release}" STREQUAL "") + string(APPEND RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a release UseDebugLibraries field, but should have one.\n") + endif() + return(PROPAGATE RunCMake_TEST_FAILED) +endfunction()
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/VS10ProjectUseDebugLibraries/empty.cxx similarity index 100% copy from Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake copy to Tests/RunCMake/VS10ProjectUseDebugLibraries/empty.cxx
diff --git a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake index 134821d..8737c9a 100644 --- a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake +++ b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake
@@ -13,9 +13,7 @@ run_cmake(StartupProjectMissing) run_cmake(AddPackageToDefault) -if(NOT NO_USE_FOLDERS) - run_cmake(StartupProjectUseFolders) - run_cmake(CMP0143-WARN) - run_cmake_with_options(CMP0143-OLD "-DCMAKE_POLICY_DEFAULT_CMP0143=OLD") - run_cmake_with_options(CMP0143-NEW "-DCMAKE_POLICY_DEFAULT_CMP0143=NEW") -endif() +run_cmake(StartupProjectUseFolders) +run_cmake(CMP0143-WARN) +run_cmake_with_options(CMP0143-OLD "-DCMAKE_POLICY_DEFAULT_CMP0143=OLD") +run_cmake_with_options(CMP0143-NEW "-DCMAKE_POLICY_DEFAULT_CMP0143=NEW")
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-check.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-check.cmake index 4d5508d..081e09a 100644 --- a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-check.cmake +++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-check.cmake
@@ -9,7 +9,7 @@ if(filename MATCHES "^(.*)(\\.[a-z]+)$") set(header_filename "${CMAKE_MATCH_1}") endif() - set(expected_contents "#include <${header_filename}> // IWYU pragma: associated\n") + set(expected_contents "/* NOLINTNEXTLINE(misc-header-include-cycle,misc-include-cleaner) */\n#include <${header_filename}> /* IWYU pragma: associated */\n") file(READ "${full_filename}" actual_contents) if(NOT actual_contents STREQUAL expected_contents)
diff --git a/Tests/RunCMake/XcodeProject/ArchsStandard-stdout.txt b/Tests/RunCMake/XcodeProject/ArchsStandard-stdout.txt new file mode 100644 index 0000000..460b33c --- /dev/null +++ b/Tests/RunCMake/XcodeProject/ArchsStandard-stdout.txt
@@ -0,0 +1 @@ +-- Detecting C compiler ABI info - done
diff --git a/Tests/RunCMake/XcodeProject/ArchsStandard.cmake b/Tests/RunCMake/XcodeProject/ArchsStandard.cmake new file mode 100644 index 0000000..56d9e64 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/ArchsStandard.cmake
@@ -0,0 +1,4 @@ +if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "$(ARCHS_STANDARD)") + message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES is not $(ARCHS_STANDARD)") +endif() +enable_language(C)
diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake index c124950..5e22666 100644 --- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
@@ -9,6 +9,8 @@ endfunction() RunClean() +run_cmake_with_options(ArchsStandard "-DCMAKE_OSX_ARCHITECTURES=$(ARCHS_STANDARD)") + run_cmake(ExplicitCMakeLists) run_cmake(ImplicitCMakeLists) run_cmake(InterfaceLibSources)
diff --git a/Tests/RunCMake/XcodeProject/XcodeFileType-check.cmake b/Tests/RunCMake/XcodeProject/XcodeFileType-check.cmake index 1847bc9..48b4680 100644 --- a/Tests/RunCMake/XcodeProject/XcodeFileType-check.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeFileType-check.cmake
@@ -1,4 +1,4 @@ -set(expect-default "explicitFileType = sourcecode") +set(expect-default "explicitFileType = default") set(expect-explicit "explicitFileType = sourcecode.c.h") set(expect-lastKnown "lastKnownFileType = sourcecode.c.h") foreach(src default explicit lastKnown)
diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration-check.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration-check.cmake new file mode 100644 index 0000000..4e139d4 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration-check.cmake
@@ -0,0 +1,19 @@ + +set(schema "${RunCMake_TEST_BINARY_DIR}/XcodeSchemaGeneration.xcodeproj/xcshareddata/xcschemes/foo.xcscheme") + +if(NOT EXISTS "${schema}") + set(RunCMake_TEST_FAILED "Generated schema ${schema} does not exist.") + return() +endif() + +execute_process(COMMAND + /usr/bin/xmllint --xpath "//Scheme/ProfileAction/BuildableProductRunnable" ${schema} + OUTPUT_VARIABLE stdout + ERROR_VARIABLE stderr + RESULT_VARIABLE exit_code + ERROR_STRIP_TRAILING_WHITESPACE +) +if(exit_code) + set(RunCMake_TEST_FAILED "Failed to find BuildableProductRunnable for profile action: ${stderr}") + return() +endif()
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt index 547fb1c..5741ae5 100644 --- a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt +++ b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt
@@ -1,9 +1,3 @@ -CMake Error at BadByproduct.cmake:2 \(add_custom_command\): - BYPRODUCTS containing a "#" is not allowed. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) - - CMake Error at BadByproduct.cmake:3 \(add_custom_command\): BYPRODUCTS containing a "<" is not allowed. Call Stack \(most recent call first\): @@ -17,7 +11,7 @@ ( CMake Error at BadByproduct.cmake:5 \(add_custom_command\): - BYPRODUCTS containing a "#" is not allowed. + BYPRODUCTS containing a ">" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct.cmake b/Tests/RunCMake/add_custom_command/BadByproduct.cmake index cf00f5b..4fe0b82 100644 --- a/Tests/RunCMake/add_custom_command/BadByproduct.cmake +++ b/Tests/RunCMake/add_custom_command/BadByproduct.cmake
@@ -1,8 +1,8 @@ set(CMAKE_DISABLE_SOURCE_CHANGES ON) -add_custom_command(OUTPUT a BYPRODUCTS "a#") + add_custom_command(OUTPUT b BYPRODUCTS "a<") add_custom_command(OUTPUT c BYPRODUCTS "a>") -add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/#") +add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/$<ANGLE-R>") add_custom_command(OUTPUT e BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/f) add_custom_command(OUTPUT f BYPRODUCTS "$<TARGET_PROPERTY:prop>") add_custom_command(OUTPUT h BYPRODUCTS "$<OUTPUT_CONFIG:h>")
diff --git a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt index 2e43568..9d25949 100644 --- a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt +++ b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt
@@ -1,9 +1,3 @@ -CMake Error at BadOutput.cmake:2 \(add_custom_command\): - OUTPUT containing a "#" is not allowed. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) - - CMake Error at BadOutput.cmake:3 \(add_custom_command\): OUTPUT containing a "<" is not allowed. Call Stack \(most recent call first\): @@ -17,7 +11,7 @@ ( CMake Error at BadOutput.cmake:5 \(add_custom_command\): - OUTPUT containing a "#" is not allowed. + OUTPUT containing a ">" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_command/BadOutput.cmake b/Tests/RunCMake/add_custom_command/BadOutput.cmake index f04bdd1..0f6bd07 100644 --- a/Tests/RunCMake/add_custom_command/BadOutput.cmake +++ b/Tests/RunCMake/add_custom_command/BadOutput.cmake
@@ -1,8 +1,8 @@ set(CMAKE_DISABLE_SOURCE_CHANGES ON) -add_custom_command(OUTPUT "a#" COMMAND a) + add_custom_command(OUTPUT "a<" COMMAND b) add_custom_command(OUTPUT "a>" COMMAND c) -add_custom_command(OUTPUT "$<CONFIG>/#" COMMAND d) +add_custom_command(OUTPUT "$<CONFIG>/$<ANGLE-R>" COMMAND d) add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/e COMMAND f) add_custom_command(OUTPUT "$<TARGET_PROPERTY:prop>" COMMAND g) add_custom_command(OUTPUT "$<OUTPUT_CONFIG:h>" COMMAND h)
diff --git a/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt b/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt index bf49657..76708c4 100644 --- a/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt +++ b/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt
@@ -1 +1 @@ -lorem ipsum, 01 +"lorem ipsum, 01"
diff --git a/Tests/RunCMake/add_custom_command/CommentGenex.cmake b/Tests/RunCMake/add_custom_command/CommentGenex.cmake index f517392..b3931f4 100644 --- a/Tests/RunCMake/add_custom_command/CommentGenex.cmake +++ b/Tests/RunCMake/add_custom_command/CommentGenex.cmake
@@ -3,7 +3,7 @@ add_custom_command( OUTPUT out.txt COMMAND ${CMAKE_COMMAND} -E echo true - COMMENT "$<TARGET_PROPERTY:helper,MY_TEXT>$<COMMA> $<STREQUAL:foo,bar>$<EQUAL:42,42>" + COMMENT "$<QUOTE>$<TARGET_PROPERTY:helper,MY_TEXT>$<COMMA> $<STREQUAL:foo,bar>$<EQUAL:42,42>$<QUOTE>" ) set_property(SOURCE out.txt PROPERTY SYMBOLIC 1) add_custom_target(main ALL DEPENDS out.txt)
diff --git a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake index 713b269..46e7bae 100644 --- a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
@@ -19,7 +19,7 @@ run_cmake(TargetLiteralQuotes) run_cmake(TargetNotInDir) -if(${RunCMake_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])") +if(RunCMake_GENERATOR MATCHES "Visual Studio") run_cmake(RemoveEmptyCommands) endif()
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt index da9af49..b734ddd 100644 --- a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt +++ b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt
@@ -1,9 +1,3 @@ -CMake Error at BadByproduct.cmake:2 \(add_custom_target\): - BYPRODUCTS containing a "#" is not allowed. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) - - CMake Error at BadByproduct.cmake:3 \(add_custom_target\): BYPRODUCTS containing a "<" is not allowed. Call Stack \(most recent call first\): @@ -17,7 +11,7 @@ ( CMake Error at BadByproduct.cmake:5 \(add_custom_target\): - BYPRODUCTS containing a "#" is not allowed. + BYPRODUCTS containing a ">" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct.cmake b/Tests/RunCMake/add_custom_target/BadByproduct.cmake index c317b83..d29c02f 100644 --- a/Tests/RunCMake/add_custom_target/BadByproduct.cmake +++ b/Tests/RunCMake/add_custom_target/BadByproduct.cmake
@@ -1,8 +1,8 @@ set(CMAKE_DISABLE_SOURCE_CHANGES ON) -add_custom_target(a BYPRODUCTS "a#" COMMAND b) + add_custom_target(c BYPRODUCTS "a<" COMMAND d) add_custom_target(e BYPRODUCTS "a>" COMMAND f) -add_custom_target(g BYPRODUCTS "$<CONFIG>/#" COMMAND h) +add_custom_target(g BYPRODUCTS "$<CONFIG>/$<ANGLE-R>" COMMAND h) add_custom_target(i BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/j COMMAND k) add_custom_target(l BYPRODUCTS "$<TARGET_PROPERTY:prop>" COMMAND m) add_custom_target(n BYPRODUCTS "$<OUTPUT_CONFIG:n>" COMMAND o)
diff --git a/Tests/RunCMake/add_library/RunCMakeTest.cmake b/Tests/RunCMake/add_library/RunCMakeTest.cmake index 3283625..26b94f8 100644 --- a/Tests/RunCMake/add_library/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_library/RunCMakeTest.cmake
@@ -22,3 +22,7 @@ run_cmake(SHAREDwithNoSourcesButLinkObjects) run_cmake(MODULEwithNoSourcesButLinkObjects) run_cmake(UNKNOWNwithNoSourcesButLinkObjects) + +run_cmake(TARGET_SUPPORTS_SHARED_LIBS_CMP0164_OLD) +run_cmake(TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW) +run_cmake(TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW-result.txt similarity index 100% copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt copy to Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW-result.txt
diff --git a/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW-stderr.txt b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW-stderr.txt new file mode 100644 index 0000000..2a13236 --- /dev/null +++ b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW-stderr.txt
@@ -0,0 +1,5 @@ +^CMake Error at TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW.cmake:[0-9]+ \(add_library\): + ADD_LIBRARY called with SHARED option but the target platform does not + support dynamic linking. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW.cmake b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW.cmake new file mode 100644 index 0000000..0f4d34b --- /dev/null +++ b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_NEW.cmake
@@ -0,0 +1,4 @@ +enable_language(CXX) +cmake_policy(SET CMP0164 NEW) +set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) +add_library(someLib SHARED test.cpp)
diff --git a/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_OLD.cmake b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_OLD.cmake new file mode 100644 index 0000000..3c90403 --- /dev/null +++ b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_OLD.cmake
@@ -0,0 +1,9 @@ +enable_language(CXX) +cmake_policy(SET CMP0164 OLD) +set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) + +add_library(someLib SHARED test.cpp) +get_target_property(someLibType someLib TYPE) +if(NOT someLibType STREQUAL "STATIC_LIBRARY") + message(FATAL_ERROR "expected type does not match") +endif()
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 new file mode 100644 index 0000000..89a61c0 --- /dev/null +++ b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN-stderr.txt
@@ -0,0 +1,7 @@ +^CMake Warning \(dev\) 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.$
diff --git a/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN.cmake b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN.cmake new file mode 100644 index 0000000..1f5cc84 --- /dev/null +++ b/Tests/RunCMake/add_library/TARGET_SUPPORTS_SHARED_LIBS_CMP0164_WARN.cmake
@@ -0,0 +1,3 @@ +enable_language(CXX) +set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) +add_library(someLib SHARED test.cpp)
diff --git a/Tests/RunCMake/add_subdirectory/ChangeConfigMulti.cmake b/Tests/RunCMake/add_subdirectory/ChangeConfigMulti.cmake new file mode 100644 index 0000000..449c5c8 --- /dev/null +++ b/Tests/RunCMake/add_subdirectory/ChangeConfigMulti.cmake
@@ -0,0 +1 @@ +add_subdirectory(ChangeConfigMulti)
diff --git a/Tests/RunCMake/add_subdirectory/ChangeConfigMulti/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/ChangeConfigMulti/CMakeLists.txt new file mode 100644 index 0000000..3ea7fc5 --- /dev/null +++ b/Tests/RunCMake/add_subdirectory/ChangeConfigMulti/CMakeLists.txt
@@ -0,0 +1 @@ +set(CMAKE_CONFIGURATION_TYPES NotDebug NotRelease)
diff --git a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake index 3c70d07..801abae 100644 --- a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake
@@ -7,6 +7,10 @@ run_cmake(System) unset(RunCMake_TEST_OPTIONS) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + run_cmake_with_options(ChangeConfigMulti "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release") +endif() + macro(run_cmake_install case) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) set(RunCMake_TEST_NO_CLEAN 1)
diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake index 0b3576d..76dff4f 100644 --- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
@@ -16,7 +16,7 @@ run_cmake(UserFallbackScript) -if(RunCMake_GENERATOR MATCHES "^Visual Studio " AND NOT RunCMake_GENERATOR STREQUAL "Visual Studio 9 2008") +if(RunCMake_GENERATOR MATCHES "Visual Studio") run_cmake(VsMSBuild) else() run_cmake(VsMSBuildMissing)
diff --git a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt new file mode 100644 index 0000000..bddd3ab --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set-stderr.txt
@@ -0,0 +1,6 @@ +CMake Warning \(dev\) 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:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake new file mode 100644 index 0000000..a002297 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-set.cmake
@@ -0,0 +1,11 @@ +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + +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-unset.cmake b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-unset.cmake new file mode 100644 index 0000000..be758c2 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/CxxImportStd-unset.cmake
@@ -0,0 +1,8 @@ +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 ()
diff --git a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt new file mode 100644 index 0000000..0207835 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set-stderr.txt
@@ -0,0 +1,6 @@ +CMake Warning \(dev\) 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:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set.cmake b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set.cmake new file mode 100644 index 0000000..74b3f93 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-set.cmake
@@ -0,0 +1,11 @@ +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-unset.cmake b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-unset.cmake new file mode 100644 index 0000000..c60595f --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/ExportPackageDependencies-unset.cmake
@@ -0,0 +1,8 @@ +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 ()
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/cmake_language/Experimental/Unknown-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/cmake_language/Experimental/Unknown-result.txt
diff --git a/Tests/RunCMake/cmake_language/Experimental/Unknown-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/Unknown-stderr.txt new file mode 100644 index 0000000..ca7c058 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/Unknown-stderr.txt
@@ -0,0 +1,4 @@ +CMake Error at Experimental/Unknown.cmake:1 \(cmake_language\): + cmake_language Experimental feature name "Unknown" does not exist. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/cmake_language/Experimental/Unknown.cmake b/Tests/RunCMake/cmake_language/Experimental/Unknown.cmake new file mode 100644 index 0000000..d5e3047 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/Unknown.cmake
@@ -0,0 +1,8 @@ +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "Unknown" + feature_present) + +if (NOT feature_present STREQUAL "") + message(FATAL_ERROR + "Got a result for the `Unknown` experimental feature.") +endif ()
diff --git a/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set-stderr.txt b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set-stderr.txt new file mode 100644 index 0000000..d6f8788 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set-stderr.txt
@@ -0,0 +1,6 @@ +CMake Warning \(dev\) at Experimental/WindowsKernelModeDriver-set.cmake:4 \(cmake_language\): + CMake's Windows kernel-mode driver support is experimental. It is meant + only for experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake new file mode 100644 index 0000000..f942d95 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake
@@ -0,0 +1,11 @@ +set(CMAKE_EXPERIMENTAL_WINDOWS_KERNEL_MODE_DRIVER + "5c2d848d-4efa-4529-a768-efd57171bf68") + +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "WindowsKernelModeDriver" + feature_present) + +if (NOT feature_present STREQUAL "TRUE") + message(FATAL_ERROR + "Expected the `WindowsKernelModeDriver` feature to be enabled.") +endif ()
diff --git a/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-unset.cmake b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-unset.cmake new file mode 100644 index 0000000..7aee0d3 --- /dev/null +++ b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-unset.cmake
@@ -0,0 +1,8 @@ +cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED + "WindowsKernelModeDriver" + feature_present) + +if (NOT feature_present STREQUAL "FALSE") + message(FATAL_ERROR + "Expected the `WindowsKernelModeDriver` feature to be disabled.") +endif ()
diff --git a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake index 5fb2de6..fe3da79 100644 --- a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake
@@ -156,3 +156,11 @@ -DCMAKE_MESSAGE_LOG_LEVEL=TRACE -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake ) + +run_cmake(Experimental/CxxImportStd-set) +run_cmake(Experimental/CxxImportStd-unset) +run_cmake(Experimental/ExportPackageDependencies-set) +run_cmake(Experimental/ExportPackageDependencies-unset) +run_cmake(Experimental/WindowsKernelModeDriver-set) +run_cmake(Experimental/WindowsKernelModeDriver-unset) +run_cmake(Experimental/Unknown)
diff --git a/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in b/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in index 1babd72..da7ec1a 100644 --- a/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestCoverage@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt b/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt index 1a45994..1973b2a 100644 --- a/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt +++ b/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt
@@ -1,3 +1,2 @@ -Error: a cycle exists in the test dependency graph for the test "cyclicCleanup". -Please fix the cycle and run ctest again. -No tests were found!!! +^Error: a cycle exists in the test dependency graph for the test "cyclicCleanup"\. +Please fix the cycle and run ctest again\.$
diff --git a/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt b/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt index 2aba6c9..aee735a 100644 --- a/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt +++ b/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt
@@ -1,3 +1,2 @@ -Error: a cycle exists in the test dependency graph for the test "cyclicSetup". -Please fix the cycle and run ctest again. -No tests were found!!!$ +^Error: a cycle exists in the test dependency graph for the test "cyclicSetup"\. +Please fix the cycle and run ctest again\.$
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt index a8f10b5..74f37c0 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:? '?-no-site-'?.*|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 4825d7a..ad6dcf4 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:? '?-no-site-'?.*|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 a8f10b5..74f37c0 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:? '?-no-site-'?.*|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/CDashSubmitVerbose-stdout.txt b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stdout.txt index 11a4edf..d0c4e1a 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stdout.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stdout.txt
@@ -1 +1 @@ -Upload file: .* to http:\/\/-no-site-\?FileName=test-site___test-build-name___.*-Experimental___XML___Configure.xml&build=test-build-name&site=test-site&stamp=.*-Experimental&subproject=mysubproj&MD5=.* Size: .* +Upload file: .* to http:\/\/badhostname.invalid\?FileName=test-site___test-build-name___.*-Experimental___XML___Configure.xml&build=test-build-name&site=test-site&stamp=.*-Experimental&subproject=mysubproj&MD5=.* Size: .*
diff --git a/Tests/RunCMake/ctest_submit/CMakeLists.txt.in b/Tests/RunCMake/ctest_submit/CMakeLists.txt.in index 96e6c13..5ee64be 100644 --- a/Tests/RunCMake/ctest_submit/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_submit/CMakeLists.txt.in
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestSubmit@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_submit/CTestConfig.cmake.in b/Tests/RunCMake/ctest_submit/CTestConfig.cmake.in index c35397c..140e4be 100644 --- a/Tests/RunCMake/ctest_submit/CTestConfig.cmake.in +++ b/Tests/RunCMake/ctest_submit/CTestConfig.cmake.in
@@ -3,7 +3,3 @@ # so that any ctest_submit calls fail with an error message. set(CTEST_DROP_METHOD "@CASE_DROP_METHOD@") set(CTEST_DROP_SITE "@CASE_DROP_SITE@") - -# do not use proxy for lookup of invalid site (DNS failure by proxy looks -# different than DNS failure without proxy) -set(ENV{no_proxy} "$ENV{no_proxy},@CASE_DROP_SITE@")
diff --git a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt index a8f10b5..74f37c0 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:? '?-no-site-'?.*|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-stdout.txt b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stdout.txt index 929b254..a8f4759 100644 --- a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stdout.txt +++ b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stdout.txt
@@ -1 +1 @@ -Upload file: .* to http:\/\/-no-site-\?FileName=test-site___test-build-name___.*-Experimental___XML___RunCMakeTest.cmake&MD5=.* Size: .* +Upload file: .* to http:\/\/badhostname.invalid\?FileName=test-site___test-build-name___.*-Experimental___XML___RunCMakeTest.cmake&MD5=.* Size: .*
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-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/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stdout.txt new file mode 100644 index 0000000..4f5120e --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfiguration from CMAKE_TLS_VERIFY:TLSVerify:OFF +SetCTestConfiguration:TLSVerify:OFF +.* + Set CURLOPT_SSL_VERIFYPEER to off
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-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/ctest_submit/FailDrop-TLSVerify-OFF-env-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stdout.txt new file mode 100644 index 0000000..582030a --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfiguration from ENV{CMAKE_TLS_VERIFY}:TLSVerify:OFF +SetCTestConfiguration:TLSVerify:OFF +.* + Set CURLOPT_SSL_VERIFYPEER to off
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-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/ctest_submit/FailDrop-TLSVerify-OFF-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stdout.txt new file mode 100644 index 0000000..9053f6c --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfigurationFromCMakeVariable:TLSVerify:CTEST_TLS_VERIFY +SetCTestConfiguration:TLSVerify:OFF +.* + Set CURLOPT_SSL_VERIFYPEER to off
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-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/ctest_submit/FailDrop-TLSVerify-ON-cmake-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stdout.txt new file mode 100644 index 0000000..015728c --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfiguration from CMAKE_TLS_VERIFY:TLSVerify:ON +SetCTestConfiguration:TLSVerify:ON +.* + Set CURLOPT_SSL_VERIFYPEER to on
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-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/ctest_submit/FailDrop-TLSVerify-ON-env-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stdout.txt new file mode 100644 index 0000000..add2b4c --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfiguration from ENV{CMAKE_TLS_VERIFY}:TLSVerify:ON +SetCTestConfiguration:TLSVerify:ON +.* + Set CURLOPT_SSL_VERIFYPEER to on
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-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/ctest_submit/FailDrop-TLSVerify-ON-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stdout.txt new file mode 100644 index 0000000..c2764c3 --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfigurationFromCMakeVariable:TLSVerify:CTEST_TLS_VERIFY +SetCTestConfiguration:TLSVerify:ON +.* + Set CURLOPT_SSL_VERIFYPEER to on
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-result.txt
@@ -0,0 +1 @@ +(-1|255)
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 new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-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/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stdout.txt new file mode 100644 index 0000000..ed3677a --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfiguration from CMAKE_TLS_VERSION:TLSVersion:1\.1 +SetCTestConfiguration:TLSVersion:1\.1 +.* + Set CURLOPT_SSLVERSION to CURL_SSLVERSION_TLSv1_1
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-result.txt
@@ -0,0 +1 @@ +(-1|255)
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 new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-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/ctest_submit/FailDrop-TLSVersion-1.1-env-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stdout.txt new file mode 100644 index 0000000..c31b4bc --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfiguration from ENV{CMAKE_TLS_VERSION}:TLSVersion:1\.1 +SetCTestConfiguration:TLSVersion:1\.1 +.* + Set CURLOPT_SSLVERSION to CURL_SSLVERSION_TLSv1_1
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-result.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-result.txt
@@ -0,0 +1 @@ +(-1|255)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt new file mode 100644 index 0000000..e3df62f --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-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/ctest_submit/FailDrop-TLSVersion-1.1-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stdout.txt new file mode 100644 index 0000000..be83798 --- /dev/null +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stdout.txt
@@ -0,0 +1,4 @@ +SetCTestConfigurationFromCMakeVariable:TLSVersion:CTEST_TLS_VERSION +SetCTestConfiguration:TLSVersion:1\.1 +.* + Set CURLOPT_SSLVERSION to CURL_SSLVERSION_TLSv1_1
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt index f52d2d8..b83da8d 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:? '?-no-site-'?.*|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-http-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-http-stdout.txt index c9111b0..bd7d6b6 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-http-stdout.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-http-stdout.txt
@@ -1,2 +1,2 @@ Submit files - SubmitURL: http://-no-site- + SubmitURL: http://badhostname.invalid
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt index 259f7d0..e3df62f 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:? '?-no-site-'?|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-https-stdout.txt b/Tests/RunCMake/ctest_submit/FailDrop-https-stdout.txt index 2c67eb9..f97f63c 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-https-stdout.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-https-stdout.txt
@@ -1,2 +1,2 @@ Submit files - SubmitURL: https://-no-site- + SubmitURL: https://badhostname.invalid
diff --git a/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt index 0020a0f..86cd817 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:? '?-no-site-'?.*|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/RunCMakeTest.cmake b/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake index 78856b4..3c77ddb 100644 --- a/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_submit/RunCMakeTest.cmake
@@ -2,9 +2,13 @@ # Default case parameters. set(CASE_DROP_METHOD "http") -set(CASE_DROP_SITE "-no-site-") +set(CASE_DROP_SITE "badhostname.invalid") set(CASE_CTEST_SUBMIT_ARGS "") +set(CASE_TEST_PREFIX_CODE "") +# Do not use any proxy for lookup of an invalid site. +# DNS failure by proxy looks different than DNS failure without proxy. +set(ENV{no_proxy} "$ENV{no_proxy},badhostname.invalid") function(run_ctest_submit CASE_NAME) set(CASE_CTEST_SUBMIT_ARGS "${ARGN}") @@ -51,3 +55,30 @@ run_ctest_submit_FailDrop(http) run_ctest_submit_FailDrop(https) +block() + set(CASE_DROP_METHOD "https") + set(CASE_TEST_PREFIX_CODE "set(CTEST_TLS_VERSION 1.1)") + run_ctest(FailDrop-TLSVersion-1.1 -VV) + set(CASE_TEST_PREFIX_CODE "set(CMAKE_TLS_VERSION 1.1)") # Test fallback to CMake variable. + run_ctest(FailDrop-TLSVersion-1.1-cmake -VV) + set(ENV{CMAKE_TLS_VERSION} 1.1) # Test fallback to env variable. + set(CASE_TEST_PREFIX_CODE "") + run_ctest(FailDrop-TLSVersion-1.1-env -VV) + unset(ENV{CMAKE_TLS_VERSION}) + set(CASE_TEST_PREFIX_CODE "set(CTEST_TLS_VERIFY ON)") + run_ctest(FailDrop-TLSVerify-ON -VV) + set(CASE_TEST_PREFIX_CODE "set(CMAKE_TLS_VERIFY ON)") # Test fallback to CMake variable. + run_ctest(FailDrop-TLSVerify-ON-cmake -VV) + set(ENV{CMAKE_TLS_VERIFY} ON) # Test fallback to env variable. + set(CASE_TEST_PREFIX_CODE "") + run_ctest(FailDrop-TLSVerify-ON-env -VV) + unset(ENV{CMAKE_TLS_VERIFY}) + set(CASE_TEST_PREFIX_CODE "set(CTEST_TLS_VERIFY OFF)") + run_ctest(FailDrop-TLSVerify-OFF -VV) + set(CASE_TEST_PREFIX_CODE "set(CMAKE_TLS_VERIFY OFF)") # Test fallback to CMake variable. + run_ctest(FailDrop-TLSVerify-OFF-cmake -VV) + set(ENV{CMAKE_TLS_VERIFY} OFF) # Test fallback to env variable. + set(CASE_TEST_PREFIX_CODE "") + run_ctest(FailDrop-TLSVerify-OFF-env -VV) + unset(ENV{CMAKE_TLS_VERIFY}) +endblock()
diff --git a/Tests/RunCMake/ctest_submit/test.cmake.in b/Tests/RunCMake/ctest_submit/test.cmake.in index 0f4885f..6026c35 100644 --- a/Tests/RunCMake/ctest_submit/test.cmake.in +++ b/Tests/RunCMake/ctest_submit/test.cmake.in
@@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.5) +@CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_upload/UploadQuiet-check.cmake b/Tests/RunCMake/ctest_upload/UploadQuiet-check.cmake new file mode 100644 index 0000000..e45aa73 --- /dev/null +++ b/Tests/RunCMake/ctest_upload/UploadQuiet-check.cmake
@@ -0,0 +1,12 @@ +file(GLOB upload_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Upload.xml") +if(upload_xml_file) + file(READ "${upload_xml_file}" upload_xml LIMIT 4096) + if(NOT upload_xml MATCHES "<Time>") + string(REPLACE "\n" "\n " upload_xml " ${upload_xml}") + set(RunCMake_TEST_FAILED + "Upload.xml does not contain a <Time> attribute:\n${upload_xml}" + ) + endif() +else() + set(RunCMake_TEST_FAILED "Upload.xml not found") +endif()
diff --git a/Tests/RunCMake/detect_jobserver.c b/Tests/RunCMake/detect_jobserver.c index 8cbfe2e..67cc7db 100644 --- a/Tests/RunCMake/detect_jobserver.c +++ b/Tests/RunCMake/detect_jobserver.c
@@ -118,7 +118,6 @@ { int read_fd; int write_fd; - const char* path; // First try to parse as "R,W" file descriptors if (sscanf(jobserver, "%d,%d", &read_fd, &write_fd) == 2) { @@ -127,7 +126,7 @@ // Then try to parse as "fifo:PATH" if (strncmp(jobserver, "fifo:", 5) == 0) { - path = jobserver + 5; + const char* path = jobserver + 5; read_fd = open(path, O_RDONLY); write_fd = open(path, O_WRONLY); return test_fd(read_fd, write_fd, message);
diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt index 9627872..b1b0642 100644 --- a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt +++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt
@@ -1,4 +1,4 @@ CMake Error at .*AnyCommandAbnormalExit.cmake:[0-9]+ \(execute_process\): execute_process failed command indexes: - 1: "Abnormal exit with child return code: Segmentation fault + 1: "Abnormal exit with child return code:
diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake index 1017e0f..ef63778 100644 --- a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake +++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake
@@ -1,5 +1,4 @@ -execute_process(COMMAND "${Python_EXECUTABLE}" -c - "import os; os.kill(os.getpid(),11)" +execute_process(COMMAND "${EXIT_CRASH_EXE}" COMMAND ${CMAKE_COMMAND} -E true COMMAND_ERROR_IS_FATAL ANY )
diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake index e4a125d..1786d53 100644 --- a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake
@@ -1,12 +1,10 @@ -execute_process(COMMAND "${Python_EXECUTABLE}" -c - "import os; os.kill(os.getpid(),11)" +execute_process(COMMAND "${EXIT_CRASH_EXE}" COMMAND ${CMAKE_COMMAND} -E true RESULT_VARIABLE result ) if(result EQUAL "0") - execute_process(COMMAND "${Python_EXECUTABLE}" -c - "import os; os.kill(os.getpid(),11)" + execute_process(COMMAND "${EXIT_CRASH_EXE}" COMMAND ${CMAKE_COMMAND} -E true COMMAND_ERROR_IS_FATAL LAST )
diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt index c915e58..6db023b 100644 --- a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt
@@ -1,2 +1,2 @@ CMake Error at .*LastCommandAbnormalExit-2.cmake:[0-9]+ \(execute_process\): - execute_process Abnormal exit: Segmentation fault + execute_process Abnormal exit:
diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake index 6c3fbf8..a0c3aaf 100644 --- a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake
@@ -1,13 +1,11 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E true - COMMAND "${Python_EXECUTABLE}" -c - "import os; os.kill(os.getpid(),11)" + COMMAND "${EXIT_CRASH_EXE}" RESULT_VARIABLE result ) if(NOT result EQUAL "0") execute_process(COMMAND ${CMAKE_COMMAND} -E true - COMMAND "${Python_EXECUTABLE}" -c - "import os; os.kill(os.getpid(),11)" + COMMAND "${EXIT_CRASH_EXE}" COMMAND_ERROR_IS_FATAL LAST ) endif()
diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index cd5e41b..7ee75af 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
@@ -41,10 +41,10 @@ run_cmake_command(StdoutStderrNoexist ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/StderrNoexist.cmake) run_cmake_command(InOutErrDirectory ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/InOutErrDirectory.cmake) -if(UNIX AND Python_EXECUTABLE) - run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake) - run_cmake_command(LastCommandAbnormalExit-1 ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-1.cmake) - run_cmake_command(LastCommandAbnormalExit-2 ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-2.cmake) +if(EXIT_CRASH_EXE) + run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake) + run_cmake_command(LastCommandAbnormalExit-1 ${CMAKE_COMMAND} -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-1.cmake) + run_cmake_command(LastCommandAbnormalExit-2 ${CMAKE_COMMAND} -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-2.cmake) endif() if(WIN32 OR CYGWIN)
diff --git a/Tests/RunCMake/exit_crash.c b/Tests/RunCMake/exit_crash.c new file mode 100644 index 0000000..324ffad --- /dev/null +++ b/Tests/RunCMake/exit_crash.c
@@ -0,0 +1,14 @@ +int main(int argc, const char* argv[]) +{ +#ifndef __clang_analyzer__ /* Suppress clang-analyzer warnings */ + /* Construct an invalid address that cannot be predicted by the + compiler/optimizer, and that is not NULL (which is undefined + behavior to dereference). */ + volatile int* invalidAddress = 0; + invalidAddress += argc ? 1 : 2; + (void)argv; + /* Write to the invalid address to cause SIGSEGV or similar. */ + *invalidAddress = 0; +#endif + return 0; +}
diff --git a/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake b/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake index d757eea..5e0310b 100644 --- a/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake
@@ -11,6 +11,7 @@ run_cmake(netrc-bad) run_cmake(tls-cainfo-not-set) run_cmake(tls-verify-not-set) +run_cmake(TLS_VERSION-missing) run_cmake(pass-not-set) run_cmake(no-save-hash) @@ -25,7 +26,14 @@ run_cmake(bad-hostname) endif() +run_cmake_with_options(TLS_VERSION-bad) +if(CMake_TEST_TLS_VERIFY_URL_BAD) + run_cmake_with_options(TLS_VERIFY-bad -Durl=${CMake_TEST_TLS_VERIFY_URL_BAD}) +endif() + if(CMake_TEST_TLS_VERIFY_URL) - run_cmake(TLS_VERIFY-bad) run_cmake_with_options(TLS_VERIFY-good -Durl=${CMake_TEST_TLS_VERIFY_URL}) + if(CMake_TEST_TLS_VERSION) + run_cmake_with_options(TLS_VERSION-good -Durl=${CMake_TEST_TLS_VERIFY_URL} -Dtls_version=${CMake_TEST_TLS_VERSION}) + endif() endif()
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt index 8f5d437..fbff3b9 100644 --- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt
@@ -1 +1,7 @@ --- (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- def-0: 0;"No error" +-- env-0: 0;"No error" +-- env-1: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- var-0: 0;"No error" +-- var-1: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- opt-0: 0;"No error" +-- opt-1: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error")
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake index 333f990..7d50ece 100644 --- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake
@@ -1,6 +1,35 @@ -file(DOWNLOAD https://expired.badssl.com TLS_VERIFY 1 STATUS status LOG log) -message(STATUS "${status}") -list(GET status 0 code) -if(NOT code MATCHES "^(35|60)$") - message("${log}") -endif() +function(download case) + # URL with semantics like https://expired.badssl.com is provided by caller + file(DOWNLOAD ${url} ${ARGN} STATUS status LOG log) + message(STATUS "${case}: ${status}") + if(case MATCHES "1$" AND NOT status MATCHES "^(35|60);") + message("${log}") + endif() +endfunction() + +# The default is OFF. +unset(ENV{CMAKE_TLS_VERIFY}) +unset(CMAKE_TLS_VERIFY) +download(def-0) + +# The environment variable overrides the default. +set(ENV{CMAKE_TLS_VERIFY} 0) +download(env-0) +set(ENV{CMAKE_TLS_VERIFY} 1) +download(env-1) + +# The cmake variable overrides the environment variable. +set(ENV{CMAKE_TLS_VERIFY} 1) +set(CMAKE_TLS_VERIFY 0) +download(var-0) +set(ENV{CMAKE_TLS_VERIFY} 0) +set(CMAKE_TLS_VERIFY 1) +download(var-1) + +# The explicit argument overrides the cmake variable and the environment variable. +set(ENV{CMAKE_TLS_VERIFY} 1) +set(CMAKE_TLS_VERIFY 1) +download(opt-0 TLS_VERIFY 0) +set(ENV{CMAKE_TLS_VERIFY} 0) +set(CMAKE_TLS_VERIFY 0) +download(opt-1 TLS_VERIFY 1)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-result.txt
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stderr.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stderr.txt new file mode 100644 index 0000000..421c8cf --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stderr.txt
@@ -0,0 +1,14 @@ +^CMake Error at TLS_VERSION-bad\.cmake:[0-9]+ \(file\): + file DOWNLOAD given unknown TLS/SSL version bad-env +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at TLS_VERSION-bad\.cmake:[0-9]+ \(file\): + file DOWNLOAD given unknown TLS/SSL version bad-var +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at TLS_VERSION-bad\.cmake:[0-9]+ \(file\): + file DOWNLOAD given unknown TLS/SSL version bad-arg +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake new file mode 100644 index 0000000..51ae4a2 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake
@@ -0,0 +1,10 @@ +# The environment variable provides a default. +set(ENV{CMAKE_TLS_VERSION} bad-env) +file(DOWNLOAD "" TLS_VERIFY 1 STATUS status LOG log) + +# The cmake variable overrides the environment variable. +set(CMAKE_TLS_VERSION bad-var) +file(DOWNLOAD "" TLS_VERIFY 1 STATUS status LOG log) + +# The explicit argument overrides the cmake variable. +file(DOWNLOAD "" TLS_VERSION bad-arg TLS_VERIFY 1 STATUS status LOG log)
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-good.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-good.cmake new file mode 100644 index 0000000..fcbf650 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-good.cmake
@@ -0,0 +1,6 @@ +file(DOWNLOAD ${url} TLS_VERSION "${tls_version}" TLS_VERIFY 1 STATUS status LOG log) +message(STATUS "${status}") +list(GET status 0 code) +if(NOT code EQUAL 0) + message("${log}") +endif()
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing-result.txt
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing-stderr.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing-stderr.txt new file mode 100644 index 0000000..05d9d90 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at TLS_VERSION-missing\.cmake:[0-9]+ \(file\): + file DOWNLOAD missing value for TLS_VERSION\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing.cmake new file mode 100644 index 0000000..29fcd96 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-missing.cmake
@@ -0,0 +1 @@ +file(DOWNLOAD "" "" TLS_VERSION)
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake index a68607e..f7ede51 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
@@ -42,6 +42,7 @@ run_install_test(macos-unresolved) run_install_test(macos-conflict) run_install_test(macos-notfile) + run_install_test(macos-parent-rpath-propagation) run_install_test(file-filter) endif() run_cmake(project)
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-parent-rpath-propagation.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-parent-rpath-propagation.cmake new file mode 100644 index 0000000..43df621 --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-parent-rpath-propagation.cmake
@@ -0,0 +1,44 @@ +enable_language(C) + +# bin/exe (RPATH = "lib1:lib2:lib3") +# ^ +# | +# lib1/libone.dylib (RPATH erased) +# ^ +# | +# lib2/libtwo.dylib (RPATH erased) +# ^ +# | +# lib3/libthree.dylib (RPATH erased) +# GET_RUNTIME_DEPENDENCIES(bin/exe) should resolve all three libraries + +set(TEST_SOURCE_DIR "macos/parent-rpath-propagation") + +add_library(three SHARED "${TEST_SOURCE_DIR}/three.c") + +add_library(two SHARED "${TEST_SOURCE_DIR}/two.c") +target_link_libraries(two PRIVATE three) + +add_library(one SHARED "${TEST_SOURCE_DIR}/one.c") +target_link_libraries(one PRIVATE two) + +add_executable(exe "${TEST_SOURCE_DIR}/main.c") +target_link_libraries(exe PUBLIC one) + +set_property(TARGET exe PROPERTY INSTALL_RPATH + @loader_path/../lib1 + @loader_path/../lib2 + @loader_path/../lib3 +) + +install(TARGETS exe DESTINATION bin) +install(TARGETS one DESTINATION lib1) +install(TARGETS two DESTINATION lib2) +install(TARGETS three DESTINATION lib3) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + ) + ]])
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/main.c b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/main.c new file mode 100644 index 0000000..fc02afa --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/main.c
@@ -0,0 +1,8 @@ +extern void one(void); + +int main(void) +{ + one(); + + return 0; +}
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/one.c b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/one.c new file mode 100644 index 0000000..0c480cc --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/one.c
@@ -0,0 +1,6 @@ +extern void two(void); + +void one(void) +{ + two(); +}
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/three.c b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/three.c new file mode 100644 index 0000000..0be5f47 --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/three.c
@@ -0,0 +1,3 @@ +void three(void) +{ +}
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/two.c b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/two.c new file mode 100644 index 0000000..370baf7 --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos/parent-rpath-propagation/two.c
@@ -0,0 +1,6 @@ +extern void three(void); + +void two(void) +{ + three(); +}
diff --git a/Tests/RunCMake/file-RPATH/CMakeLists.txt b/Tests/RunCMake/file-RPATH/CMakeLists.txt new file mode 100644 index 0000000..94e43ba --- /dev/null +++ b/Tests/RunCMake/file-RPATH/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/file-RPATH/LargeELF.c b/Tests/RunCMake/file-RPATH/LargeELF.c new file mode 100644 index 0000000..263c2f3 --- /dev/null +++ b/Tests/RunCMake/file-RPATH/LargeELF.c
@@ -0,0 +1,26 @@ +/* Create more than 65536 ELF sections. */ + +/* clang-format off */ +#define C0(i) int v##i __attribute__((section("s" #i))) +#define C1(i) C0(i##0); C0(i##1); C0(i##2); C0(i##3); C0(i##4); \ + C0(i##5); C0(i##6); C0(i##7); C0(i##8); C0(i##9) +#define C2(i) C1(i##0); C1(i##1); C1(i##2); C1(i##3); C1(i##4); \ + C1(i##5); C1(i##6); C1(i##7); C1(i##8); C1(i##9) +#define C3(i) C2(i##0); C2(i##1); C2(i##2); C2(i##3); C2(i##4); \ + C2(i##5); C2(i##6); C2(i##7); C2(i##8); C2(i##9) +#define C4(i) C3(i##0); C3(i##1); C3(i##2); C3(i##3); C3(i##4); \ + C3(i##5); C3(i##6); C3(i##7); C3(i##8); C3(i##9) +/* clang-format on */ + +C4(1); +C4(2); +C4(3); +C4(4); +C4(5); +C4(6); +C4(7); + +int main(void) +{ + return 0; +}
diff --git a/Tests/RunCMake/file-RPATH/LargeELF.cmake b/Tests/RunCMake/file-RPATH/LargeELF.cmake new file mode 100644 index 0000000..2eb813e --- /dev/null +++ b/Tests/RunCMake/file-RPATH/LargeELF.cmake
@@ -0,0 +1,5 @@ +enable_language(C) + +add_executable(LargeELF LargeELF.c) +set_property(TARGET LargeELF PROPERTY INSTALL_RPATH "/test") +install(TARGETS LargeELF)
diff --git a/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake b/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake index 525df09..028fa11 100644 --- a/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake
@@ -16,3 +16,15 @@ run_cmake_command(TextSetEmpty ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextSetEmpty.cmake) run_cmake_command(TextRemove ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextRemove.cmake) + +# Test install RPATH for ELF files with more than 65536 sections. +# This is supported only by certain platforms/toolchains, so run +# this case only if explicitly enabled. +if(CMake_TEST_ELF_LARGE) + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LargeELF-build) + run_cmake_with_options(LargeELF -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_BINARY_DIR}/root) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(LargeELF-build ${CMAKE_COMMAND} --build . --target install) + endblock() +endif()
diff --git a/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt b/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt index 2247aa6..6d70016 100644 --- a/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt +++ b/Tests/RunCMake/file/LOCK-error-no-path-stderr.txt
@@ -1,4 +1,4 @@ CMake Error at LOCK-error-no-path.cmake:[0-9]+ \(file\): - file must be called with at least two arguments. + sub-command LOCK requires at least two arguments\. Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index c75e062..be8ee7c 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake
@@ -12,6 +12,7 @@ run_cmake(UPLOAD-netrc-bad) run_cmake(UPLOAD-tls-cainfo-not-set) run_cmake(UPLOAD-tls-verify-not-set) +run_cmake(UPLOAD-TLS_VERSION-missing) run_cmake(UPLOAD-pass-not-set) run_cmake(INSTALL-DIRECTORY) run_cmake(INSTALL-FILES_FROM_DIR)
diff --git a/Tests/RunCMake/file/TOUCH-stderr.txt b/Tests/RunCMake/file/TOUCH-stderr.txt deleted file mode 100644 index 9f31676..0000000 --- a/Tests/RunCMake/file/TOUCH-stderr.txt +++ /dev/null
@@ -1,9 +0,0 @@ -^CMake Error at TOUCH\.cmake:[0-9]+ \(file\): - file must be called with at least two arguments\. -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at TOUCH\.cmake:[0-9]+ \(file\): - file must be called with at least two arguments\. -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file/TOUCH.cmake b/Tests/RunCMake/file/TOUCH.cmake index 8931eb5..c92a962 100644 --- a/Tests/RunCMake/file/TOUCH.cmake +++ b/Tests/RunCMake/file/TOUCH.cmake
@@ -12,5 +12,6 @@ endif() file(REMOVE "${file}") +# Empty arguments used to be an error, but this is valid since CMake 3.30 file(TOUCH) file(TOUCH_NOCREATE)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing-result.txt similarity index 100% rename from Tests/RunCMake/file/TOUCH-result.txt rename to Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing-result.txt
diff --git a/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing-stderr.txt b/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing-stderr.txt new file mode 100644 index 0000000..d61aba7 --- /dev/null +++ b/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Error at UPLOAD-TLS_VERSION-missing\.cmake:[0-9]+ \(file\): + file UPLOAD missing value for TLS_VERSION\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing.cmake b/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing.cmake new file mode 100644 index 0000000..dff9a9c --- /dev/null +++ b/Tests/RunCMake/file/UPLOAD-TLS_VERSION-missing.cmake
@@ -0,0 +1 @@ +file(UPLOAD "" "" TLS_VERSION)
diff --git a/Tests/RunCMake/find_package/CMP0167-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0167-NEW-stderr.txt new file mode 100644 index 0000000..be9b26c --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0167-NEW-stderr.txt
@@ -0,0 +1,4 @@ +^CMake Warning at CMP0167-NEW\.cmake:[0-9]+ \(find_package\): + No "FindBoost\.cmake" found in CMAKE_MODULE_PATH\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/CMP0167-NEW.cmake b/Tests/RunCMake/find_package/CMP0167-NEW.cmake new file mode 100644 index 0000000..4924a34 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0167-NEW.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0167 NEW) +set(_FindBoost_testing TRUE) +find_package(Boost MODULE) + +if(_FindBoost_included) + message(FATAL_ERROR "FindBoost.cmake erroneously included") +endif()
diff --git a/Tests/RunCMake/find_package/CMP0167-OLD.cmake b/Tests/RunCMake/find_package/CMP0167-OLD.cmake new file mode 100644 index 0000000..bc607f8 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0167-OLD.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0167 OLD) +set(_FindBoost_testing TRUE) +find_package(Boost MODULE) + +if(NOT _FindBoost_included) + message(FATAL_ERROR "FindBoost.cmake not included") +endif()
diff --git a/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt new file mode 100644 index 0000000..72b5225 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0167-WARN-stderr.txt
@@ -0,0 +1,8 @@ +CMake Warning \(dev\) 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\.$
diff --git a/Tests/RunCMake/find_package/CMP0167-WARN.cmake b/Tests/RunCMake/find_package/CMP0167-WARN.cmake new file mode 100644 index 0000000..5853a56 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0167-WARN.cmake
@@ -0,0 +1,6 @@ +set(_FindBoost_testing TRUE) +find_package(Boost MODULE) + +if(NOT _FindBoost_included) + message(FATAL_ERROR "FindBoost.cmake not included") +endif()
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index a93b811..8cda4c1 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -53,6 +53,9 @@ run_cmake(CMP0148-Libs-OLD) run_cmake(CMP0148-Libs-WARN) run_cmake(CMP0148-Libs-NEW) +run_cmake(CMP0167-OLD) +run_cmake(CMP0167-WARN) +run_cmake(CMP0167-NEW) run_cmake(WrongVersionRange) run_cmake(EmptyVersionRange) run_cmake(VersionRangeWithEXACT)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/include/CMP0167-NEW-name-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/include/CMP0167-NEW-name-result.txt
diff --git a/Tests/RunCMake/include/CMP0167-NEW-name-stderr.txt b/Tests/RunCMake/include/CMP0167-NEW-name-stderr.txt new file mode 100644 index 0000000..797b45b --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-NEW-name-stderr.txt
@@ -0,0 +1,6 @@ +^CMake Error at CMP0167-NEW-name\.cmake:[0-9]+ \(include\): + include could not find requested file: + + FindBoost +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0167-NEW-name.cmake b/Tests/RunCMake/include/CMP0167-NEW-name.cmake new file mode 100644 index 0000000..60eec25 --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-NEW-name.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0167 NEW) +include(FindBoost)
diff --git a/Tests/RunCMake/file/TOUCH-result.txt b/Tests/RunCMake/include/CMP0167-NEW-path-result.txt similarity index 100% copy from Tests/RunCMake/file/TOUCH-result.txt copy to Tests/RunCMake/include/CMP0167-NEW-path-result.txt
diff --git a/Tests/RunCMake/include/CMP0167-NEW-path-stderr.txt b/Tests/RunCMake/include/CMP0167-NEW-path-stderr.txt new file mode 100644 index 0000000..0b91cac --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-NEW-path-stderr.txt
@@ -0,0 +1,6 @@ +^CMake Error at [^ +]*/Modules/FindBoost.cmake:[0-9]+ \(message\): + The FindBoost module has been removed by policy CMP0167\. +Call Stack \(most recent call first\): + CMP0167-NEW-path\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0167-NEW-path.cmake b/Tests/RunCMake/include/CMP0167-NEW-path.cmake new file mode 100644 index 0000000..e0fb5e0 --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-NEW-path.cmake
@@ -0,0 +1,2 @@ +cmake_policy(SET CMP0167 NEW) +include(${CMAKE_ROOT}/Modules/FindBoost.cmake)
diff --git a/Tests/RunCMake/include/CMP0167-OLD.cmake b/Tests/RunCMake/include/CMP0167-OLD.cmake new file mode 100644 index 0000000..09630cb --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-OLD.cmake
@@ -0,0 +1,7 @@ +cmake_policy(SET CMP0167 OLD) +set(_FindBoost_testing 1) +include(FindBoost) + +if(NOT _FindBoost_included) + message(FATAL_ERROR "FindBoost.cmake not included") +endif()
diff --git a/Tests/RunCMake/include/CMP0167-WARN-stderr.txt b/Tests/RunCMake/include/CMP0167-WARN-stderr.txt new file mode 100644 index 0000000..41de30d --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-WARN-stderr.txt
@@ -0,0 +1,8 @@ +^CMake Warning \(dev\) 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\.$
diff --git a/Tests/RunCMake/include/CMP0167-WARN.cmake b/Tests/RunCMake/include/CMP0167-WARN.cmake new file mode 100644 index 0000000..85472d5 --- /dev/null +++ b/Tests/RunCMake/include/CMP0167-WARN.cmake
@@ -0,0 +1,7 @@ +# Do not set CMP0167. +set(_FindBoost_testing 1) +include(FindBoost) + +if(NOT _FindBoost_included) + message(FATAL_ERROR "FindBoost.cmake not included") +endif()
diff --git a/Tests/RunCMake/include/RunCMakeTest.cmake b/Tests/RunCMake/include/RunCMakeTest.cmake index 685173e..f3b58c9 100644 --- a/Tests/RunCMake/include/RunCMakeTest.cmake +++ b/Tests/RunCMake/include/RunCMakeTest.cmake
@@ -21,3 +21,8 @@ run_cmake(CMP0148-Libs-WARN) run_cmake(CMP0148-Libs-NEW-name) run_cmake(CMP0148-Libs-NEW-path) + +run_cmake(CMP0167-OLD) +run_cmake(CMP0167-WARN) +run_cmake(CMP0167-NEW-name) +run_cmake(CMP0167-NEW-path)
diff --git a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake index 4fbf147..11be360 100644 --- a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake +++ b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
@@ -6,12 +6,13 @@ run_cmake(CustomGuidTypePlatform) run_cmake(CustomConfig) -if(RunCMake_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])") +if(RunCMake_GENERATOR MATCHES "Visual Studio") run_cmake(SkipGetTargetFrameworkProperties) run_cmake(VSCSharpReference) endif() -if(RunCMake_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])") +if(RunCMake_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])" + AND NOT RunCMake_GENERATOR_TOOLSET MATCHES "^(v80|v90|v100|v110|v120)$") function(run_VSCSharpOnlyProject) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VSCSharpOnlyProject-build) run_cmake(VSCSharpOnlyProject)
diff --git a/Tests/RunCMake/install/EXPORT-InterfaceLinkNoexist.cmake b/Tests/RunCMake/install/EXPORT-InterfaceLinkNoexist.cmake new file mode 100644 index 0000000..14442bc --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-InterfaceLinkNoexist.cmake
@@ -0,0 +1,4 @@ +add_library(foo INTERFACE) +target_link_libraries(foo INTERFACE nonexistent::bar) +install(TARGETS foo DESTINATION lib EXPORT foo-targets) +install(EXPORT foo-targets FILE foo-targets.cmake DESTINATION lib)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 0439b42..295922c 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -8,7 +8,9 @@ file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") run_cmake(${case}) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug) + unset(RunCMake_TEST_OUTPUT_MERGE) # Check "all" components. set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-all) run_cmake_command(${case}-all ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -P cmake_install.cmake) @@ -83,6 +85,7 @@ run_cmake(EXPORT-NamelinkOnly) run_cmake(EXPORT-SeparateNamelink) run_cmake(EXPORT-TargetTwice) +run_cmake(EXPORT-InterfaceLinkNoexist) run_cmake(CMP0062-OLD) run_cmake(CMP0062-NEW) run_cmake(CMP0062-WARN)
diff --git a/Tests/RunCMake/project/ProjectCMP0126.cmake b/Tests/RunCMake/project/ProjectCMP0126.cmake new file mode 100644 index 0000000..65e3982 --- /dev/null +++ b/Tests/RunCMake/project/ProjectCMP0126.cmake
@@ -0,0 +1,11 @@ +cmake_policy(SET CMP0126 NEW) +set(example_SOURCE_DIR "bad/path") +set(example_BINARY_DIR "bad/path") +project(example LANGUAGES) + +if(NOT "${example_SOURCE_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") + message(FATAL_ERROR "example_SOURCE_DIR not set to expected value") +endif() +if(NOT "${example_BINARY_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + message(FATAL_ERROR "example_BINARY_DIR not set to expected value") +endif()
diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index 16f10be..ed4b8ba 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake
@@ -33,6 +33,7 @@ if(RunCMake_GENERATOR MATCHES "Make|Ninja") run_cmake(LanguagesUsedButNotEnabled) endif() +run_cmake(ProjectCMP0126) run_cmake(ProjectDescription) run_cmake(ProjectDescription2) run_cmake(ProjectDescriptionNoArg) @@ -61,4 +62,10 @@ run_cmake(CMP0096-OLD) run_cmake(CMP0096-NEW) +# We deliberately run these twice to verify behavior of the second CMake run +run_cmake(SameProjectVarsSubdir) +set(RunCMake_TEST_NO_CLEAN 1) +run_cmake(SameProjectVarsSubdir) +set(RunCMake_TEST_NO_CLEAN 0) + run_cmake(NoMinimumRequired)
diff --git a/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt b/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt new file mode 100644 index 0000000..73dadfd --- /dev/null +++ b/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt
@@ -0,0 +1,9 @@ +(-- )? SameProjectVarsSubdir_SOURCE_DIR = [^ +]+/subdir1 + SameProjectVarsSubdir_BINARY_DIR = [^ +]+/subdir1 + SameProjectVarsSubdir_IS_TOP_LEVEL = OFF +(-- )? sub2proj_SOURCE_DIR = [^ +]+/subdir2 + sub2proj_BINARY_DIR = [^ +]+/subdir2
diff --git a/Tests/RunCMake/project/SameProjectVarsSubdir.cmake b/Tests/RunCMake/project/SameProjectVarsSubdir.cmake new file mode 100644 index 0000000..36a7960 --- /dev/null +++ b/Tests/RunCMake/project/SameProjectVarsSubdir.cmake
@@ -0,0 +1,17 @@ +add_subdirectory(subdir1) + +# Simulate a situation that FetchContent_MakeAvailable() used to be able to +# create, but that should no longer be possible. If depname_SOURCE_DIR and +# depname_BINARY_DIR variables are defined as non-cache variables before the +# project(depname) call, those non-cache variables used to prevent project() +# from setting those variables itself due to CMP0126 (if set to NEW). This only +# showed up if the project(depname) call was not in the dependency's top level +# CMakeLists.txt file, but rather in a subdirectory (googletest is one example +# that used to do this). Since CMake 3.30.3, the dependency's project() call +# should set non-cache variables that will make the variable values visible +# and avoid any masking from variables set before the project() call. We want +# to verify this 3.30.3+ behavior here and in subdir2. +set(sub2proj_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(sub2proj_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory(subdir2)
diff --git a/Tests/RunCMake/project/subdir1/CMakeLists.txt b/Tests/RunCMake/project/subdir1/CMakeLists.txt new file mode 100644 index 0000000..d6be229 --- /dev/null +++ b/Tests/RunCMake/project/subdir1/CMakeLists.txt
@@ -0,0 +1 @@ +project(${RunCMake_TEST} LANGUAGES NONE)
diff --git a/Tests/RunCMake/project/subdir2/CMakeLists.txt b/Tests/RunCMake/project/subdir2/CMakeLists.txt new file mode 100644 index 0000000..c28e0c9 --- /dev/null +++ b/Tests/RunCMake/project/subdir2/CMakeLists.txt
@@ -0,0 +1,12 @@ +message(STATUS + " ${RunCMake_TEST}_SOURCE_DIR = ${${RunCMake_TEST}_SOURCE_DIR}\n" + " ${RunCMake_TEST}_BINARY_DIR = ${${RunCMake_TEST}_BINARY_DIR}\n" + " ${RunCMake_TEST}_IS_TOP_LEVEL = ${${RunCMake_TEST}_IS_TOP_LEVEL}" +) + +project(sub2proj LANGUAGES NONE) + +message(STATUS + " sub2proj_SOURCE_DIR = ${sub2proj_SOURCE_DIR}\n" + " sub2proj_BINARY_DIR = ${sub2proj_BINARY_DIR}" +)
diff --git a/Tests/RunCMake/property_init/CompileSources.cmake b/Tests/RunCMake/property_init/CompileSources.cmake index e8c5554..22b8f3f 100644 --- a/Tests/RunCMake/property_init/CompileSources.cmake +++ b/Tests/RunCMake/property_init/CompileSources.cmake
@@ -100,6 +100,7 @@ "C_LINKER_LAUNCHER" "ccache" "<SAME>" ### C++ "CXX_LINKER_LAUNCHER" "ccache" "<SAME>" + "CXX_MODULE_STD" "ON" "<SAME>" ### CUDA "CUDA_RESOLVE_DEVICE_SYMBOLS" "ON" "<SAME>" "CUDA_RUNTIME_LIBRARY" "Static" "<SAME>"
diff --git a/Tests/RunCMake/separate_arguments/ProgramCommand.cmake b/Tests/RunCMake/separate_arguments/ProgramCommand.cmake index bdf5810..cbd44bf 100644 --- a/Tests/RunCMake/separate_arguments/ProgramCommand.cmake +++ b/Tests/RunCMake/separate_arguments/ProgramCommand.cmake
@@ -4,12 +4,11 @@ message (SEND_ERROR "unexpected result with nonexistent program") endif() -set (TEST_EXE_DIR "${CMAKE_CURRENT_BINARY_DIR}/TestExe") -file(MAKE_DIRECTORY "${TEST_EXE_DIR}") -file(COPY "${CMAKE_COMMAND}" DESTINATION "${TEST_EXE_DIR}") -cmake_path (GET CMAKE_COMMAND FILENAME cmake_exe) +get_filename_component(cmake_command "${CMAKE_COMMAND}" ABSOLUTE) +cmake_path (GET cmake_command FILENAME cmake_exe) +cmake_path (GET cmake_command PARENT_PATH cmake_dir) -set (ENV{PATH} "${TEST_EXE_DIR}") +set (ENV{PATH} "${cmake_dir}") separate_arguments (out UNIX_COMMAND PROGRAM "${cmake_exe}") @@ -22,8 +21,8 @@ endif() list(GET out 0 cmake) list(GET out 1 args) -if (NOT cmake STREQUAL "${TEST_EXE_DIR}/${cmake_exe}") - message (SEND_ERROR "bad path for program: '${cmake}' instead of '${TEST_EXE_DIR}/${cmake_exe}'") +if (NOT cmake STREQUAL "${cmake_dir}/${cmake_exe}") + message (SEND_ERROR "bad path for program: '${cmake}' instead of '${cmake_dir}/${cmake_exe}'") endif() if (NOT args STREQUAL "") message (SEND_ERROR "bad value for args: '${args}' instead of ''") @@ -40,8 +39,8 @@ endif() list(GET out 0 cmake) list(GET out 1 args) -if (NOT cmake STREQUAL "${TEST_EXE_DIR}/${cmake_exe}") - message (SEND_ERROR "bad path for program: '${cmake}' instead of '${TEST_EXE_DIR}/${cmake_exe}'") +if (NOT cmake STREQUAL "${cmake_dir}/${cmake_exe}") + message (SEND_ERROR "bad path for program: '${cmake}' instead of '${cmake_dir}/${cmake_exe}'") endif() if (NOT args STREQUAL " a b c") message (SEND_ERROR "bad value for args: '${args}' instead of ' a b c'")
diff --git a/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake b/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake index 2826cc9..b93b42c 100644 --- a/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake +++ b/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake
@@ -4,12 +4,11 @@ message (SEND_ERROR "unexpected result with nonexistent program") endif() -set (TEST_EXE_DIR "${CMAKE_CURRENT_BINARY_DIR}/TestExe") -file(MAKE_DIRECTORY "${TEST_EXE_DIR}") -file(COPY "${CMAKE_COMMAND}" DESTINATION "${TEST_EXE_DIR}") -cmake_path (GET CMAKE_COMMAND FILENAME cmake_exe) +get_filename_component(cmake_command "${CMAKE_COMMAND}" ABSOLUTE) +cmake_path (GET cmake_command FILENAME cmake_exe) +cmake_path (GET cmake_command PARENT_PATH cmake_dir) -set (ENV{PATH} "${TEST_EXE_DIR}") +set (ENV{PATH} "${cmake_dir}") separate_arguments (out UNIX_COMMAND PROGRAM SEPARATE_ARGS "${cmake_exe} a b c") list (LENGTH out length) @@ -20,8 +19,8 @@ message(FATAL_ERROR "unexpected arguments") endif() list(POP_FRONT out cmake) -if (NOT cmake STREQUAL "${TEST_EXE_DIR}/${cmake_exe}") - message (SEND_ERROR "bad path for program: '${cmake}' instead of '${TEST_EXE_DIR}/${cmake_exe}'") +if (NOT cmake STREQUAL "${cmake_dir}/${cmake_exe}") + message (SEND_ERROR "bad path for program: '${cmake}' instead of '${cmake_dir}/${cmake_exe}'") endif() if (NOT out STREQUAL "a;b;c") message (SEND_ERROR "bad path for args: '${out}' instead of 'a;b;c'")
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake index 88a7e63..97a96b4 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -135,4 +135,5 @@ OR CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|visionOS|watchOS|Linux|BSD|MSYS|CYGWIN") run_cmake(feature-WHOLE_ARCHIVE) run_cmake_target(feature-WHOLE_ARCHIVE link-exe main) + run_cmake_target(feature-WHOLE_ARCHIVE circular-exe main_circular) endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c new file mode 100644 index 0000000..80ee413 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c
@@ -0,0 +1,6 @@ +void circular2(void); + +void circular1(void) +{ + circular2(); +}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c new file mode 100644 index 0000000..751bab5 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c
@@ -0,0 +1,7 @@ + +void circular1(void); + +void circular2(void) +{ + circular1(); +}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt new file mode 100644 index 0000000..fb4871c --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt
@@ -0,0 +1 @@ +(ld: warning: ignoring duplicate libraries:)?
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake index e525325..5c599c9 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
@@ -9,3 +9,13 @@ add_executable(main main.c) target_link_libraries(main PRIVATE lib) + + +add_library(circular1 STATIC circular1.c) +add_library(circular2 STATIC circular2.c) + +target_link_libraries(circular1 PRIVATE circular2) +target_link_libraries(circular2 PRIVATE circular1) + +add_executable(main_circular main_circular.c) +target_link_libraries(main_circular PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,circular1>)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c new file mode 100644 index 0000000..16ebee6 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c
@@ -0,0 +1,9 @@ + +void circular1(void); + +int main(void) +{ + circular1(); + + return 0; +}
diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake index 229c102..124034a 100644 --- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake +++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
@@ -16,6 +16,7 @@ include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") run_cmake(ConfigureLog) +run_cmake(TopIncludes) run_cmake(NoArgs) run_cmake(OneArg) run_cmake(TwoArgs)
diff --git a/Tests/RunCMake/try_compile/TopIncludes-config.txt b/Tests/RunCMake/try_compile/TopIncludes-config.txt new file mode 100644 index 0000000..6bd9c90 --- /dev/null +++ b/Tests/RunCMake/try_compile/TopIncludes-config.txt
@@ -0,0 +1,85 @@ +^ +--- +events:( + - + kind: "message-v1" + backtrace:( + - "[^"]+")+ + message: \|( ++ [^ +]*)*)+ + - + kind: "try_compile-v1" + backtrace: + - "[^"]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\)" + - "[^"]*/Modules/CMakeTestCCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)" + - "TopIncludes.cmake:[0-9]+ \(enable_language\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + checks: + - "Detecting C compiler ABI info" + directories: + source: "[^"]*/Tests/RunCMake/try_compile/TopIncludes-build/CMakeFiles/CMakeScratch/TryCompile-[^/"]+" + binary: "[^"]*/Tests/RunCMake/try_compile/TopIncludes-build/CMakeFiles/CMakeScratch/TryCompile-[^/"]+" + cmakeVariables:( + CMAKE_[^ +]*)+ + buildResult: + variable: "CMAKE_C_ABI_COMPILED" + cached: true + stdout: \|.* + exitCode: 0( + - + kind: "message-v1" + backtrace:( + - "[^"]+")+ + message: \|( ++ [^ +]*)*)* + - + kind: "try_compile-v1" + backtrace: + - "TopIncludes.cmake:[0-9]+ \(try_compile\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + description: "Project without property set\." + directories: + source: "[^"]*/Tests/RunCMake/try_compile/proj" + binary: "[^"]*/Tests/RunCMake/try_compile/TopIncludes-build/CMakeFiles/CMakeScratch/TryCompile-[^/"]+" + buildResult: + variable: "result" + cached: true + stdout: \|.* + exitCode: 0 + - + kind: "try_compile-v1" + backtrace: + - "TopIncludes.cmake:[0-9]+ \(try_compile\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + description: "Project with property set\." + directories: + source: "[^"]*/Tests/RunCMake/try_compile/proj" + binary: "[^"]*/Tests/RunCMake/try_compile/TopIncludes-build/CMakeFiles/CMakeScratch/TryCompile-[^/"]+" + cmakeVariables: + CMAKE_PROJECT_TOP_LEVEL_INCLUDES: "[^"]*/Tests/RunCMake/try_compile/include_pass1.cmake;[^"]*/Tests/RunCMake/try_compile/include_pass2.cmake" + buildResult: + variable: "result" + cached: true + stdout: \|.* + exitCode: 0 + - + kind: "try_compile-v1" + backtrace: + - "TopIncludes.cmake:[0-9]+ \(try_compile\)" + - "CMakeLists.txt:[0-9]+ \(include\)" + description: "Source file with property set\." + directories: + source: "[^"]*/Tests/RunCMake/try_compile/TopIncludes-build/CMakeFiles/CMakeScratch/TryCompile-[^/"]+" + binary: "[^"]*/Tests/RunCMake/try_compile/TopIncludes-build/CMakeFiles/CMakeScratch/TryCompile-[^/"]+" + cmakeVariables:( + CMAKE_[^ +]*)+ + buildResult: + variable: "result" + cached: true + stdout: \|.* + exitCode: 0 +\.\.\.$
diff --git a/Tests/RunCMake/try_compile/TopIncludes.cmake b/Tests/RunCMake/try_compile/TopIncludes.cmake new file mode 100644 index 0000000..2a70cc7 --- /dev/null +++ b/Tests/RunCMake/try_compile/TopIncludes.cmake
@@ -0,0 +1,34 @@ +enable_language(C) + +# Normally CMAKE_PROJECT_TOP_LEVEL_INCLUDES must be set before the first +# project() call. We don't care about the variable's usual effects here, we +# only care whether the variable is propagated to try_compile() project calls. +set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/include_error.cmake +) + +try_compile(result + PROJECT TestProject + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/proj + LOG_DESCRIPTION "Project without property set." +) + +set_property(GLOBAL PROPERTY PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILE YES) +set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/include_pass1.cmake + ${CMAKE_CURRENT_LIST_DIR}/include_pass2.cmake +) +try_compile(result + PROJECT TestProject + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/proj + LOG_DESCRIPTION "Project with property set." +) + +# Confirm the property only affects whole project signature +set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/include_error.cmake +) +try_compile(result + SOURCES ${CMAKE_CURRENT_LIST_DIR}/src.c + LOG_DESCRIPTION "Source file with property set." +)
diff --git a/Tests/RunCMake/try_compile/include_error.cmake b/Tests/RunCMake/try_compile/include_error.cmake new file mode 100644 index 0000000..7c73ab6 --- /dev/null +++ b/Tests/RunCMake/try_compile/include_error.cmake
@@ -0,0 +1 @@ +message(FATAL_ERROR "Unexpectedly read top level include file")
diff --git a/Tests/RunCMake/try_compile/include_pass1.cmake b/Tests/RunCMake/try_compile/include_pass1.cmake new file mode 100644 index 0000000..e831ca3 --- /dev/null +++ b/Tests/RunCMake/try_compile/include_pass1.cmake
@@ -0,0 +1 @@ +message(STATUS "Top level include file was read - pass 1")
diff --git a/Tests/RunCMake/try_compile/include_pass2.cmake b/Tests/RunCMake/try_compile/include_pass2.cmake new file mode 100644 index 0000000..31336aa --- /dev/null +++ b/Tests/RunCMake/try_compile/include_pass2.cmake
@@ -0,0 +1 @@ +message(STATUS "Top level include file was read - pass 2")
diff --git a/Tests/RunCMake/try_compile/proj/CMakeLists.txt b/Tests/RunCMake/try_compile/proj/CMakeLists.txt index 652f5b6..09feca4 100644 --- a/Tests/RunCMake/try_compile/proj/CMakeLists.txt +++ b/Tests/RunCMake/try_compile/proj/CMakeLists.txt
@@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(TestProject NONE)
diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index c5a4b83..e933ea1 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt
@@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) if(POLICY CMP0126) cmake_policy(SET CMP0126 NEW) endif() @@ -28,6 +28,10 @@ add_subdirectory(SubA) add_subdirectory(SubB) +add_subdirectory(SubC) +add_subdirectory(SubD) +add_subdirectory(SubE) + set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift) add_executable(SwiftOnly main.swift) @@ -43,6 +47,17 @@ target_link_libraries(N PUBLIC M) +# FIXME(#25989): The Xcode generator doesn't respect CMAKE_Swift_MODULE_DIRECTORY. +if(NOT CMAKE_GENERATOR STREQUAL "Xcode") + add_custom_command(TARGET M + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E compare_files + "${CMAKE_Swift_MODULE_DIRECTORY}/M.swiftmodule" + "${CMAKE_Swift_MODULE_DIRECTORY}/M.swiftmodule" + COMMENT "check that .swiftmodule files are generated in CMAKE_Swift_MODULE_DIRECTORY" + VERBATIM) +endif() + if(NOT XCODE_VERSION OR XCODE_VERSION VERSION_GREATER_EQUAL 9.0) # TODO: Add a wholemodule object-library test once that is working add_library(O OBJECT O.swift L.swift) @@ -57,16 +72,7 @@ add_library(SwiftIface INTERFACE) target_link_libraries(SwiftOnly PRIVATE SwiftIface) -# @_alwaysEmitIntoClient ensures that the function body is inserted into the -# swiftmodule instead of as a symbol in the binary itself. I'm doing this to -# avoid having to link the executable. There are some flags required in order to -# link an executable into a library that I didn't see CMake emitting for Swift -# on macOS. AEIC is the easiest workaround that still tests this functionality. -# Unfortunately, AEIC was only added recently (~Swift 5.2), so we need to check -# that it is available before using it. -if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2) - add_subdirectory("SwiftPlugin") -endif() +add_subdirectory("SwiftPlugin") function(test_cmp0157_default mode) if(POLICY CMP0157)
diff --git a/Tests/SwiftOnly/SubC/CMakeLists.txt b/Tests/SwiftOnly/SubC/CMakeLists.txt new file mode 100644 index 0000000..4fdb3d3 --- /dev/null +++ b/Tests/SwiftOnly/SubC/CMakeLists.txt
@@ -0,0 +1,2 @@ +add_library(SubC SubC.swift) +target_link_libraries(SubC PUBLIC SubD)
diff --git a/Tests/SwiftOnly/SubC/SubC.swift b/Tests/SwiftOnly/SubC/SubC.swift new file mode 100644 index 0000000..1ca44f4 --- /dev/null +++ b/Tests/SwiftOnly/SubC/SubC.swift
@@ -0,0 +1 @@ +import SubD
diff --git a/Tests/SwiftOnly/SubD/CMakeLists.txt b/Tests/SwiftOnly/SubD/CMakeLists.txt new file mode 100644 index 0000000..01b38d3 --- /dev/null +++ b/Tests/SwiftOnly/SubD/CMakeLists.txt
@@ -0,0 +1 @@ +add_library(SubD SubD.swift)
diff --git a/Tests/SwiftOnly/SubD/SubD.swift b/Tests/SwiftOnly/SubD/SubD.swift new file mode 100644 index 0000000..857d3f4 --- /dev/null +++ b/Tests/SwiftOnly/SubD/SubD.swift
@@ -0,0 +1 @@ +public let x = 42
diff --git a/Tests/SwiftOnly/SubE/CMakeLists.txt b/Tests/SwiftOnly/SubE/CMakeLists.txt new file mode 100644 index 0000000..099a81c --- /dev/null +++ b/Tests/SwiftOnly/SubE/CMakeLists.txt
@@ -0,0 +1,2 @@ +add_executable(SubE main.swift) +target_link_libraries(SubE PUBLIC SubD)
diff --git a/Tests/SwiftOnly/SubE/main.swift b/Tests/SwiftOnly/SubE/main.swift new file mode 100644 index 0000000..1ca44f4 --- /dev/null +++ b/Tests/SwiftOnly/SubE/main.swift
@@ -0,0 +1 @@ +import SubD
diff --git a/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt b/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt index 4069f16..2bfbc8a 100644 --- a/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt +++ b/Tests/SwiftOnly/SwiftPlugin/CMakeLists.txt
@@ -1,5 +1,5 @@ add_executable(main main.swift) set_target_properties(main PROPERTIES ENABLE_EXPORTS TRUE) -add_library(plugin plugin.swift) +add_library(plugin MODULE plugin.swift) target_link_libraries(plugin PRIVATE main)
diff --git a/Tests/SwiftOnly/SwiftPlugin/main.swift b/Tests/SwiftOnly/SwiftPlugin/main.swift index f5aac51..caba1fb 100644 --- a/Tests/SwiftOnly/SwiftPlugin/main.swift +++ b/Tests/SwiftOnly/SwiftPlugin/main.swift
@@ -1,4 +1,3 @@ -@_alwaysEmitIntoClient -public func exported() -> Int { 32 } +public func exported() -> Int { return 32 } print(exported())
diff --git a/Tests/SwiftOnly/SwiftPlugin/plugin.swift b/Tests/SwiftOnly/SwiftPlugin/plugin.swift index e84f248..d00ca33 100644 --- a/Tests/SwiftOnly/SwiftPlugin/plugin.swift +++ b/Tests/SwiftOnly/SwiftPlugin/plugin.swift
@@ -1,3 +1,3 @@ import main -public func importing() -> Int { main.exported() + 1 } +public func importing() -> Int { return main.exported() + 1 }
diff --git a/Tests/TestsWorkingDirectory/CMakeLists.txt b/Tests/TestsWorkingDirectory/CMakeLists.txt index f77370c..c7b58a9 100644 --- a/Tests/TestsWorkingDirectory/CMakeLists.txt +++ b/Tests/TestsWorkingDirectory/CMakeLists.txt
@@ -7,30 +7,32 @@ set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") -add_test(NAME WorkingDirectory0 COMMAND WorkingDirectory "${CMAKE_BINARY_DIR}") +file(REAL_PATH "${CMAKE_BINARY_DIR}" _real_binary_dir) -add_test(NAME WorkingDirectory1 COMMAND WorkingDirectory "${CMAKE_BINARY_DIR}") +add_test(NAME WorkingDirectory0 COMMAND WorkingDirectory "${_real_binary_dir}") + +add_test(NAME WorkingDirectory1 COMMAND WorkingDirectory "${_real_binary_dir}") set_tests_properties(WorkingDirectory1 PROPERTIES WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" ) -string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${CMAKE_BINARY_DIR}") +string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${_real_binary_dir}") add_test(NAME WorkingDirectory2 COMMAND WorkingDirectory "${_parent_dir}") set_tests_properties(WorkingDirectory2 PROPERTIES WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/.." ) -set(_default_cwd "${CMAKE_BINARY_DIR}") +set(_default_cwd "${_real_binary_dir}") # FIXME: How to deal with /debug, /release, etc. with VS or Xcode? if(${CMAKE_GENERATOR} MATCHES "Makefiles") add_test(WorkingDirectory3 ${EXECUTABLE_OUTPUT_PATH}/WorkingDirectory ${_default_cwd}) endif() -add_test(NAME WorkingDirectory4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND WorkingDirectory ${CMAKE_BINARY_DIR}) +add_test(NAME WorkingDirectory4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND WorkingDirectory ${_real_binary_dir}) -string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${CMAKE_BINARY_DIR}") +string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${_real_binary_dir}") add_test(NAME WorkingDirectory5 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/.. COMMAND WorkingDirectory ${_parent_dir})
diff --git a/Tests/TestsWorkingDirectory/subdir/CMakeLists.txt b/Tests/TestsWorkingDirectory/subdir/CMakeLists.txt index c16b1db..a7f32b8 100644 --- a/Tests/TestsWorkingDirectory/subdir/CMakeLists.txt +++ b/Tests/TestsWorkingDirectory/subdir/CMakeLists.txt
@@ -1,27 +1,29 @@ -add_test(NAME WorkingDirectory-Subdir0 COMMAND WorkingDirectory "${CMAKE_CURRENT_BINARY_DIR}") +file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}" _real_current_binary_dir) -add_test(NAME WorkingDirectory-Subdir1 COMMAND WorkingDirectory "${CMAKE_CURRENT_BINARY_DIR}") +add_test(NAME WorkingDirectory-Subdir0 COMMAND WorkingDirectory "${_real_current_binary_dir}") + +add_test(NAME WorkingDirectory-Subdir1 COMMAND WorkingDirectory "${_real_current_binary_dir}") set_tests_properties(WorkingDirectory-Subdir1 PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) -string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${CMAKE_CURRENT_BINARY_DIR}") +string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${_real_current_binary_dir}") add_test(NAME WorkingDirectory-Subdir2 COMMAND WorkingDirectory "${_parent_dir}") set_tests_properties(WorkingDirectory-Subdir2 PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." ) -set(_default_cwd "${CMAKE_CURRENT_BINARY_DIR}") +set(_default_cwd "${_real_current_binary_dir}") # FIXME: How to deal with /debug, /release, etc. with VS or Xcode? if(${CMAKE_GENERATOR} MATCHES "Makefiles") add_test(WorkingDirectory-Subdir3 ${EXECUTABLE_OUTPUT_PATH}/WorkingDirectory ${_default_cwd}) endif() -add_test(NAME WorkingDirectory-Subdir4 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND WorkingDirectory ${CMAKE_CURRENT_BINARY_DIR}) +add_test(NAME WorkingDirectory-Subdir4 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND WorkingDirectory ${_real_current_binary_dir}) -string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${CMAKE_CURRENT_BINARY_DIR}") +string(REGEX REPLACE "/[^/]*$" "" _parent_dir "${_real_current_binary_dir}") add_test(NAME WorkingDirectory-Subdir5 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.. COMMAND WorkingDirectory ${_parent_dir})
diff --git a/Tests/VSAndroid/CMakeLists.txt b/Tests/VSAndroid/CMakeLists.txt index 73b1e07..774ffc0 100644 --- a/Tests/VSAndroid/CMakeLists.txt +++ b/Tests/VSAndroid/CMakeLists.txt
@@ -2,7 +2,7 @@ project(VSAndroid C CXX) set(CMAKE_ANDROID_ARCH armv7-a) -set(CMAKE_ANDROID_STL_TYPE stlport_shared) +set(CMAKE_ANDROID_STL_TYPE c++_shared) set(CMAKE_ANDROID_API_MIN 9) set(CMAKE_ANDROID_API 15) set(CMAKE_ANDROID_GUI 1)
diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt index 2a8c855..5e6ef0a 100644 --- a/Utilities/CMakeLists.txt +++ b/Utilities/CMakeLists.txt
@@ -13,7 +13,7 @@ add_subdirectory(Sphinx) endif() -if(WIX_CUSTOM_ACTION_ENABLED) +if(CMake_BUILD_WIX_CUSTOM_ACTION) add_subdirectory(Release/WiX) endif()
diff --git a/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx b/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx index 37ecd70..9ec8e5f 100644 --- a/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx +++ b/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx
@@ -55,10 +55,10 @@ , FID(FID) { SourceManager& SM = this->PP->getSourceManager(); - const FileEntry* Entry = SM.getFileEntryForID(FID); + OptionalFileEntryRef Entry = SM.getFileEntryRefForID(FID); assert(Entry && "Invalid FileID given"); - Lexer MyLexer(FID, SM.getMemoryBufferForFileOrFake(Entry), SM, + Lexer MyLexer(FID, SM.getMemoryBufferForFileOrFake(*Entry), SM, this->PP->getLangOpts()); Token Tok; @@ -157,9 +157,10 @@ // guards. SourceManager& SM = this->PP->getSourceManager(); if (Reason == EnterFile && FileType == SrcMgr::C_User) { - if (const FileEntry* FE = SM.getFileEntryForID(SM.getFileID(Loc))) { + if (OptionalFileEntryRef FE = + SM.getFileEntryRefForID(SM.getFileID(Loc))) { std::string FileName = cleanPath(FE->getName()); - this->Files[FileName] = FE; + this->Files.try_emplace(FileName, *FE); } } } @@ -205,9 +206,9 @@ continue; } - const FileEntry* FE = - SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc())); - std::string FileName = cleanPath(FE->getName()); + FileEntryRef FE = + *SM.getFileEntryRefForID(SM.getFileID(MI->getDefinitionLoc())); + std::string FileName = cleanPath(FE.getName()); this->Files.erase(FileName); // Look up Locations for this guard. @@ -290,7 +291,7 @@ } std::vector<std::pair<Token, const MacroInfo*>> Macros; - llvm::StringMap<const FileEntry*> Files; + llvm::StringMap<FileEntryRef> Files; std::map<const IdentifierInfo*, std::pair<SourceLocation, SourceLocation>> Ifndefs; std::map<SourceLocation, SourceLocation> EndIfs;
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index 52a31eb..8bf591b 100644 --- a/Utilities/Doxygen/CMakeLists.txt +++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeDeveloperReference_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.28 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/Release/WiX/WIX.template.in b/Utilities/Release/WiX/WIX.template.in index 1f8953b..afbd66f 100644 --- a/Utilities/Release/WiX/WIX.template.in +++ b/Utilities/Release/WiX/WIX.template.in
@@ -2,17 +2,22 @@ <?include "cpack_variables.wxi"?> -<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" - RequiredVersion="3.6.3303.0"> +<Wix + xmlns="http://wixtoolset.org/schemas/v4/wxs"@CPACK_WIX_CUSTOM_XMLNS_EXPANDED@ + RequiredVersion="4.0" + > - <Product Id="$(var.CPACK_WIX_PRODUCT_GUID)" + <Package Name="$(var.CPACK_PACKAGE_NAME)" - Language="1033" Version="$(var.CPACK_PACKAGE_VERSION)" Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" - UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)"> - - <Package InstallerVersion="301" Compressed="yes" InstallScope="perMachine"/> + UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)" + ProductCode="$(var.CPACK_WIX_PRODUCT_GUID)" + Scope="perMachine" + InstallerVersion="500" + Language="1033" + Compressed="yes" + > <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/> @@ -26,7 +31,7 @@ <Property Id="WIXUI_INSTALLDIR" Value="INSTALL_ROOT"/> <?ifdef CPACK_WIX_PRODUCT_ICON?> - <Property Id="ARPPRODUCTICON">ProductIcon.ico</Property> + <Property Id="ARPPRODUCTICON" Value="ProductIcon.ico" /> <Icon Id="ProductIcon.ico" SourceFile="$(var.CPACK_WIX_PRODUCT_ICON)"/> <?endif?> @@ -38,17 +43,15 @@ <WixVariable Id="WixUIDialogBmp" Value="$(var.CPACK_WIX_UI_DIALOG)"/> <?endif?> - <DirectoryRef Id="TARGETDIR"> - <Component Id="CMakeRegistry"> - <RegistryKey Root="HKLM" Key="Software\Kitware\CMake"> - <RegistryValue Type="string" Name="InstallDir" - Value="[INSTALL_ROOT]" KeyPath="yes"/> - </RegistryKey> + <StandardDirectory Id="TARGETDIR"> + <Component Id="CMakeRegistry_InstallDir"> + <RegistryValue Root="HKLM" Key="Software\Kitware\CMake" Name="InstallDir" Type="string" Value="[INSTALL_ROOT]" /> </Component> - </DirectoryRef> + </StandardDirectory> <FeatureRef Id="ProductFeature"> - <ComponentRef Id="CMakeRegistry"/> + <ComponentRef Id="CMakeRegistry_InstallDir" /> + <ComponentRef Id="CMakeRegistry_InstallInPATH" /> </FeatureRef> <UIRef Id="$(var.CPACK_WIX_UI_REF)" /> @@ -56,5 +59,5 @@ <?include "properties.wxi"?> <?include "product_fragment.wxi"?> - </Product> + </Package> </Wix>
diff --git a/Utilities/Release/WiX/cmake_extra_dialog.wxs b/Utilities/Release/WiX/cmake_extra_dialog.wxs deleted file mode 100644 index 0ee3d99..0000000 --- a/Utilities/Release/WiX/cmake_extra_dialog.wxs +++ /dev/null
@@ -1,36 +0,0 @@ -<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> - <Fragment> - <UI> - <Property Id="ADD_CMAKE_TO_PATH" Value="None"/> - <Dialog Id="CMakeExtraDialog" Width="370" Height="270" Title="Install Options"> - - <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)"/> - <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)"/> - - <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)"> - <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish> - </Control> - - <Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="Choose options for installing CMake [ProductVersion]"/> - <Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="Install Options"/> - <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)"/> - <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0"/> - <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0"/> - - <Control Id="Hint" Type="Text" X="26" Y="60" Width="250" Height="16" Transparent="yes" Text="By default CMake does not add its directory to the system PATH."/> - - <Control Id="ADD_CMAKE_TO_PATHOption" Type="RadioButtonGroup" X="26" Y="100" Width="305" Height="65" Property="ADD_CMAKE_TO_PATH"> - <RadioButtonGroup Property="ADD_CMAKE_TO_PATH"> - <RadioButton Value="None" X="0" Y="0" Width="295" Height="16" Text="Do not add CMake to the system PATH"/> - <RadioButton Value="System" X="0" Y="20" Width="295" Height="16" Text="Add CMake to the system PATH for all users"/> - <RadioButton Value="User" X="0" Y="40" Width="295" Height="16" Text="Add CMake to the system PATH for the current user"/> - </RadioButtonGroup> - </Control> - - <?ifdef BUILD_QtDialog ?> - <Control Id="DesktopShortcutCheckBox" Type="CheckBox" X="20" Y="170" Width="330" Height="18" CheckBoxValue="1" Property="DESKTOP_SHORTCUT_REQUESTED" Text="Create CMake Desktop Icon"/> - <?endif ?> - </Dialog> - </UI> - </Fragment> -</Wix>
diff --git a/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs b/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs index 8fe60f2..deec0f0 100644 --- a/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs +++ b/Utilities/Release/WiX/cmake_nsis_overwrite_dialog.wxs
@@ -1,21 +1,15 @@ -<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> - <Fragment> - <UI> - <Dialog Id="CMakeNsisOverwriteDialog" Width="310" Height="120" Title="NSIS Installation Conflict"> - <Control Id="OK" Type="PushButton" X="122" Y="90" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIOK)"> - <Publish Event="EndDialog" Value="Return">1</Publish> - </Control> - <Control Id="Text" Type="Text" X="48" Y="22" Width="260" Height="60"> - <Text> - Uninstall.exe was detected in your chosen installation prefix. - This indicates a conflicting NSIS based installation of CMake. - - Please uninstall your old CMake installation or choose a different - installation directory. - </Text> - </Control> - <Control Id="Icon" Type="Icon" X="15" Y="15" Width="24" Height="24" ToolTip="!(loc.InvalidDirDlgIconTooltip)" FixedSize="yes" IconSize="32" Text="!(loc.InvalidDirDlgIcon)" /> - </Dialog> - </UI> - </Fragment> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <UI> + <Dialog Id="CMakeNsisOverwriteDialog" Width="310" Height="120" Title="NSIS Installation Conflict"> + <Control Id="OK" Type="PushButton" X="122" Y="90" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIOK)"> + <Publish Event="EndDialog" Value="Return" /> + </Control> + <Control Id="Text" Type="Text" X="48" Y="22" Width="260" Height="60"> + <Text Value="Uninstall.exe was detected in your chosen installation prefix.
This indicates a conflicting NSIS based installation of CMake.

Please uninstall your old CMake installation or choose a different
installation directory." /> + </Control> + <Control Id="Icon" Type="Icon" X="15" Y="15" Width="24" Height="24" ToolTip="!(loc.InvalidDirDlgIconTooltip)" FixedSize="yes" IconSize="32" Text="!(loc.InvalidDirDlgIcon)" /> + </Dialog> + </UI> + </Fragment> </Wix>
diff --git a/Utilities/Release/WiX/custom_action_dll.wxs.in b/Utilities/Release/WiX/custom_action_dll.wxs.in index 021e63c..a39bc0d 100644 --- a/Utilities/Release/WiX/custom_action_dll.wxs.in +++ b/Utilities/Release/WiX/custom_action_dll.wxs.in
@@ -1,6 +1,6 @@ -<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> - <Fragment> - <Binary Id="CMakeCustomActionsDll" - SourceFile="$<TARGET_FILE:CMakeWiXCustomActions>"/> - </Fragment> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <Binary Id="CMakeCustomActionsDll" + SourceFile="$<TARGET_FILE:CMakeWiXCustomActions>"/> + </Fragment> </Wix>
diff --git a/Utilities/Release/WiX/install_dir.wxs b/Utilities/Release/WiX/install_dir.wxs index 49b74e3..a13da97 100644 --- a/Utilities/Release/WiX/install_dir.wxs +++ b/Utilities/Release/WiX/install_dir.wxs
@@ -1,72 +1,81 @@ -<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> - <Fragment> - <UI Id="CMakeUI_InstallDir"> - <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" /> - <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" /> - <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" /> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <?foreach WIXUIARCH in X86;X64;A64 ?> + <Fragment> + <UI Id="CMakeUI_InstallDir_$(WIXUIARCH)"> + <Publish Dialog="LicenseAgreementDlg" Control="Print" Event="DoAction" Value="WixUIPrintEula_$(WIXUIARCH)" /> + <Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH" /> + <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" /> + </UI> - <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" /> - <Property Id="WixUI_Mode" Value="InstallDir" /> + <UIRef Id="CMakeUI_InstallDir" /> + </Fragment> + <?endforeach?> - <DialogRef Id="CMakeExtraDialog" /> - <?ifdef CHECK_NSIS ?> - <DialogRef Id="CMakeNsisOverwriteDialog" /> - <?endif ?> + <Fragment> + <UI Id="file CMakeUI_InstallDir"> + <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" /> + <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" /> + <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" /> - <DialogRef Id="BrowseDlg" /> - <DialogRef Id="DiskCostDlg" /> - <DialogRef Id="ErrorDlg" /> - <DialogRef Id="FatalError" /> - <DialogRef Id="FilesInUse" /> - <DialogRef Id="MsiRMFilesInUse" /> - <DialogRef Id="PrepareDlg" /> - <DialogRef Id="ProgressDlg" /> - <DialogRef Id="ResumeDlg" /> - <DialogRef Id="UserExit" /> + <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" /> - <Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish> - <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish> + <DialogRef Id="CMakeOptionsDlg" /> + <?ifdef CHECK_NSIS ?> + <DialogRef Id="CMakeNsisOverwriteDialog" /> + <?endif?> - <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish> + <DialogRef Id="BrowseDlg" /> + <DialogRef Id="DiskCostDlg" /> + <DialogRef Id="ErrorDlg" /> + <DialogRef Id="FatalError" /> + <DialogRef Id="FilesInUse" /> + <DialogRef Id="MsiRMFilesInUse" /> + <DialogRef Id="PrepareDlg" /> + <DialogRef Id="ProgressDlg" /> + <DialogRef Id="ResumeDlg" /> + <DialogRef Id="UserExit" /> - <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">NOT Installed</Publish> - <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish> + <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" /> - <Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish> - <Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="CMakeExtraDialog">LicenseAccepted = "1"</Publish> + <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" /> - <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="CMakeExtraDialog">1</Publish> - <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish> - <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish> - <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish> - <?ifdef CHECK_NSIS ?> - <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="CMakeDetectNsisOverwrite" Order="4">1</Publish> - <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="CMakeNsisOverwriteDialog" Order="5">CMAKE_NSIS_OVERWRITE_DETECTED="1"</Publish> - <?endif ?> - <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="6"><![CDATA[(WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1") AND CMAKE_NSIS_OVERWRITE_DETECTED<>1]]></Publish> - <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish> - <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish> + <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg" Condition="NOT Installed" /> + <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" /> - <Publish Dialog="CMakeExtraDialog" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg">1</Publish> - <Publish Dialog="CMakeExtraDialog" Control="Next" Event="NewDialog" Value="InstallDirDlg">1</Publish> + <Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" /> + <Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="CMakeOptionsDlg" Condition="LicenseAccepted = "1"" /> - <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1">NOT Installed</Publish> - <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish> - <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">Installed AND PATCH</Publish> + <Publish Dialog="CMakeOptionsDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" /> + <Publish Dialog="CMakeOptionsDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" /> - <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish> + <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="CMakeOptionsDlg" /> + <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" /> + <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" /> + <?ifdef CHECK_NSIS ?> + <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="CMakeDetectNsisOverwrite" Order="4" /> + <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="CMakeNsisOverwriteDialog" Order="5" Condition="CMAKE_NSIS_OVERWRITE_DETECTED="1"" /> + <?endif?> + <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="6" Condition="(WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1") AND CMAKE_NSIS_OVERWRITE_DETECTED<>1" /> + <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" /> + <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" /> - <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish> - <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish> - <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish> + <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1" Condition="NOT Installed" /> + <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" /> + <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" /> - <Property Id="ARPNOMODIFY" Value="1" /> - </UI> + <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" /> - <UIRef Id="WixUI_Common" /> + <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" /> + <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" /> + <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" /> - <?ifdef CHECK_NSIS ?> - <CustomAction Id="CMakeDetectNsisOverwrite" BinaryKey="CMakeCustomActionsDll" DllEntry="DetectNsisOverwrite"/> - <?endif ?> - </Fragment> + <Property Id="ARPNOMODIFY" Value="1" /> + </UI> + + <UIRef Id="WixUI_Common" /> + + <?ifdef CHECK_NSIS ?> + <CustomAction Id="CMakeDetectNsisOverwrite" BinaryRef="CMakeCustomActionsDll" DllEntry="DetectNsisOverwrite" /> + <?endif?> + </Fragment> </Wix>
diff --git a/Utilities/Release/WiX/options.wxs b/Utilities/Release/WiX/options.wxs new file mode 100644 index 0000000..28d4365 --- /dev/null +++ b/Utilities/Release/WiX/options.wxs
@@ -0,0 +1,36 @@ +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <!-- Hold the "CMAKE_IN_PATH" checkbox value in a public property to propagate to the InstallExecuteSequence. --> + <Property Id="CMAKE_IN_PATH" Value="Init" /> + + <!-- Always initialize the checkbox value so it cannot be directly overridden from the command line. --> + <SetProperty Action="Set_CMAKE_IN_PATH_Init" Id="CMAKE_IN_PATH" After="AppSearch" Sequence="first" Value="1" /> + + <!-- Read the "CMAKE_IN_PATH" checkbox value from the registry, if it was stored previously. + Properties using RegistrySearch cannot be private, so use a private-looking public name. --> + <Property Id="_CMAKE_IN_PATH_REG"> + <RegistrySearch Id="CMAKE_IN_PATH_RegistrySearch_HKLM" Root="HKLM" Key="Software\Kitware\CMake" Name="InstallInPATH" Type="raw" /> + </Property> + + <!-- Override the default "CMAKE_IN_PATH" checkbox with a value read from the registry, if any. --> + <SetProperty Action="Set_CMAKE_IN_PATH_REG_0" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_Init" Sequence="first" Value="{}" Condition="_CMAKE_IN_PATH_REG = "#0"" /> + <SetProperty Action="Set_CMAKE_IN_PATH_REG_1" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_REG_0" Sequence="first" Value="1" Condition="_CMAKE_IN_PATH_REG = "#1"" /> + + <!-- Override the default "CMAKE_IN_PATH" checkbox with a value specified on the command line, if any. --> + <SetProperty Action="Set_CMAKE_IN_PATH_CMD_0" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_REG_1" Sequence="first" Value="{}" Condition="ADD_CMAKE_TO_PATH = "0"" /> + <SetProperty Action="Set_CMAKE_IN_PATH_CMD_1" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_CMD_0" Sequence="first" Value="1" Condition="ADD_CMAKE_TO_PATH = "1"" /> + <!-- Support legacy values too. --> + <SetProperty Action="Set_CMAKE_IN_PATH_CMD_None" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_CMD_1" Sequence="first" Value="{}" Condition="ADD_CMAKE_TO_PATH = "None"" /> + <SetProperty Action="Set_CMAKE_IN_PATH_CMD_System" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_CMD_None" Sequence="first" Value="1" Condition="ADD_CMAKE_TO_PATH = "System" AND ALLUSERS" /> + <!-- Per-user installation is not implemented, but reserve the old value for future use. --> + <SetProperty Action="Set_CMAKE_IN_PATH_CMD_User" Id="CMAKE_IN_PATH" After="Set_CMAKE_IN_PATH_CMD_System" Sequence="first" Value="1" Condition="ADD_CMAKE_TO_PATH = "User" AND NOT ALLUSERS" /> + + <StandardDirectory Id="TARGETDIR"> + <!-- Save the "CMAKE_IN_PATH" checkbox value persistently in the registry. --> + <Component Id="CMakeRegistry_InstallInPATH"> + <!-- Use a leading "0" so the value parses as an integer even when the property is unset. --> + <RegistryValue Root="HKLM" Key="Software\Kitware\CMake" Name="InstallInPATH" Type="integer" Value="0[CMAKE_IN_PATH]" /> + </Component> + </StandardDirectory> + </Fragment> +</Wix>
diff --git a/Utilities/Release/WiX/options_dlg.wxs b/Utilities/Release/WiX/options_dlg.wxs new file mode 100644 index 0000000..ea2854f --- /dev/null +++ b/Utilities/Release/WiX/options_dlg.wxs
@@ -0,0 +1,28 @@ +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <UI> + <PropertyRef Id="CMAKE_IN_PATH" /> + <Dialog Id="CMakeOptionsDlg" Width="370" Height="270" Title="Install Options"> + + <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" /> + <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" /> + + <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)"> + <Publish Event="SpawnDialog" Value="CancelDlg" /> + </Control> + + <Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="Choose options for installing CMake [ProductVersion]" /> + <Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="Install Options" /> + <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" /> + <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" /> + <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" /> + + <Control Id="CMAKE_IN_PATH_CheckBox" Type="CheckBox" X="20" Y="60" Width="330" Height="18" CheckBoxValue="1" Property="CMAKE_IN_PATH" Text="Add CMake to the PATH environment variable" /> + + <?ifdef BUILD_QtDialog ?> + <Control Id="DesktopShortcutCheckBox" Type="CheckBox" X="20" Y="80" Width="330" Height="18" CheckBoxValue="1" Property="DESKTOP_SHORTCUT_REQUESTED" Text="Add CMake shortcut to the Desktop" /> + <?endif?> + </Dialog> + </UI> + </Fragment> +</Wix>
diff --git a/Utilities/Release/WiX/patch_desktop_shortcut.xml b/Utilities/Release/WiX/patch_desktop_shortcut.xml index d30705d..6170d64 100644 --- a/Utilities/Release/WiX/patch_desktop_shortcut.xml +++ b/Utilities/Release/WiX/patch_desktop_shortcut.xml
@@ -1,5 +1,3 @@ <CPackWiXPatch> - <CPackWiXFragment Id="CM_SHORTCUT_DESKTOP"> - <Condition>DESKTOP_SHORTCUT_REQUESTED = 1</Condition> - </CPackWiXFragment> + <CPackWiXFragment Id="CM_SHORTCUT_DESKTOP" Condition="DESKTOP_SHORTCUT_REQUESTED" /> </CPackWiXPatch>
diff --git a/Utilities/Release/WiX/patch_path_env.xml b/Utilities/Release/WiX/patch_path_env.xml index 0e335c4..bf52b27 100644 --- a/Utilities/Release/WiX/patch_path_env.xml +++ b/Utilities/Release/WiX/patch_path_env.xml
@@ -1,18 +1,15 @@ <CPackWiXPatch> <CPackWiXFragment Id="CM_DP_bin"> - <Component Id="CMakeSystemPathEntryCMP" KeyPath="yes" Guid="0E782367-5D68-4539-81D1-B9757AE496A1"> - - <Condition>ADD_CMAKE_TO_PATH = "System"</Condition> - + <!-- Implement the "CMAKE_IN_PATH" checkbox when installing for all users. --> + <Component Id="CMakeSystemPathEntryCMP" KeyPath="yes" Guid="0E782367-5D68-4539-81D1-B9757AE496A1" Condition="CMAKE_IN_PATH AND ALLUSERS"> <Environment Id="CMakeSystemPathEntryENV" Action="set" Part="last" Name="PATH" Value="[INSTALL_ROOT]bin" System="yes"/> </Component> - <Component Id="CMakeUserPathEntryCMP" KeyPath="yes" Guid="392E524D-D5BF-4F16-A7AF-A82B07482CB9"> - - <Condition>ADD_CMAKE_TO_PATH = "User"</Condition> - + <!-- Implement the "CMAKE_IN_PATH" checkbox when installing per-user. --> + <!-- This is not currently reachable, but is reserved for future use. --> + <Component Id="CMakeUserPathEntryCMP" KeyPath="yes" Guid="392E524D-D5BF-4F16-A7AF-A82B07482CB9" Condition="CMAKE_IN_PATH AND NOT ALLUSERS"> <Environment Id="CMakeUserPathEntryENV" Action="set" Part="last" Name="PATH" Value="[INSTALL_ROOT]bin" System="no"/>
diff --git a/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1 b/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1 old mode 100755 new mode 100644
diff --git a/Utilities/Release/win/sign-package.ps1 b/Utilities/Release/win/sign-package.ps1 old mode 100755 new mode 100644
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 09bb65b..3f55046 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_6_0" +readonly tag="curl-8_8_0" readonly shortlog=false readonly paths=" CMake/*
diff --git a/Utilities/Scripts/update-zlib.bash b/Utilities/Scripts/update-zlib.bash index c18f118..aad18bf 100755 --- a/Utilities/Scripts/update-zlib.bash +++ b/Utilities/Scripts/update-zlib.bash
@@ -8,7 +8,7 @@ readonly ownership="zlib upstream <kwrobot@kitware.com>" readonly subtree="Utilities/cmzlib" readonly repo="https://github.com/madler/zlib.git" -readonly tag="v1.2.13" # When updating, sync Copyright.txt below! +readonly tag="v1.3.1" # When updating, sync Copyright.txt below! readonly shortlog=false readonly paths=" README @@ -45,7 +45,7 @@ pushd "${extractdir}/${name}-reduced" echo "* -whitespace" > .gitattributes echo -n "'zlib' general purpose compression library -version 1.2.13, October 13th, 2022 +version 1.3.1, January 22nd, 2024 Copyright " > Copyright.txt sed -n '/^ (C) 1995-/,+19 {s/^ \?//;p}' README >> Copyright.txt
diff --git a/Utilities/Scripts/update-zstd.bash b/Utilities/Scripts/update-zstd.bash index 48ca5a1..7bfb3dc 100755 --- a/Utilities/Scripts/update-zstd.bash +++ b/Utilities/Scripts/update-zstd.bash
@@ -8,7 +8,7 @@ readonly ownership="zstd upstream <kwrobot@kitware.com>" readonly subtree="Utilities/cmzstd" readonly repo="https://github.com/facebook/zstd.git" -readonly tag="v1.5.0" +readonly tag="v1.5.5" readonly shortlog=false readonly paths=" LICENSE
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index 4ffcdd7..746c872 100644 --- a/Utilities/Sphinx/CMakeLists.txt +++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeHelp_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...3.27 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.28 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/CTestCustom.cmake.in b/Utilities/Sphinx/CTestCustom.cmake.in index 840121b..ee13aa1 100644 --- a/Utilities/Sphinx/CTestCustom.cmake.in +++ b/Utilities/Sphinx/CTestCustom.cmake.in
@@ -1,3 +1,4 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "cmake.texi:[0-9]+: warning: .definfoenclose is obsolete" + "cmake.texi:[0-9]+: warning: @ref should not appear on @item line" )
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in index 20e1340..09a7d5a 100644 --- a/Utilities/Sphinx/conf.py.in +++ b/Utilities/Sphinx/conf.py.in
@@ -98,6 +98,7 @@ r'https://cdash\.org': r'https://www\.cdash\.org/', r'https://cmake.org/get-involved/': r'https://cmake.org/documentation/', r'https://docs\.nvidia\.com/cuda/': r'https://docs\.nvidia\.com/cuda/index\.html', + r'https://learn\.microsoft\.com/en-us/cpp/build/reference/openmp-enable-openmp-2-0-support': r'https://learn\.microsoft\.com/en-us/cpp/build/reference/openmp-enable-openmp-2-0-support\?.*', r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments': r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments\?.*', r'https://openjdk\.java\.net/jeps/313': r'https://openjdk\.org:443/jeps/313', r'https://www\.sphinx-doc\.org': r'https://www\.sphinx-doc\.org/en/master/',
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index 83d743d..483b9a2 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -228,7 +228,7 @@ #endif int main(void) { - int flags = 0; + unsigned long flags = 0; if(0 != ioctlsocket(0, FIONBIO, &flags)) return 1; ;
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake index 3957646..d3528cb 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
@@ -25,7 +25,7 @@ find_path(NGHTTP2_INCLUDE_DIR "nghttp2/nghttp2.h") -find_library(NGHTTP2_LIBRARY NAMES nghttp2) +find_library(NGHTTP2_LIBRARY NAMES nghttp2 nghttp2_static) find_package_handle_standard_args(NGHTTP2 FOUND_VAR
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index 9ff62ea..d5439fc 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -68,35 +68,6 @@ endif() endmacro() -macro(curl_nroff_check) - find_program(NROFF NAMES gnroff nroff) - if(NROFF) - # Need a way to write to stdin, this will do - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt" "test") - # Tests for a valid nroff option to generate a manpage - foreach(_MANOPT "-man" "-mandoc") - execute_process(COMMAND "${NROFF}" ${_MANOPT} - OUTPUT_VARIABLE NROFF_MANOPT_OUTPUT - INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt" - ERROR_QUIET) - # Save the option if it was valid - if(NROFF_MANOPT_OUTPUT) - message("Found *nroff option: -- ${_MANOPT}") - set(NROFF_MANOPT ${_MANOPT}) - set(NROFF_USEFUL ON) - break() - endif() - endforeach() - # No need for the temporary file - file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt") - if(NOT NROFF_USEFUL) - message(WARNING "Found no *nroff option to get plaintext from man pages") - endif() - else() - message(WARNING "Found no *nroff program") - endif() -endmacro() - macro(optional_dependency DEPENDENCY) set(CURL_${DEPENDENCY} AUTO CACHE STRING "Build curl with ${DEPENDENCY} support (AUTO, ON or OFF)") set_property(CACHE CURL_${DEPENDENCY} PROPERTY STRINGS AUTO ON OFF)
diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake index d82bbb1..d1183fe 100644 --- a/Utilities/cmcurl/CMake/PickyWarnings.cmake +++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake
@@ -25,7 +25,11 @@ unset(WPICKY) -if(CURL_WERROR AND CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0) +if(CURL_WERROR AND + ((CMAKE_COMPILER_IS_GNUCC AND + NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND + NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR # check_symbol_exists() incompatible with GCC -pedantic-errors in earlier CMake versions + CMAKE_C_COMPILER_ID MATCHES "Clang")) set(WPICKY "${WPICKY} -pedantic-errors") endif()
diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index d3391d9..082154f 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
@@ -137,6 +137,9 @@ set(HAVE_TERMIO_H 0) set(HAVE_UTIME_H 0) # mingw-w64 has it (wrapper to sys/utime.h) +set(HAVE_DIRENT_H 0) +set(HAVE_OPENDIR 0) + set(HAVE_FSEEKO 0) set(HAVE__FSEEKI64 1) set(HAVE_SOCKET 1)
diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake index 9ff38e3..84a40f4 100644 --- a/Utilities/cmcurl/CMake/Utilities.cmake +++ b/Utilities/cmcurl/CMake/Utilities.cmake
@@ -23,7 +23,7 @@ ########################################################################### # File containing various utilities -# Returns a list of arguments that evaluate to true +# Returns number of arguments that evaluate to true function(count_true output_count_var) set(lst_len 0) foreach(option_var IN LISTS ARGN)
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 3dd24f1..b766aee 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt
@@ -90,9 +90,13 @@ set(HTTP_ONLY OFF CACHE INTERNAL "Curl is not http-only") set(PICKY_COMPILER OFF CACHE INTERNAL "Enable picky compiler options") set(SHARE_LIB_OBJECT OFF) +set(USE_ECH OFF) +set(USE_HTTPSRR OFF) set(USE_LIBIDN2 ON) +set(USE_LIBRTMP OFF) set(USE_NGHTTP2 ON) set(USE_NGTCP2 OFF) +set(USE_OPENSSL_QUIC OFF) set(USE_QUICHE OFF) set(USE_WIN32_IDN OFF) set(USE_WIN32_LDAP OFF CACHE INTERNAL "No curl Windows LDAP") @@ -195,6 +199,7 @@ # HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS # HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL # HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE +# HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL # # For each of the above variables, if the variable is DEFINED (either # to ON or OFF), the symbol detection will be skipped. If the @@ -470,23 +475,23 @@ list(APPEND CURL_LIBS "-framework SystemConfiguration") endif() endif() +if(ENABLE_IPV6) + set(USE_IPV6 ON) +endif() if(0) # This code not needed for building within CMake. find_package(Perl) option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON) -# curl source release tarballs come with the curl man page pre-built. -option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" OFF) +option(BUILD_MISC_DOCS "to build misc man pages (e.g. curl-config and mk-ca-bundle)" ON) +option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" ON) if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) if(PERL_FOUND) - curl_nroff_check() - if(NROFF_USEFUL) - set(HAVE_MANUAL_TOOLS ON) - endif() + set(HAVE_MANUAL_TOOLS ON) endif() if(NOT HAVE_MANUAL_TOOLS) - message(WARNING "Perl not found, or nroff not useful. Will not build manuals.") + message(WARNING "Perl not found. Will not build manuals.") endif() endif() endif() @@ -548,9 +553,6 @@ if(WIN32) list(APPEND CURL_LIBS "ws2_32" "bcrypt") - if(USE_LIBRTMP) - list(APPEND CURL_LIBS "winmm") - endif() endif() if(0) # This code not needed for building within CMake. @@ -566,8 +568,7 @@ endif() if(WIN32) cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF) - cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without OpenSSL" ON - CURL_USE_SCHANNEL OFF) + option(CURL_WINDOWS_SSPI "Enable SSPI on Windows" ${CURL_USE_SCHANNEL}) endif() cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) @@ -612,7 +613,7 @@ find_library(SECURITY_FRAMEWORK "Security") if(NOT SECURITY_FRAMEWORK) - message(FATAL_ERROR "Security framework not found") + message(FATAL_ERROR "Security framework not found") endif() set(SSL_ENABLED ON) @@ -629,10 +630,10 @@ find_library(CORESERVICES_FRAMEWORK "CoreServices") if(NOT COREFOUNDATION_FRAMEWORK) - message(FATAL_ERROR "CoreFoundation framework not found") + message(FATAL_ERROR "CoreFoundation framework not found") endif() if(NOT CORESERVICES_FRAMEWORK) - message(FATAL_ERROR "CoreServices framework not found") + message(FATAL_ERROR "CoreServices framework not found") endif() list(APPEND CURL_LIBS "-framework CoreFoundation -framework CoreServices") @@ -784,8 +785,8 @@ endif() endif() -# Check symbol in OpenSSL-like TLS backends. -macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE) +# Check symbol in an OpenSSL-like TLS backend, or in EXTRA_LIBS depending on it. +macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE EXTRA_LIBS) cmake_push_check_state() if(USE_OPENSSL) set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") @@ -809,6 +810,9 @@ endif() list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T) # to pull in stdint.h (as of wolfSSL v5.5.4) endif() + if(NOT "${EXTRA_LIBS}" STREQUAL "") + list(APPEND CMAKE_REQUIRED_LIBRARIES "${EXTRA_LIBS}") + endif() check_symbol_exists("${SYMBOL}" "${FILES}" "${VARIABLE}") cmake_pop_check_state() endmacro() @@ -817,9 +821,9 @@ macro(openssl_check_quic) if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) if(USE_OPENSSL) - openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD "") elseif(USE_WOLFSSL) - openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD "") endif() endif() if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) @@ -829,10 +833,34 @@ if(USE_OPENSSL OR USE_WOLFSSL) if(NOT DEFINED HAVE_SSL_SET0_WBIO) - openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO) + openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO "") endif() if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP) - openssl_check_symbol_exists(SSL_CTX_set_srp_username "openssl/ssl.h" HAVE_OPENSSL_SRP) + openssl_check_symbol_exists(SSL_CTX_set_srp_username "openssl/ssl.h" HAVE_OPENSSL_SRP "") + endif() +endif() + +option(USE_HTTPSRR "Enable HTTPS RR support for ECH (experimental)" OFF) +option(USE_ECH "Enable ECH support" OFF) +if(USE_ECH) + if(USE_OPENSSL OR USE_WOLFSSL) + # Be sure that the TLS library actually supports ECH. + if(NOT DEFINED HAVE_ECH) + if(USE_OPENSSL AND HAVE_BORINGSSL) + openssl_check_symbol_exists(SSL_set1_ech_config_list "openssl/ssl.h" HAVE_ECH "") + elseif(USE_OPENSSL) + openssl_check_symbol_exists(SSL_ech_set1_echconfig "openssl/ech.h" HAVE_ECH "") + elseif(USE_WOLFSSL) + openssl_check_symbol_exists(wolfSSL_CTX_GenerateEchConfig "wolfssl/options.h;wolfssl/ssl.h" HAVE_ECH "") + endif() + endif() + if(NOT HAVE_ECH) + message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/wolfSSL") + else() + message(STATUS "ECH enabled.") + endif() + else() + message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL or wolfSSL") endif() endif() @@ -901,7 +929,28 @@ list(APPEND CURL_LIBS ${MSH3_LIBRARIES}) endif() -if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3)) +option(USE_OPENSSL_QUIC "Use openssl and nghttp3 libraries for HTTP/3 support" OFF) +if(USE_OPENSSL_QUIC) + if(USE_NGTCP2 OR USE_QUICHE OR USE_MSH3) + message(FATAL_ERROR "Only one HTTP/3 backend can be selected!") + endif() + find_package(OpenSSL 3.2.0 REQUIRED) + + find_package(NGHTTP3 REQUIRED) + set(USE_NGHTTP3 ON) + include_directories(${NGHTTP3_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) +endif() + +if(0) # This code not needed for building within CMake. +if(USE_MBEDTLS OR + USE_BEARSSL OR + USE_SECTRANSP) + message(WARNING "A selected TLS library does not support TLS 1.3.") +endif() +endif() + +if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)) message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") endif() @@ -1037,6 +1086,21 @@ endif() endif() +if(APPLE) + option(USE_APPLE_IDN "Use Apple built-in IDN support" OFF) + if(USE_APPLE_IDN) + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES "icucore") + check_symbol_exists("uidna_openUTS46" "unicode/uidna.h" HAVE_APPLE_IDN) + cmake_pop_check_state() + if(HAVE_APPLE_IDN) + list(APPEND CURL_LIBS "icucore") + else() + set(USE_APPLE_IDN OFF) + endif() + endif() +endif() + #libpsl option(CURL_USE_LIBPSL "Use libPSL" ON) mark_as_advanced(CURL_USE_LIBPSL) @@ -1133,7 +1197,6 @@ string(REPLACE ";" " " GSS_LINKER_FLAGS "${GSS_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") - set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") list(APPEND CURL_LIBS ${GSS_LIBRARIES}) else() @@ -1141,6 +1204,26 @@ endif() endif() +option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) +if(USE_LIBRTMP) + cmake_push_check_state() + set(_extra_libs "rtmp") + if(WIN32) + list(APPEND _extra_libs "winmm") + endif() + openssl_check_symbol_exists("RTMP_Init" "librtmp/rtmp.h" HAVE_LIBRTMP "${_extra_libs}") + cmake_pop_check_state() + if(HAVE_LIBRTMP) + list(APPEND CURL_LIBS "rtmp") + if(WIN32) + list(APPEND CURL_LIBS "winmm") + endif() + else() + message(WARNING "librtmp requested, but not found or missing OpenSSL. Skipping.") + set(USE_LIBRTMP OFF) + endif() +endif() + option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON) if(ENABLE_UNIX_SOCKETS) include(CheckStructHasMember) @@ -1294,6 +1377,7 @@ check_include_file_concat("sys/utime.h" HAVE_SYS_UTIME_H) check_include_file_concat("sys/xattr.h" HAVE_SYS_XATTR_H) check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) +check_include_file_concat("dirent.h" HAVE_DIRENT_H) check_include_file_concat("fcntl.h" HAVE_FCNTL_H) check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) check_include_file_concat("io.h" HAVE_IO_H) @@ -1342,6 +1426,7 @@ check_symbol_exists(fnmatch "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) check_symbol_exists(basename "${CURL_INCLUDES};string.h" HAVE_BASENAME) +check_symbol_exists(opendir "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR) check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) check_symbol_exists(sched_yield "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD) check_symbol_exists(socketpair "${CURL_INCLUDES}" HAVE_SOCKETPAIR) @@ -1383,7 +1468,6 @@ check_symbol_exists(freeaddrinfo "${CURL_INCLUDES}" HAVE_FREEADDRINFO) check_symbol_exists(pipe "${CURL_INCLUDES}" HAVE_PIPE) check_symbol_exists(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE) -check_symbol_exists(fseeko "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) check_symbol_exists(_fseeki64 "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64) check_symbol_exists(getpeername "${CURL_INCLUDES}" HAVE_GETPEERNAME) check_symbol_exists(getsockname "${CURL_INCLUDES}" HAVE_GETSOCKNAME) @@ -1393,10 +1477,6 @@ check_symbol_exists(setmode "${CURL_INCLUDES}" HAVE_SETMODE) check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT) -if(HAVE_FSEEKO) - set(HAVE_DECL_FSEEKO 1) -endif() - if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900)) # earlier MSVC compilers had faulty snprintf implementations check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF) @@ -1459,6 +1539,15 @@ endif() check_type_size("off_t" SIZEOF_OFF_T) +# fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with +# _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24) +# so we need to test fseeko after testing for _FILE_OFFSET_BITS +check_symbol_exists(fseeko "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) + +if(HAVE_FSEEKO) + set(HAVE_DECL_FSEEKO 1) +endif() + # include this header to get the type set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include") set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") @@ -1670,6 +1759,10 @@ set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") +cmake_dependent_option(BUILD_TESTING "Build tests" + ON "PERL_FOUND;NOT CURL_DISABLE_TESTS" + OFF) + if(HAVE_MANUAL_TOOLS) add_subdirectory(docs) endif() @@ -1680,15 +1773,24 @@ add_subdirectory(src) endif() -cmake_dependent_option(BUILD_TESTING "Build tests" - ON "PERL_FOUND;NOT CURL_DISABLE_TESTS" - OFF) +option(BUILD_EXAMPLES "Build libcurl examples" OFF) +if(BUILD_EXAMPLES) + add_subdirectory(docs/examples) +endif() + if(BUILD_TESTING) add_subdirectory(tests) endif() if(NOT CURL_DISABLE_INSTALL) + install(FILES "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + # Helper to populate a list (_items) with a label when conditions (the remaining # args) are satisfied macro(_add_if label) @@ -1713,7 +1815,7 @@ _add_if("brotli" HAVE_BROTLI) _add_if("zstd" HAVE_ZSTD) _add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) - _add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN) + _add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN OR USE_APPLE_IDN) _add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) _add_if("SSPI" USE_WINDOWS_SSPI) @@ -1726,12 +1828,9 @@ (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) _add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND (use_curl_ntlm_core OR USE_WINDOWS_SSPI)) - _add_if("NTLM_WB" NOT (CURL_DISABLE_NTLM) AND - (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND - NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) _add_if("TLS-SRP" USE_TLS_SRP) _add_if("HTTP2" USE_NGHTTP2) - _add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) + _add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC) _add_if("MultiSSL" CURL_WITH_MULTI_SSL) # TODO wolfSSL only support this from v5.0.0 onwards _add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS @@ -1751,6 +1850,8 @@ _add_if("IPFS" NOT CURL_DISABLE_HTTP) _add_if("IPNS" NOT CURL_DISABLE_HTTP) _add_if("HTTPS" NOT CURL_DISABLE_HTTP AND SSL_ENABLED) + _add_if("ECH" HAVE_ECH) + _add_if("HTTPSRR" HAVE_ECH) _add_if("FTP" NOT CURL_DISABLE_FTP) _add_if("FTPS" NOT CURL_DISABLE_FTP AND SSL_ENABLED) _add_if("FILE" NOT CURL_DISABLE_FILE) @@ -1816,6 +1917,30 @@ set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") set(LIBCURL_LIBS "") set(libdir "${CMAKE_INSTALL_PREFIX}/lib") + + # For processing full path libraries into -L and -l ld options, + # the directories that go with the -L option are cached, so they + # only get added once per such directory. + set(_libcurl_libs_dirs) + # To avoid getting unnecessary -L options for known system directories, + # _libcurl_libs_dirs is seeded with them. + foreach(_libdir ${CMAKE_SYSTEM_PREFIX_PATH}) + if(_libdir MATCHES "/$") + set(_libdir "${_libdir}lib") + else() + set(_libdir "${_libdir}/lib") + endif() + if(IS_DIRECTORY "${_libdir}") + list(APPEND _libcurl_libs_dirs "${_libdir}") + endif() + if(DEFINED CMAKE_LIBRARY_ARCHITECTURE) + set(_libdir "${_libdir}/${CMAKE_LIBRARY_ARCHITECTURE}") + if(IS_DIRECTORY "${_libdir}") + list(APPEND _libcurl_libs_dirs "${_libdir}") + endif() + endif() + endforeach() + foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) if(TARGET "${_lib}") set(_libname "${_lib}") @@ -1831,8 +1956,24 @@ continue() endif() endif() - if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-") + if(_lib MATCHES "^-") set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") + elseif(_lib MATCHES ".*/.*") + # This gets a bit more complex, because we want to specify the + # directory separately, and only once per directory + string(REGEX REPLACE "^(.*)/[^/]*$" "\\1" _libdir "${_lib}") + string(REGEX REPLACE "^.*/([^/.]*).*$" "\\1" _libname "${_lib}") + if(_libname MATCHES "^lib") + list(FIND _libcurl_libs_dirs "${_libdir}" _libdir_index) + if(_libdir_index LESS 0) + list(APPEND _libcurl_libs_dirs "${_libdir}") + set(LIBCURL_LIBS "${LIBCURL_LIBS} -L${_libdir}") + endif() + string(REGEX REPLACE "^lib" "" _libname "${_libname}") + set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_libname}") + else() + set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") + endif() else() set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}") endif()
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 5b9bcc6..6d6abfb 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h
@@ -632,6 +632,7 @@ CURLE_SSL_CLIENTCERT, /* 98 - client-side certificate required */ CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */ CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */ + CURLE_ECH_REQUIRED, /* 101 - ECH tried but failed */ CURL_LAST /* never use! */ } CURLcode; @@ -811,7 +812,10 @@ #define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE #define CURLAUTH_NTLM (((unsigned long)1)<<3) #define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#ifndef CURL_NO_OLDIES + /* functionality removed since 8.8.0 */ #define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#endif #define CURLAUTH_BEARER (((unsigned long)1)<<6) #define CURLAUTH_AWS_SIGV4 (((unsigned long)1)<<7) #define CURLAUTH_ONLY (((unsigned long)1)<<31) @@ -2206,6 +2210,9 @@ /* millisecond version */ CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324), + /* set ECH configuration */ + CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 325), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2311,30 +2318,26 @@ CURL_NETRC_LAST }; -enum { - CURL_SSLVERSION_DEFAULT, - CURL_SSLVERSION_TLSv1, /* TLS 1.x */ - CURL_SSLVERSION_SSLv2, - CURL_SSLVERSION_SSLv3, - CURL_SSLVERSION_TLSv1_0, - CURL_SSLVERSION_TLSv1_1, - CURL_SSLVERSION_TLSv1_2, - CURL_SSLVERSION_TLSv1_3, +#define CURL_SSLVERSION_DEFAULT 0 +#define CURL_SSLVERSION_TLSv1 1 /* TLS 1.x */ +#define CURL_SSLVERSION_SSLv2 2 +#define CURL_SSLVERSION_SSLv3 3 +#define CURL_SSLVERSION_TLSv1_0 4 +#define CURL_SSLVERSION_TLSv1_1 5 +#define CURL_SSLVERSION_TLSv1_2 6 +#define CURL_SSLVERSION_TLSv1_3 7 - CURL_SSLVERSION_LAST /* never use, keep last */ -}; +#define CURL_SSLVERSION_LAST 8 /* never use, keep last */ -enum { - CURL_SSLVERSION_MAX_NONE = 0, - CURL_SSLVERSION_MAX_DEFAULT = (CURL_SSLVERSION_TLSv1 << 16), - CURL_SSLVERSION_MAX_TLSv1_0 = (CURL_SSLVERSION_TLSv1_0 << 16), - CURL_SSLVERSION_MAX_TLSv1_1 = (CURL_SSLVERSION_TLSv1_1 << 16), - CURL_SSLVERSION_MAX_TLSv1_2 = (CURL_SSLVERSION_TLSv1_2 << 16), - CURL_SSLVERSION_MAX_TLSv1_3 = (CURL_SSLVERSION_TLSv1_3 << 16), +#define CURL_SSLVERSION_MAX_NONE 0 +#define CURL_SSLVERSION_MAX_DEFAULT (CURL_SSLVERSION_TLSv1 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_0 (CURL_SSLVERSION_TLSv1_0 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_1 (CURL_SSLVERSION_TLSv1_1 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_2 (CURL_SSLVERSION_TLSv1_2 << 16) +#define CURL_SSLVERSION_MAX_TLSv1_3 (CURL_SSLVERSION_TLSv1_3 << 16) /* never use, keep last */ - CURL_SSLVERSION_MAX_LAST = (CURL_SSLVERSION_LAST << 16) -}; +#define CURL_SSLVERSION_MAX_LAST (CURL_SSLVERSION_LAST << 16) enum CURL_TLSAUTH { CURL_TLSAUTH_NONE, @@ -2938,7 +2941,8 @@ CURLINFO_XFER_ID = CURLINFO_OFF_T + 63, CURLINFO_CONN_ID = CURLINFO_OFF_T + 64, CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65, - CURLINFO_LASTONE = 65 + CURLINFO_USED_PROXY = CURLINFO_LONG + 66, + CURLINFO_LASTONE = 66 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -3034,17 +3038,18 @@ */ typedef enum { - CURLVERSION_FIRST, - CURLVERSION_SECOND, - CURLVERSION_THIRD, - CURLVERSION_FOURTH, - CURLVERSION_FIFTH, - CURLVERSION_SIXTH, - CURLVERSION_SEVENTH, - CURLVERSION_EIGHTH, - CURLVERSION_NINTH, - CURLVERSION_TENTH, - CURLVERSION_ELEVENTH, + CURLVERSION_FIRST, /* 7.10 */ + CURLVERSION_SECOND, /* 7.11.1 */ + CURLVERSION_THIRD, /* 7.12.0 */ + CURLVERSION_FOURTH, /* 7.16.1 */ + CURLVERSION_FIFTH, /* 7.57.0 */ + CURLVERSION_SIXTH, /* 7.66.0 */ + CURLVERSION_SEVENTH, /* 7.70.0 */ + CURLVERSION_EIGHTH, /* 7.72.0 */ + CURLVERSION_NINTH, /* 7.75.0 */ + CURLVERSION_TENTH, /* 7.77.0 */ + CURLVERSION_ELEVENTH, /* 7.87.0 */ + CURLVERSION_TWELFTH, /* 8.8.0 */ CURLVERSION_LAST /* never actually use this */ } CURLversion; @@ -3053,7 +3058,7 @@ 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_ELEVENTH +#define CURLVERSION_NOW CURLVERSION_TWELFTH struct curl_version_info_data { CURLversion age; /* age of the returned struct */ @@ -3113,6 +3118,9 @@ /* These fields were added in CURLVERSION_ELEVENTH */ /* feature_names is terminated by an entry with a NULL feature name */ const char * const *feature_names; + + /* These fields were added in CURLVERSION_TWELFTH */ + const char *rtmp_version; /* human readable string. */ }; typedef struct curl_version_info_data curl_version_info_data; @@ -3153,7 +3161,7 @@ #define CURL_VERSION_GSASL (1<<29) /* libgsasl is supported */ #define CURL_VERSION_THREADSAFE (1<<30) /* libcurl API is thread-safe */ - /* +/* * NAME curl_version_info() * * DESCRIPTION
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 85008e1..e63ad22 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,12 +32,12 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.6.0" +#define LIBCURL_VERSION "8.8.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 6 +#define LIBCURL_VERSION_MINOR 8 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080600 +#define LIBCURL_VERSION_NUM 0x080800 /* * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index e79b48f..561470c 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h
@@ -464,6 +464,20 @@ struct curl_pushheaders *headers, void *userp); +/* + * Name: curl_multi_waitfds() + * + * 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. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi, + struct curl_waitfd *ufds, + unsigned int size, + unsigned int *fd_count); + #ifdef __cplusplus } /* end of extern "C" */ #endif
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index b880f3d..873a49e 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -275,6 +275,7 @@ (option) == CURLOPT_DNS_LOCAL_IP6 || \ (option) == CURLOPT_DNS_SERVERS || \ (option) == CURLOPT_DOH_URL || \ + (option) == CURLOPT_ECH || \ (option) == CURLOPT_EGDSOCKET || \ (option) == CURLOPT_FTP_ACCOUNT || \ (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index 91f8c45..19388c3 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -99,6 +99,9 @@ #define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ #define CURLU_PUNYCODE (1<<12) /* get the host name in punycode */ #define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ +#define CURLU_GET_EMPTY (1<<14) /* allow empty queries and fragments + when extracting the URL or the + components */ typedef struct Curl_URL CURLU;
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index bf25d89..ca88a4d 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -31,9 +31,10 @@ transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake) -list(APPEND HHEADERS - ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h - ) +# DllMain is added later for DLL builds only. +list(REMOVE_ITEM CSOURCES dllmain.c) + +list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) # The rest of the build @@ -93,6 +94,7 @@ ${HHEADERS} ${CSOURCES} ) target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) + target_link_libraries(curlu PRIVATE ${CURL_LIBS}) endif() if(ENABLE_CURLDEBUG) @@ -101,10 +103,6 @@ set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() -if(BUILD_TESTING) - target_link_libraries(curlu PRIVATE ${CURL_LIBS}) -endif() - transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) @@ -152,17 +150,17 @@ endif() endif() -if(WIN32) - # Define CURL_STATICLIB always, to disable __declspec(dllexport) for exported - # libcurl symbols. We handle exports via libcurl.def instead. Except with - # symbol hiding disabled or debug mode enabled, when we export _all_ symbols - # from libcurl DLL, without using libcurl.def. - add_definitions("-DCURL_STATICLIB") -endif() - if(SHARE_LIB_OBJECT) set(LIB_OBJECT "libcurl_object") 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. + # Except with symbol hiding disabled or debug mode enabled, when we export + # _all_ symbols from libcurl DLL, without using libcurl.def. + set_property(TARGET ${LIB_OBJECT} APPEND + PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") + endif() target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) @@ -190,6 +188,10 @@ list(APPEND libcurl_export ${LIB_STATIC}) add_library(${LIB_STATIC} STATIC ${LIB_SOURCE}) add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC}) + if(WIN32) + set_property(TARGET ${LIB_OBJECT} APPEND + PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") + endif() target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS}) # Remove the "lib" prefix since the library is already named "libcurl". set_target_properties(${LIB_STATIC} PROPERTIES @@ -219,6 +221,15 @@ list(APPEND libcurl_export ${LIB_SHARED}) add_library(${LIB_SHARED} SHARED ${LIB_SOURCE}) add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED}) + if(WIN32 OR CYGWIN) + if(CYGWIN) + # For cygwin always compile dllmain.c as a separate unit since it + # includes windows.h, which shouldn't be included in other units. + set_source_files_properties(dllmain.c PROPERTIES + SKIP_UNITY_BUILD_INCLUSION ON) + endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES dllmain.c) + endif() if(WIN32) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc) if(HIDES_CURL_PRIVATE_SYMBOLS)
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 627148a..66680f3 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -44,6 +44,7 @@ LIB_VTLS_CFILES = \ vtls/bearssl.c \ + vtls/cipher_suite.c \ vtls/gtls.c \ vtls/hostcheck.c \ vtls/keylog.c \ @@ -60,6 +61,7 @@ LIB_VTLS_HFILES = \ vtls/bearssl.h \ + vtls/cipher_suite.h \ vtls/gtls.h \ vtls/hostcheck.h \ vtls/keylog.h \ @@ -129,15 +131,17 @@ curl_memrchr.c \ curl_multibyte.c \ curl_ntlm_core.c \ - curl_ntlm_wb.c \ curl_path.c \ curl_range.c \ curl_rtmp.c \ curl_sasl.c \ + curl_sha512_256.c \ curl_sspi.c \ curl_threads.c \ curl_trc.c \ + cw-out.c \ dict.c \ + dllmain.c \ doh.c \ dynbuf.c \ dynhds.c \ @@ -199,6 +203,7 @@ psl.c \ rand.c \ rename.c \ + request.c \ rtsp.c \ select.c \ sendf.c \ @@ -268,7 +273,6 @@ curl_memrchr.h \ curl_multibyte.h \ curl_ntlm_core.h \ - curl_ntlm_wb.h \ curl_path.h \ curl_printf.h \ curl_range.h \ @@ -277,10 +281,12 @@ curl_setup.h \ curl_setup_once.h \ curl_sha256.h \ + curl_sha512_256.h \ curl_sspi.h \ curl_threads.h \ curl_trc.h \ curlx.h \ + cw-out.h \ dict.h \ doh.h \ dynbuf.h \ @@ -333,6 +339,7 @@ psl.h \ rand.h \ rename.h \ + request.h \ rtsp.h \ select.h \ sendf.h \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index e9f62bf..b72a596 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c
@@ -191,7 +191,7 @@ as->expires = expires; as->prio = prio; as->persist = persist ? 1 : 0; - Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); + Curl_llist_append(&asi->list, as, &as->node); } } @@ -209,7 +209,6 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) { CURLcode result = CURLE_OK; - char *line = NULL; FILE *fp; /* we need a private copy of the file name so that the altsvc cache file @@ -221,11 +220,10 @@ fp = fopen(file, FOPEN_READTEXT); if(fp) { - line = malloc(MAX_ALTSVC_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) { - char *lineptr = line; + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_ALTSVC_LINE); + while(Curl_get_line(&buf, fp)) { + char *lineptr = Curl_dyn_ptr(&buf); while(*lineptr && ISBLANK(*lineptr)) lineptr++; if(*lineptr == '#') @@ -234,16 +232,10 @@ altsvc_add(asi, lineptr); } - free(line); /* free the line buffer */ + Curl_dyn_free(&buf); /* free the line buffer */ fclose(fp); } return result; - -fail: - Curl_safefree(asi->filename); - free(line); - fclose(fp); - return CURLE_OUT_OF_MEMORY; } /* @@ -260,7 +252,7 @@ CURLcode result = Curl_gmtime(as->expires, &stamp); if(result) return result; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else { char ipv6_unused[16]; if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) { @@ -311,7 +303,7 @@ #ifdef USE_HTTP2 | CURLALTSVC_H2 #endif -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 | CURLALTSVC_H3 #endif ; @@ -651,7 +643,7 @@ account. [See RFC 7838 section 3.1] */ as->expires = maxage + time(NULL); as->persist = persist; - Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); + Curl_llist_append(&asi->list, as, &as->node); infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport, Curl_alpnid2str(dstalpnid)); }
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 76efba7..8fed617 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -122,6 +122,8 @@ #define CARES_TIMEOUT_PER_ATTEMPT 2000 +static int ares_ver = 0; + /* * Curl_resolver_global_init() - the generic low-level asynchronous name * resolve API. Called from curl_global_init() to initialize global resolver @@ -134,6 +136,7 @@ return CURLE_FAILED_INIT; } #endif + ares_version(&ares_ver); return CURLE_OK; } @@ -173,16 +176,8 @@ int status; struct ares_options options; int optmask = ARES_OPT_SOCK_STATE_CB; - static int ares_ver = 0; options.sock_state_cb = sock_state_cb; options.sock_state_cb_data = easy; - if(ares_ver == 0) - ares_version(&ares_ver); - - if(ares_ver < 0x011400) { /* c-ares included similar change since 1.20.0 */ - options.timeout = CARES_TIMEOUT_PER_ATTEMPT; - optmask |= ARES_OPT_TIMEOUTMS; - } /* if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s) @@ -193,6 +188,11 @@ if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to overwrite c-ares' timeout. */ + DEBUGASSERT(ares_ver); + if(ares_ver < 0x011400) { + options.timeout = CARES_TIMEOUT_PER_ATTEMPT; + optmask |= ARES_OPT_TIMEOUTMS; + } status = ares_init_options((ares_channel*)resolver, &options, optmask); if(status != ARES_SUCCESS) { @@ -539,7 +539,7 @@ if(!ai) return; -#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ +#ifdef USE_IPV6 /* CURLRES_IPV6 */ if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) { /* We have results already, put the new IPv6 entries at the head of the list. */ @@ -684,7 +684,7 @@ /* settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else if(ai->ai_family == AF_INET6) ss_size = sizeof(struct sockaddr_in6); #endif @@ -850,7 +850,7 @@ /* If server is NULL or empty, this would purge all DNS servers * from ares library, which will cause any and all queries to fail. * So, just return OK if none are configured and don't actually make - * any changes to c-ares. This lets c-ares use it's defaults, which + * any changes to c-ares. This lets c-ares use its defaults, which * it gets from the OS (for instance from /etc/resolv.conf on Linux). */ if(!(servers && servers[0])) @@ -932,7 +932,7 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, const char *local_ip6) { -#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6) +#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6) unsigned char a6[INET6_ADDRSTRLEN]; if((!local_ip6) || (local_ip6[0] == 0)) {
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index d4d382a..1760d6c 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -325,7 +325,7 @@ /* settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else if(ai->ai_family == AF_INET6) ss_size = sizeof(struct sockaddr_in6); #endif @@ -444,7 +444,7 @@ /* * getaddrinfo_thread() resolves a name and then exits. * - * For builds without ARES, but with ENABLE_IPV6, create a resolver thread + * For builds without ARES, but with USE_IPV6, create a resolver thread * and wait on it. */ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) @@ -554,11 +554,15 @@ if(!done) { #ifdef _WIN32 - if(td->complete_ev) + if(td->complete_ev) { CloseHandle(td->complete_ev); - else + td->complete_ev = NULL; + } #endif - Curl_thread_destroy(td->thread_hnd); + if(td->thread_hnd != curl_thread_t_null) { + Curl_thread_destroy(td->thread_hnd); + td->thread_hnd = curl_thread_t_null; + } } else { #ifdef _WIN32 @@ -566,6 +570,7 @@ Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); WaitForSingleObject(td->complete_ev, INFINITE); CloseHandle(td->complete_ev); + td->complete_ev = NULL; } #endif if(td->thread_hnd != curl_thread_t_null) @@ -581,7 +586,7 @@ * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL */ Curl_multi_closed(data, sock_rd); - sclose(sock_rd); + wakeup_close(sock_rd); #endif } async->tdata = NULL; @@ -672,7 +677,7 @@ td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); #endif - if(!td->thread_hnd) { + if(td->thread_hnd == curl_thread_t_null) { /* The thread never started, so mark it as done here for proper cleanup. */ td->tsd.done = 1; err = errno; @@ -713,6 +718,7 @@ if(td->complete_ev) { WaitForSingleObject(td->complete_ev, INFINITE); CloseHandle(td->complete_ev); + td->complete_ev = NULL; if(entry) result = getaddrinfo_complete(data); } @@ -754,6 +760,13 @@ /* If we're still resolving, we must wait for the threads to fully clean up, unfortunately. Otherwise, we can simply cancel to clean up any resolver data. */ +#ifdef _WIN32 + if(td && td->complete_ev) { + Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); + (void)thread_wait_resolv(data, NULL, FALSE); + } + else +#endif if(td && td->thread_hnd != curl_thread_t_null && (data->set.quick_exit != 1L)) (void)thread_wait_resolv(data, NULL, FALSE);
diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c index 919eb62..8373115 100644 --- a/Utilities/cmcurl/lib/base64.c +++ b/Utilities/cmcurl/lib/base64.c
@@ -243,7 +243,7 @@ *outptr = base64data; /* Return the length of the new data */ - *outlen = output - base64data; + *outlen = (size_t)(output - base64data); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/bufq.c b/Utilities/cmcurl/lib/bufq.c index d03906d..c324551 100644 --- a/Utilities/cmcurl/lib/bufq.c +++ b/Utilities/cmcurl/lib/bufq.c
@@ -396,7 +396,7 @@ while(len) { tail = get_non_full_tail(q); if(!tail) { - if(q->chunk_count < q->max_chunks) { + if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) { *err = CURLE_OUT_OF_MEMORY; return -1; } @@ -417,6 +417,17 @@ return nwritten; } +CURLcode Curl_bufq_cwrite(struct bufq *q, + const char *buf, size_t len, + size_t *pnwritten) +{ + ssize_t n; + CURLcode result; + n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result); + *pnwritten = (n < 0)? 0 : (size_t)n; + return result; +} + ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode *err) { @@ -440,6 +451,16 @@ return nread; } +CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, + size_t *pnread) +{ + ssize_t n; + CURLcode result; + n = Curl_bufq_read(q, (unsigned char *)buf, len, &result); + *pnread = (n < 0)? 0 : (size_t)n; + return result; +} + bool Curl_bufq_peek(struct bufq *q, const unsigned char **pbuf, size_t *plen) {
diff --git a/Utilities/cmcurl/lib/bufq.h b/Utilities/cmcurl/lib/bufq.h index 089d61b..87ffa45 100644 --- a/Utilities/cmcurl/lib/bufq.h +++ b/Utilities/cmcurl/lib/bufq.h
@@ -85,7 +85,7 @@ * preferably never fail (except for memory exhaustion). * * By default and without a pool, a bufq will keep chunks that read - * read empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will + * empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will * disable that and free chunks once they become empty. * * When providing a pool to a bufq, all chunk creation and spare handling @@ -178,6 +178,10 @@ const unsigned char *buf, size_t len, CURLcode *err); +CURLcode Curl_bufq_cwrite(struct bufq *q, + const char *buf, size_t len, + size_t *pnwritten); + /** * Read buf from the start of the buffer queue. The buf is copied * and the amount of copied bytes is returned. @@ -187,6 +191,9 @@ ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode *err); +CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, + size_t *pnread); + /** * Peek at the head chunk in the buffer queue. Returns a pointer to * the chunk buf (at the current offset) and its length. Does not
diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index d02ecd7..0593d97 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c
@@ -53,7 +53,9 @@ #include <hyper.h> #include "urldata.h" +#include "cfilters.h" #include "sendf.h" +#include "headers.h" #include "transfer.h" #include "multiif.h" #include "progress.h" @@ -65,6 +67,9 @@ #include "curl_memory.h" #include "memdebug.h" + +static CURLcode cr_hyper_add(struct Curl_easy *data); + typedef enum { USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */ USERDATA_RESP_BODY @@ -73,7 +78,8 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx, uint8_t *buf, size_t buflen) { - struct Curl_easy *data = userp; + struct hyp_io_ctx *io_ctx = userp; + struct Curl_easy *data = io_ctx->data; struct connectdata *conn = data->conn; CURLcode result; ssize_t nread; @@ -81,7 +87,8 @@ (void)ctx; DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen)); - result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread); + result = Curl_conn_recv(data, io_ctx->sockindex, + (char *)buf, buflen, &nread); if(result == CURLE_AGAIN) { /* would block, register interest */ DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen)); @@ -105,15 +112,14 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx, const uint8_t *buf, size_t buflen) { - struct Curl_easy *data = userp; - struct connectdata *conn = data->conn; + struct hyp_io_ctx *io_ctx = userp; + struct Curl_easy *data = io_ctx->data; CURLcode result; - ssize_t nwrote; + size_t nwrote; DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen)); - result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote); - if(!result && !nwrote) - result = CURLE_AGAIN; + result = Curl_conn_send(data, io_ctx->sockindex, + (void *)buf, buflen, &nwrote); if(result == CURLE_AGAIN) { DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen)); /* would block, register interest */ @@ -152,9 +158,6 @@ return HYPER_ITER_BREAK; } - if(!data->req.bytecount) - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - Curl_dyn_reset(&data->state.headerb); if(name_len) { if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n", @@ -168,7 +171,7 @@ len = Curl_dyn_len(&data->state.headerb); headp = Curl_dyn_ptr(&data->state.headerb); - result = Curl_http_header(data, data->conn, headp); + result = Curl_http_header(data, headp, len); if(result) { data->state.hresult = result; return HYPER_ITER_BREAK; @@ -204,7 +207,6 @@ CURLcode result = CURLE_OK; if(0 == k->bodywrites) { - bool done = FALSE; #if defined(USE_NTLM) struct connectdata *conn = data->conn; if(conn->bits.close && @@ -217,27 +219,26 @@ Curl_safefree(data->req.newurl); } #endif - if(data->state.expect100header) { - Curl_expire_done(data, EXPIRE_100_TIMEOUT); + if(Curl_http_exp100_is_selected(data)) { if(data->req.httpcode < 400) { - k->exp100 = EXP100_SEND_DATA; - if(data->hyp.exp100_waker) { - hyper_waker_wake(data->hyp.exp100_waker); - data->hyp.exp100_waker = NULL; + Curl_http_exp100_got100(data); + if(data->hyp.send_body_waker) { + hyper_waker_wake(data->hyp.send_body_waker); + data->hyp.send_body_waker = NULL; } } else { /* >= 4xx */ - k->exp100 = EXP100_FAILED; + Curl_req_abort_sending(data); } } if(data->state.hconnect && (data->req.httpcode/100 != 2) && data->state.authproxy.done) { - done = TRUE; + data->req.done = TRUE; result = CURLE_OK; } else - result = Curl_http_firstwrite(data, data->conn, &done); - if(result || done) { + result = Curl_http_firstwrite(data); + if(result || data->req.done) { infof(data, "Return early from hyper_body_chunk"); data->state.hresult = result; return HYPER_ITER_BREAK; @@ -273,14 +274,13 @@ /* We need to set 'httpcodeq' for functions that check the response code in a single place. */ data->req.httpcode = http_status; - + data->req.httpversion = http_version == HYPER_HTTP_VERSION_1_1? 11 : + (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); if(data->state.hconnect) /* CONNECT */ data->info.httpproxycode = http_status; else { - conn->httpversion = - http_version == HYPER_HTTP_VERSION_1_1 ? 11 : - (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); + conn->httpversion = (unsigned char)data->req.httpversion; if(http_version == HYPER_HTTP_VERSION_1_0) data->state.httpwant = CURL_HTTP_VERSION_1_0; @@ -335,7 +335,6 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, struct connectdata *conn, int *didwhat, - bool *done, int select_res) { hyper_response *resp = NULL; @@ -352,20 +351,9 @@ struct SingleRequest *k = &data->req; (void)conn; - if(k->exp100 > EXP100_SEND_DATA) { - struct curltime now = Curl_now(); - timediff_t ms = Curl_timediff(now, k->start100); - if(ms >= data->set.expect_100_timeout) { - /* we've waited long enough, continue anyway */ - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - infof(data, "Done waiting for 100-continue"); - if(data->hyp.exp100_waker) { - hyper_waker_wake(data->hyp.exp100_waker); - data->hyp.exp100_waker = NULL; - } - } + if(data->hyp.send_body_waker) { + hyper_waker_wake(data->hyp.send_body_waker); + data->hyp.send_body_waker = NULL; } if(select_res & CURL_CSELECT_IN) { @@ -379,7 +367,6 @@ h->write_waker = NULL; } - *done = FALSE; do { hyper_task_return_type t; task = hyper_executor_poll(h->exec); @@ -422,7 +409,7 @@ break; } } - *done = TRUE; + data->req.done = TRUE; hyper_error_free(hypererr); break; } @@ -431,12 +418,11 @@ hyper_task_free(task); if((userdata_t)userdata == USERDATA_RESP_BODY) { /* end of transfer */ - *done = TRUE; + data->req.done = TRUE; infof(data, "hyperstream is done"); if(!k->bodywrites) { /* hyper doesn't always call the body write callback */ - bool stilldone; - result = Curl_http_firstwrite(data, data->conn, &stilldone); + result = Curl_http_firstwrite(data); } break; } @@ -462,11 +448,11 @@ reasonp = hyper_response_reason_phrase(resp); reason_len = hyper_response_reason_phrase_len(resp); - if(http_status == 417 && data->state.expect100header) { + if(http_status == 417 && Curl_http_exp100_is_selected(data)) { infof(data, "Got 417 while waiting for a 100"); data->state.disableexpect = TRUE; data->req.newurl = strdup(data->state.url); - Curl_done_sending(data, k); + Curl_req_abort_sending(data); } result = status_line(data, conn, @@ -654,115 +640,66 @@ return result; } -static int uploadpostfields(void *userdata, hyper_context *ctx, - hyper_buf **chunk) -{ - struct Curl_easy *data = (struct Curl_easy *)userdata; - (void)ctx; - if(data->req.exp100 > EXP100_SEND_DATA) { - if(data->req.exp100 == EXP100_FAILED) - return HYPER_POLL_ERROR; - - /* still waiting confirmation */ - if(data->hyp.exp100_waker) - hyper_waker_free(data->hyp.exp100_waker); - data->hyp.exp100_waker = hyper_context_waker(ctx); - return HYPER_POLL_PENDING; - } - if(data->req.upload_done) - *chunk = NULL; /* nothing more to deliver */ - else { - /* send everything off in a single go */ - hyper_buf *copy = hyper_buf_copy(data->set.postfields, - (size_t)data->req.p.http->postsize); - if(copy) - *chunk = copy; - else { - data->state.hresult = CURLE_OUT_OF_MEMORY; - return HYPER_POLL_ERROR; - } - /* increasing the writebytecount here is a little premature but we - don't know exactly when the body is sent */ - data->req.writebytecount += (size_t)data->req.p.http->postsize; - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - data->req.upload_done = TRUE; - } - return HYPER_POLL_READY; -} - static int uploadstreamed(void *userdata, hyper_context *ctx, hyper_buf **chunk) { size_t fillcount; struct Curl_easy *data = (struct Curl_easy *)userdata; - struct connectdata *conn = (struct connectdata *)data->conn; CURLcode result; + char *xfer_ulbuf; + size_t xfer_ulblen; + bool eos; + int rc = HYPER_POLL_ERROR; (void)ctx; - if(data->req.exp100 > EXP100_SEND_DATA) { - if(data->req.exp100 == EXP100_FAILED) - return HYPER_POLL_ERROR; + result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen); + if(result) + goto out; - /* still waiting confirmation */ - if(data->hyp.exp100_waker) - hyper_waker_free(data->hyp.exp100_waker); - data->hyp.exp100_waker = hyper_context_waker(ctx); - return HYPER_POLL_PENDING; - } + result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos); + if(result) + goto out; - if(data->req.upload_chunky && conn->bits.authneg) { - fillcount = 0; - data->req.upload_chunky = FALSE; - result = CURLE_OK; - } - else { - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, - &fillcount); - } - if(result) { - data->state.hresult = result; - return HYPER_POLL_ERROR; - } - if(!fillcount) { - if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE) - /* done! */ - *chunk = NULL; - else { - /* paused, save a waker */ - if(data->hyp.send_body_waker) - hyper_waker_free(data->hyp.send_body_waker); - data->hyp.send_body_waker = hyper_context_waker(ctx); - return HYPER_POLL_PENDING; - } - } - else { - hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount); + if(fillcount) { + hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount); if(copy) *chunk = copy; else { - data->state.hresult = CURLE_OUT_OF_MEMORY; - return HYPER_POLL_ERROR; + result = CURLE_OUT_OF_MEMORY; + goto out; } /* increasing the writebytecount here is a little premature but we don't know exactly when the body is sent */ data->req.writebytecount += fillcount; Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + rc = HYPER_POLL_READY; } - return HYPER_POLL_READY; + else if(eos) { + *chunk = NULL; + rc = HYPER_POLL_READY; + } + else { + /* paused, save a waker */ + if(data->hyp.send_body_waker) + hyper_waker_free(data->hyp.send_body_waker); + data->hyp.send_body_waker = hyper_context_waker(ctx); + rc = HYPER_POLL_PENDING; + } + +out: + Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); + data->state.hresult = result; + return rc; } /* - * bodysend() sets up headers in the outgoing request for an HTTP transfer that - * sends a body + * finalize_request() sets up last headers and optional body settings */ - -static CURLcode bodysend(struct Curl_easy *data, - struct connectdata *conn, - hyper_headers *headers, - hyper_request *hyperreq, - Curl_HttpReq httpreq) +static CURLcode finalize_request(struct Curl_easy *data, + hyper_headers *headers, + hyper_request *hyperreq, + Curl_HttpReq httpreq) { - struct HTTP *http = data->req.p.http; CURLcode result = CURLE_OK; struct dynbuf req; if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) @@ -770,34 +707,31 @@ else { hyper_body *body; Curl_dyn_init(&req, DYN_HTTP_REQUEST); - result = Curl_http_bodysend(data, conn, &req, httpreq); + result = Curl_http_req_complete(data, &req, httpreq); + if(result) + return result; - if(!result) + /* if the "complete" above did produce more than the closing line, + parse the added headers */ + if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) { result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); + if(result) + return result; + } Curl_dyn_free(&req); body = hyper_body_new(); hyper_body_set_userdata(body, data); - if(data->set.postfields) - hyper_body_set_data_func(body, uploadpostfields); - else { - result = Curl_get_upload_buffer(data); - if(result) { - hyper_body_free(body); - return result; - } - /* init the "upload from here" pointer */ - data->req.upload_fromhere = data->state.ulbuf; - hyper_body_set_data_func(body, uploadstreamed); - } + hyper_body_set_data_func(body, uploadstreamed); + if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) { /* fail */ result = CURLE_OUT_OF_MEMORY; } } - http->sending = HTTPSEND_BODY; - return result; + + return cr_hyper_add(data); } static CURLcode cookies(struct Curl_easy *data, @@ -885,7 +819,16 @@ may be parts of the request that is not yet sent, since we can deal with the rest of the request in the PERFORM phase. */ *done = TRUE; - Curl_client_cleanup(data); + result = Curl_client_start(data); + if(result) + return result; + + /* Add collecting of headers written to client. For a new connection, + * we might have done that already, but reuse + * or multiplex needs it here as well. */ + result = Curl_headers_init(data); + if(result) + return result; infof(data, "Time for the Hyper dance"); memset(h, 0, sizeof(struct hyptransfer)); @@ -913,9 +856,9 @@ return result; } - result = Curl_http_resume(data, conn, httpreq); + result = Curl_http_req_set_reader(data, httpreq, &te); if(result) - return result; + goto error; result = Curl_http_range(data, httpreq); if(result) @@ -932,7 +875,9 @@ goto error; } /* tell Hyper how to read/write network data */ - hyper_io_set_userdata(io, data); + h->io_ctx.data = data; + h->io_ctx.sockindex = FIRSTSOCKET; + hyper_io_set_userdata(io, &h->io_ctx); hyper_io_set_read(io, Curl_hyper_recv); hyper_io_set_write(io, Curl_hyper_send); @@ -1005,11 +950,6 @@ goto error; } } - else { - if(!data->state.disableexpect) { - data->state.expect100header = TRUE; - } - } if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) { failf(data, "error setting method"); @@ -1034,21 +974,19 @@ goto error; } - result = Curl_http_body(data, conn, httpreq, &te); - if(result) - goto error; - if(data->state.aptr.host) { result = Curl_hyper_header(data, headers, data->state.aptr.host); if(result) goto error; } +#ifndef CURL_DISABLE_PROXY if(data->state.aptr.proxyuserpwd) { result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd); if(result) goto error; } +#endif if(data->state.aptr.userpwd) { result = Curl_hyper_header(data, headers, data->state.aptr.userpwd); @@ -1160,13 +1098,13 @@ if(result) goto error; - result = bodysend(data, conn, headers, req, httpreq); + result = finalize_request(data, headers, req, httpreq); if(result) goto error; Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); - if(data->req.upload_chunky && conn->bits.authneg) { + if(data->req.upload_chunky && data->req.authneg) { data->req.upload_chunky = TRUE; } else { @@ -1193,18 +1131,17 @@ if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { /* HTTP GET/HEAD download */ Curl_pgrsSetUploadSize(data, 0); /* nothing */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); } + + Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); conn->datastream = Curl_hyper_stream; - if(data->state.expect100header) - /* Timeout count starts now since with Hyper we don't know exactly when - the full request has been sent. */ - data->req.start100 = Curl_now(); /* 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 return CURLE_OK; error: DEBUGASSERT(result); @@ -1241,10 +1178,51 @@ hyper_waker_free(h->write_waker); h->write_waker = NULL; } - if(h->exp100_waker) { - hyper_waker_free(h->exp100_waker); - h->exp100_waker = NULL; + if(h->send_body_waker) { + hyper_waker_free(h->send_body_waker); + h->send_body_waker = NULL; } } +static CURLcode cr_hyper_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)reader; + if(data->hyp.send_body_waker) { + hyper_waker_wake(data->hyp.send_body_waker); + data->hyp.send_body_waker = NULL; + } + return CURLE_OK; +} + +/* Hyper client reader, handling unpausing */ +static const struct Curl_crtype cr_hyper_protocol = { + "cr-hyper", + Curl_creader_def_init, + Curl_creader_def_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + Curl_creader_def_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + cr_hyper_unpause, + Curl_creader_def_done, + sizeof(struct Curl_creader) +}; + +static CURLcode cr_hyper_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_hyper_protocol, + CURL_CR_PROTOCOL); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + #endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h index 0c7de90..89dd53b 100644 --- a/Utilities/cmcurl/lib/c-hyper.h +++ b/Utilities/cmcurl/lib/c-hyper.h
@@ -29,13 +29,18 @@ #include <hyper.h> +struct hyp_io_ctx { + struct Curl_easy *data; + int sockindex; +}; + /* per-transfer data for the Hyper backend */ struct hyptransfer { hyper_waker *write_waker; hyper_waker *read_waker; const hyper_executor *exec; - hyper_waker *exp100_waker; hyper_waker *send_body_waker; + struct hyp_io_ctx io_ctx; }; size_t Curl_hyper_recv(void *userp, hyper_context *ctx, @@ -45,7 +50,6 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, struct connectdata *conn, int *didwhat, - bool *done, int select_res); CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index 167e531..093c33b 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -114,18 +114,12 @@ struct h1_tunnel_state **pts) { struct h1_tunnel_state *ts; - CURLcode result; if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); return CURLE_UNSUPPORTED_PROTOCOL; } - /* we might need the upload buffer for streaming a partial request */ - result = Curl_get_upload_buffer(data); - if(result) - return result; - ts = calloc(1, sizeof(*ts)); if(!ts) return CURLE_OUT_OF_MEMORY; @@ -201,17 +195,24 @@ static void tunnel_free(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct h1_tunnel_state *ts = cf->ctx; - if(ts) { - h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - Curl_dyn_free(&ts->rcvbuf); - Curl_dyn_free(&ts->request_data); - Curl_httpchunk_free(data, &ts->ch); - free(ts); - cf->ctx = NULL; + if(cf) { + struct h1_tunnel_state *ts = cf->ctx; + if(ts) { + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + Curl_dyn_free(&ts->rcvbuf); + Curl_dyn_free(&ts->request_data); + Curl_httpchunk_free(data, &ts->ch); + free(ts); + cf->ctx = NULL; + } } } +static bool tunnel_want_send(struct h1_tunnel_state *ts) +{ + return (ts->tunnel_state == H1_TUNNEL_CONNECT); +} + #ifndef USE_HYPER static CURLcode start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -238,6 +239,8 @@ http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); + if(!result) + result = Curl_creader_set_null(data); out: if(result) @@ -366,7 +369,6 @@ { CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); char *linep; size_t line_len; int error, writetype; @@ -386,7 +388,7 @@ /* Read one byte at a time to avoid a race condition. Wait at most one second before looping to ensure continuous pgrsUpdates. */ - result = Curl_read(data, tunnelsocket, &byte, 1, &nread); + result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread); if(result == CURLE_AGAIN) /* socket buffer drained, return */ return CURLE_OK; @@ -593,7 +595,9 @@ goto error; } /* tell Hyper how to read/write network data */ - hyper_io_set_userdata(io, data); + h->io_ctx.data = data; + h->io_ctx.sockindex = cf->sockindex; + hyper_io_set_userdata(io, &h->io_ctx); hyper_io_set_read(io, Curl_hyper_recv); hyper_io_set_write(io, Curl_hyper_send); conn->sockfd = tunnelsocket; @@ -749,6 +753,10 @@ if(result) goto error; + result = Curl_creader_set_null(data); + if(result) + goto error; + sendtask = hyper_clientconn_send(client, req); if(!sendtask) { failf(data, "hyper_clientconn_send"); @@ -832,9 +840,9 @@ int didwhat; (void)ts; - *done = FALSE; - result = Curl_hyper_stream(data, cf->conn, &didwhat, done, + result = Curl_hyper_stream(data, cf->conn, &didwhat, CURL_CSELECT_IN | CURL_CSELECT_OUT); + *done = data->req.done; if(result || !*done) return result; if(h->exec) { @@ -918,6 +926,7 @@ * If the other side indicated a connection close, or if someone * else told us to close this connection, do so now. */ + Curl_req_soft_reset(&data->req, data); if(ts->close_connection || conn->bits.close) { /* Close this filter and the sub-chain, re-connect the * sub-chain and continue. Closing this filter will @@ -1003,11 +1012,9 @@ *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); if(*done) { cf->connected = TRUE; - /* Restore `data->req` fields that may habe been touched */ - data->req.header = TRUE; /* assume header */ - data->req.bytecount = 0; - data->req.ignorebody = FALSE; - Curl_client_cleanup(data); + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); @@ -1031,7 +1038,7 @@ wait for the socket to become readable to be able to get the response headers or if we're still sending the request, wait for write. */ - if(ts->CONNECT.sending == HTTPSEND_REQUEST) + if(tunnel_want_send(ts)) Curl_pollset_set_out_only(data, ps, sock); else Curl_pollset_set_in_only(data, ps, sock); @@ -1052,18 +1059,20 @@ struct Curl_easy *data) { CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - if(cf->ctx) { - h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); + if(cf) { + cf->connected = FALSE; + if(cf->ctx) { + h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); + } + if(cf->next) + cf->next->cft->do_close(cf->next, data); } - if(cf->next) - cf->next->cft->do_close(cf->next, data); } struct Curl_cftype Curl_cft_h1_proxy = { "H1-PROXY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 0, cf_h1_proxy_destroy, cf_h1_proxy_connect,
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index f8f2f3c..0bff15f 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -38,6 +38,7 @@ #include "http2.h" #include "http_proxy.h" #include "multiif.h" +#include "sendf.h" #include "cf-h2-proxy.h" /* The last 3 #include files should be in this order */ @@ -956,6 +957,9 @@ result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); if(result) goto out; + result = Curl_creader_set_null(data); + if(result) + goto out; infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); @@ -1125,7 +1129,12 @@ out: *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED); - cf->connected = *done; + if(*done) { + cf->connected = TRUE; + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); + } CF_DATA_RESTORE(cf, save); return result; } @@ -1523,7 +1532,7 @@ struct Curl_cftype Curl_cft_h2_proxy = { "H2-PROXY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect,
diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index c062887..2abc4d7 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c
@@ -86,14 +86,14 @@ if(data->set.str[STRING_HAPROXY_CLIENT_IP]) client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; else - client_ip = data->info.conn_local_ip; + client_ip = data->info.primary.local_ip; result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", tcp_version, client_ip, - data->info.conn_primary_ip, - data->info.conn_local_port, - data->info.conn_primary_port); + data->info.primary.remote_ip, + data->info.primary.local_port, + data->info.primary.remote_port); #ifdef USE_UNIX_SOCKETS } @@ -129,12 +129,17 @@ case HAPROXY_SEND: len = Curl_dyn_len(&ctx->data_out); if(len > 0) { - ssize_t written = Curl_conn_send(data, cf->sockindex, - Curl_dyn_ptr(&ctx->data_out), - len, &result); - if(written < 0) + size_t written; + result = Curl_conn_send(data, cf->sockindex, + Curl_dyn_ptr(&ctx->data_out), + len, &written); + if(result == CURLE_AGAIN) { + result = CURLE_OK; + written = 0; + } + else if(result) goto out; - Curl_dyn_tail(&ctx->data_out, len - (size_t)written); + Curl_dyn_tail(&ctx->data_out, len - written); if(Curl_dyn_len(&ctx->data_out) > 0) { result = CURLE_OK; goto out; @@ -184,7 +189,7 @@ struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", - 0, + CF_TYPE_PROXY, 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 b23fa05..50ac8d4 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -102,8 +102,8 @@ CURLcode result; /* overall result */ struct cf_hc_baller h3_baller; struct cf_hc_baller h21_baller; - int soft_eyeballs_timeout_ms; - int hard_eyeballs_timeout_ms; + unsigned int soft_eyeballs_timeout_ms; + unsigned int hard_eyeballs_timeout_ms; }; static void cf_hc_baller_init(struct cf_hc_baller *b,
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index 742902f..3e87889 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -81,7 +81,7 @@ #include "memdebug.h" -#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) +#if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) /* It makes support for IPv4-mapped IPv6 addresses. * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; * Windows Vista and later: default is on; @@ -287,7 +287,7 @@ /* no socket, no connection */ return CURLE_COULDNT_CONNECT; -#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(data->conn->scope_id && (addr->family == AF_INET6)) { struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; sa6->sin6_scope_id = data->conn->scope_id; @@ -405,7 +405,7 @@ struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; #endif @@ -419,7 +419,7 @@ #ifdef IP_BIND_ADDRESS_NO_PORT int on = 1; #endif -#ifndef ENABLE_IPV6 +#ifndef USE_IPV6 (void)scope; #endif @@ -475,7 +475,7 @@ #endif switch(Curl_if2ip(af, -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 scope, conn->scope_id, #endif dev, myhost, sizeof(myhost))) { @@ -514,7 +514,7 @@ if(af == AF_INET) conn->ip_version = CURL_IPRESOLVE_V4; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else if(af == AF_INET6) conn->ip_version = CURL_IPRESOLVE_V6; #endif @@ -547,7 +547,7 @@ } if(done > 0) { -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 /* IPv6 address */ if(af == AF_INET6) { #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID @@ -596,7 +596,7 @@ } else { /* no device was given, prepare sa to match af's needs */ -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(af == AF_INET6) { si6->sin6_family = AF_INET6; si6->sin6_port = htons(port); @@ -616,16 +616,6 @@ for(;;) { if(bind(sockfd, sock, sizeof_sa) >= 0) { /* we succeeded to bind */ - struct Curl_sockaddr_storage add; - curl_socklen_t size = sizeof(add); - memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); - if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_INTERFACE_FAILED; - } infof(data, "Local port: %hu", port); conn->bits.bound = TRUE; return CURLE_OK; @@ -639,7 +629,7 @@ /* We reuse/clobber the port variable here below */ if(sock->sa_family == AF_INET) si4->sin_port = ntohs(port); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else si6->sin6_port = ntohs(port); #endif @@ -776,10 +766,7 @@ struct Curl_sockaddr_ex addr; /* address to connect to */ curl_socket_t sock; /* current attempt socket */ struct bufq recvbuf; /* used when `buffer_recv` is set */ - char r_ip[MAX_IPADR_LEN]; /* remote IP as string */ - int r_port; /* remote port number */ - char l_ip[MAX_IPADR_LEN]; /* local IP as string */ - int l_port; /* local port number */ + struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ struct curltime started_at; /* when socket was created */ struct curltime connected_at; /* when socket connected/got first byte */ struct curltime first_byte_at; /* when first byte was recvd */ @@ -880,8 +867,9 @@ nread = -1; } } - CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", - len, (int)nread, *err); + CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%" + CURL_FORMAT_SOCKET_T ") -> %d, err=%d", + len, ctx->sock, (int)nread, *err); return nread; } @@ -925,7 +913,8 @@ struct cf_socket_ctx *ctx = cf->ctx; #ifdef HAVE_GETSOCKNAME - if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) { + if((ctx->sock != CURL_SOCKET_BAD) && + !(data->conn->handler->protocol & CURLPROTO_TFTP)) { /* TFTP does not connect, so it cannot get the IP like this */ char buffer[STRERROR_LEN]; @@ -940,7 +929,7 @@ return CURLE_FAILED_INIT; } if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, - ctx->l_ip, &ctx->l_port)) { + ctx->ip.local_ip, &ctx->ip.local_port)) { failf(data, "ssloc inet_ntop() failed with errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); return CURLE_FAILED_INIT; @@ -948,8 +937,8 @@ } #else (void)data; - ctx->l_ip[0] = 0; - ctx->l_port = -1; + ctx->ip.local_ip[0] = 0; + ctx->ip.local_port = -1; #endif return CURLE_OK; } @@ -961,7 +950,7 @@ /* store remote address and port used in this connection attempt */ if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen, - ctx->r_ip, &ctx->r_port)) { + ctx->ip.remote_ip, &ctx->ip.remote_port)) { char buffer[STRERROR_LEN]; ctx->error = errno; @@ -993,16 +982,16 @@ if(result) goto out; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(ctx->addr.family == AF_INET6) { set_ipv6_v6only(ctx->sock, 0); - infof(data, " Trying [%s]:%d...", ctx->r_ip, ctx->r_port); + infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); } else #endif - infof(data, " Trying %s:%d...", ctx->r_ip, ctx->r_port); + infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 is_tcp = (ctx->addr.family == AF_INET || ctx->addr.family == AF_INET6) && ctx->addr.socktype == SOCK_STREAM; @@ -1039,7 +1028,7 @@ #ifndef CURL_DISABLE_BINDLOCAL /* possibly bind the local end to an IP, interface or port */ if(ctx->addr.family == AF_INET -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 || ctx->addr.family == AF_INET6 #endif ) { @@ -1166,9 +1155,9 @@ error = SOCKERRNO; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "local address %s port %d...", - ctx->l_ip, ctx->l_port); + ctx->ip.local_ip, ctx->ip.local_port); if(-1 == rc) { - result = socket_connect_result(data, ctx->r_ip, error); + result = socket_connect_result(data, ctx->ip.remote_ip, error); goto out; } } @@ -1213,7 +1202,8 @@ { char buffer[STRERROR_LEN]; infof(data, "connect to %s port %u from %s port %d failed: %s", - ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port, + ctx->ip.remote_ip, ctx->ip.remote_port, + ctx->ip.local_ip, ctx->ip.local_port, Curl_strerror(ctx->error, buffer, sizeof(buffer))); } #endif @@ -1233,10 +1223,11 @@ const char **pdisplay_host, int *pport) { + struct cf_socket_ctx *ctx = cf->ctx; (void)data; *phost = cf->conn->host.name; *pdisplay_host = cf->conn->host.dispname; - *pport = cf->conn->port; + *pport = ctx->ip.remote_port; } static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, @@ -1248,11 +1239,13 @@ if(ctx->sock != CURL_SOCKET_BAD) { if(!cf->connected) { Curl_pollset_set_out_only(data, ps, ctx->sock); - CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num); + CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%" + CURL_FORMAT_SOCKET_T, ctx->sock); } else if(!ctx->active) { Curl_pollset_add_in(data, ps, ctx->sock); - CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num); + CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%" + CURL_FORMAT_SOCKET_T, ctx->sock); } } } @@ -1286,7 +1279,7 @@ #ifdef DEBUGBUILD /* simulate network blocking/partial writes */ if(ctx->wblock_percent > 0) { - unsigned char c; + unsigned char c = 0; Curl_rand(data, &c, 1); if(c >= ((100-ctx->wblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); @@ -1364,7 +1357,7 @@ #ifdef DEBUGBUILD /* simulate network blocking/partial reads */ if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) { - unsigned char c; + unsigned char c = 0; Curl_rand(data, &c, 1); if(c >= ((100-ctx->rblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len); @@ -1433,31 +1426,24 @@ return nread; } -static void conn_set_primary_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - - (void)data; - DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip)); - memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip)); -} - static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; /* use this socket from now on */ cf->conn->sock[cf->sockindex] = ctx->sock; - /* the first socket info gets set at conn and data */ + set_local_ip(cf, data); + if(cf->sockindex == SECONDARYSOCKET) + cf->conn->secondary = ctx->ip; + else + cf->conn->primary = ctx->ip; + /* the first socket info gets some specials */ if(cf->sockindex == FIRSTSOCKET) { cf->conn->remote_addr = &ctx->addr; - #ifdef ENABLE_IPV6 + #ifdef USE_IPV6 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; #endif - conn_set_primary_ip(cf, data); - set_local_ip(cf, data); - Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); + Curl_persistconninfo(data, cf->conn, &ctx->ip); /* buffering is currently disabled by default because we have stalls * in parallel transfers where not all buffered data is consumed and no * socket events happen. @@ -1480,7 +1466,7 @@ cf_socket_active(cf, data); break; case CF_CTRL_DATA_SETUP: - Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); + Curl_persistconninfo(data, cf->conn, &ctx->ip); break; case CF_CTRL_FORGET_SOCKET: ctx->sock = CURL_SOCKET_BAD; @@ -1637,7 +1623,7 @@ #else rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); if(-1 == rc) { - return socket_connect_result(data, ctx->r_ip, SOCKERRNO); + return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); } ctx->sock_connected = TRUE; #endif @@ -1645,7 +1631,8 @@ CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T " connected: [%s:%d] -> [%s:%d]", (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", - ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port); + ctx->sock, ctx->ip.local_ip, ctx->ip.local_port, + ctx->ip.remote_ip, ctx->ip.remote_port); (void)curlx_nonblock(ctx->sock, TRUE); switch(ctx->addr.family) { @@ -1695,7 +1682,7 @@ goto out; CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" CURL_FORMAT_SOCKET_T " (%s:%d)", - ctx->sock, ctx->l_ip, ctx->l_port); + ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); } else { CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" @@ -1891,8 +1878,8 @@ struct Curl_sockaddr_storage ssrem; curl_socklen_t plen; - ctx->r_ip[0] = 0; - ctx->r_port = 0; + ctx->ip.remote_ip[0] = 0; + ctx->ip.remote_port = 0; plen = sizeof(ssrem); memset(&ssrem, 0, plen); if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { @@ -1902,14 +1889,14 @@ return; } if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - ctx->r_ip, &ctx->r_port)) { + ctx->ip.remote_ip, &ctx->ip.remote_port)) { failf(data, "ssrem inet_ntop() failed with errno %d: %s", errno, Curl_strerror(errno, buffer, sizeof(buffer))); return; } #else - ctx->r_ip[0] = 0; - ctx->r_port = 0; + ctx->ip.remote_ip[0] = 0; + ctx->ip.remote_port = 0; (void)data; #endif } @@ -1938,7 +1925,7 @@ cf->connected = TRUE; CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T ", remote=%s port=%d)", - ctx->sock, ctx->r_ip, ctx->r_port); + ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); return CURLE_OK; } @@ -1958,9 +1945,9 @@ struct Curl_easy *data, curl_socket_t *psock, const struct Curl_sockaddr_ex **paddr, - const char **pr_ip_str, int *pr_port, - const char **pl_ip_str, int *pl_port) + struct ip_quadruple *pip) { + (void)data; if(cf_is_socket(cf) && cf->ctx) { struct cf_socket_ctx *ctx = cf->ctx; @@ -1968,17 +1955,8 @@ *psock = ctx->sock; if(paddr) *paddr = &ctx->addr; - if(pr_ip_str) - *pr_ip_str = ctx->r_ip; - if(pr_port) - *pr_port = ctx->r_port; - if(pl_port ||pl_ip_str) { - set_local_ip(cf, data); - if(pl_ip_str) - *pl_ip_str = ctx->l_ip; - if(pl_port) - *pl_port = ctx->l_port; - } + if(pip) + *pip = ctx->ip; return CURLE_OK; } return CURLE_FAILED_INIT;
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h index 87e0f30..058af50 100644 --- a/Utilities/cmcurl/lib/cf-socket.h +++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -33,6 +33,7 @@ struct Curl_easy; struct connectdata; struct Curl_sockaddr_ex; +struct ip_quadruple; /* * The Curl_sockaddr_ex structure is basically libcurl's external API @@ -153,18 +154,14 @@ * The filter owns all returned values. * @param psock pointer to hold socket descriptor or NULL * @param paddr pointer to hold addr reference or NULL - * @param pr_ip_str pointer to hold remote addr as string or NULL - * @param pr_port pointer to hold remote port number or NULL - * @param pl_ip_str pointer to hold local addr as string or NULL - * @param pl_port pointer to hold local port number or NULL + * @param pip pointer to get IP quadruple or NULL * Returns error if the filter is of invalid type. */ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *psock, const struct Curl_sockaddr_ex **paddr, - const char **pr_ip_str, int *pr_port, - const char **pl_ip_str, int *pl_port); + struct ip_quadruple *pip); extern struct Curl_cftype Curl_cft_tcp; extern struct Curl_cftype Curl_cft_udp;
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index 823e90c..a327fa1 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c
@@ -67,7 +67,7 @@ else { *phost = cf->conn->host.name; *pdisplay_host = cf->conn->host.dispname; - *pport = cf->conn->port; + *pport = cf->conn->primary.remote_port; } } @@ -168,38 +168,46 @@ } } -ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code) +ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf, + size_t len, CURLcode *code) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); + *code = CURLE_OK; cf = data->conn->cfilter[num]; while(cf && !cf->connected) { cf = cf->next; } if(cf) { - return cf->cft->do_recv(cf, data, buf, len, code); + ssize_t nread = cf->cft->do_recv(cf, data, buf, len, code); + DEBUGASSERT(nread >= 0 || *code); + DEBUGASSERT(nread < 0 || !*code); + return nread; } failf(data, "recv: no filter connected"); *code = CURLE_FAILED_INIT; return -1; } -ssize_t Curl_conn_send(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code) +ssize_t Curl_cf_send(struct Curl_easy *data, int num, + const void *mem, size_t len, CURLcode *code) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); + *code = CURLE_OK; cf = data->conn->cfilter[num]; while(cf && !cf->connected) { cf = cf->next; } if(cf) { - return cf->cft->do_send(cf, data, mem, len, code); + ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, code); + DEBUGASSERT(nwritten >= 0 || *code); + DEBUGASSERT(nwritten < 0 || !*code || !len); + return nwritten; } failf(data, "send: no filter connected"); DEBUGASSERT(0); @@ -582,7 +590,7 @@ /** * Notify connection filters that the transfer represented by `data` - * is donw with sending data (e.g. has uploaded everything). + * is done with sending data (e.g. has uploaded everything). */ void Curl_conn_ev_data_done_send(struct Curl_easy *data) { @@ -662,6 +670,71 @@ return (result || n <= 0)? 1 : (size_t)n; } +int Curl_conn_get_stream_error(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + CURLcode result; + int n = 0; + + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + result = cf? cf->cft->query(cf, data, CF_QUERY_STREAM_ERROR, + &n, NULL) : CURLE_UNKNOWN_OPTION; + return (result || n < 0)? 0 : n; +} + +int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) +{ + if(data && data->conn && + sockfd != CURL_SOCKET_BAD && sockfd == data->conn->sock[SECONDARYSOCKET]) + return SECONDARYSOCKET; + return FIRSTSOCKET; +} + +CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, + char *buf, size_t blen, ssize_t *n) +{ + CURLcode result = CURLE_OK; + ssize_t nread; + + DEBUGASSERT(data->conn); + nread = data->conn->recv[sockindex](data, sockindex, buf, blen, &result); + DEBUGASSERT(nread >= 0 || result); + DEBUGASSERT(nread < 0 || !result); + *n = (nread >= 0)? (size_t)nread : 0; + return result; +} + +CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t blen, + size_t *pnwritten) +{ + ssize_t nwritten; + CURLcode result = CURLE_OK; + struct connectdata *conn; + + DEBUGASSERT(sockindex >= 0 && sockindex < 2); + DEBUGASSERT(pnwritten); + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + conn = data->conn; +#ifdef CURLDEBUG + { + /* Allow debug builds to override this logic to force short sends + */ + char *p = getenv("CURL_SMALLSENDS"); + if(p) { + size_t altsize = (size_t)strtoul(p, NULL, 10); + if(altsize) + blen = CURLMIN(blen, altsize); + } + } +#endif + nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result); + DEBUGASSERT((nwritten >= 0) || result); + *pnwritten = (nwritten < 0)? 0 : (size_t)nwritten; + return result; +} void Curl_pollset_reset(struct Curl_easy *data, struct easy_pollset *ps)
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index f838429..dcfc1b7 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h
@@ -160,6 +160,7 @@ #define CF_QUERY_SOCKET 3 /* - curl_socket_t */ #define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */ #define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */ +#define CF_QUERY_STREAM_ERROR 6 /* error code - */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -178,10 +179,12 @@ * connection, etc. * CF_TYPE_SSL: provide SSL/TLS * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles + * CF_TYPE_PROXY provides proxying */ #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) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -402,11 +405,11 @@ /** * Receive data through the filter chain at `sockindex` for connection * `data->conn`. Copy at most `len` bytes into `buf`. Return the - * actuel number of bytes copied or a negative value on error. + * actual number of bytes copied or a negative value on error. * The error code is placed into `*code`. */ -ssize_t Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *code); +ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, CURLcode *code); /** * Send `len` bytes of data from `buf` through the filter chain `sockindex` @@ -414,8 +417,8 @@ * or a negative value on error. * The error code is placed into `*code`. */ -ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *code); +ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t len, CURLcode *code); /** * The easy handle `data` is being attached to `conn`. This does @@ -449,7 +452,7 @@ /** * Notify connection filters that the transfer represented by `data` - * is donw with sending data (e.g. has uploaded everything). + * is done with sending data (e.g. has uploaded everything). */ void Curl_conn_ev_data_done_send(struct Curl_easy *data); @@ -496,6 +499,36 @@ struct connectdata *conn, int sockindex); +/** + * Get the underlying error code for a transfer stream or 0 if not known. + */ +int Curl_conn_get_stream_error(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +/** + * Get the index of the given socket in the connection's sockets. + * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the + * correct socket index. + */ +int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); + +/* + * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. + * Will return CURLE_AGAIN iff blocked on receiving. + */ +CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, + char *buf, size_t buffersize, + ssize_t *pnread); + +/* + * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. + * Will return CURLE_AGAIN iff blocked on sending. + */ +CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t blen, + size_t *pnwritten); + void Curl_pollset_reset(struct Curl_easy *data, struct easy_pollset *ps);
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index 66f18ec..2b5ee4f 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c
@@ -68,8 +68,7 @@ static void bundle_add_conn(struct connectbundle *bundle, struct connectdata *conn) { - Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn, - &conn->bundle_node); + Curl_llist_append(&bundle->conn_list, conn, &conn->bundle_node); conn->bundle = bundle; bundle->num_connections++; } @@ -101,7 +100,7 @@ bundle_destroy(b); } -int Curl_conncache_init(struct conncache *connc, int size) +int Curl_conncache_init(struct conncache *connc, size_t size) { /* allocate a new easy handle to use when closing cached connections */ connc->closure_handle = curl_easy_init(); @@ -131,7 +130,7 @@ #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { hostname = conn->http_proxy.host.name; - port = conn->port; + port = conn->primary.remote_port; } else #endif @@ -141,7 +140,7 @@ hostname = conn->host.name; /* put the numbers first so that the hostname gets cut off if too long */ -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname); #else msnprintf(buf, len, "%ld/%s", port, hostname); @@ -395,8 +394,6 @@ important that details from this (unrelated) disconnect does not taint meta-data in the data handle. */ struct conncache *connc = data->state.conn_cache; - connc->closure_handle->state.buffer = data->state.buffer; - connc->closure_handle->set.buffer_size = data->set.buffer_size; Curl_disconnect(connc->closure_handle, conn_candidate, /* dead_connection */ FALSE); } @@ -522,12 +519,9 @@ void Curl_conncache_close_all_connections(struct conncache *connc) { struct connectdata *conn; - char buffer[READBUFFER_MIN + 1]; SIGPIPE_VARIABLE(pipe_st); if(!connc->closure_handle) return; - connc->closure_handle->state.buffer = buffer; - connc->closure_handle->set.buffer_size = READBUFFER_MIN; conn = conncache_find_first_connection(connc); while(conn) { @@ -541,7 +535,6 @@ conn = conncache_find_first_connection(connc); } - connc->closure_handle->state.buffer = NULL; sigpipe_ignore(connc->closure_handle, &pipe_st); Curl_hostcache_clean(connc->closure_handle,
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h index c60f844..e685123 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h
@@ -85,7 +85,7 @@ }; /* returns 1 on error, 0 is fine */ -int Curl_conncache_init(struct conncache *, int size); +int Curl_conncache_init(struct conncache *, size_t size); void Curl_conncache_destroy(struct conncache *connc); /* return the correct bundle, to a host or a proxy */
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index 45743e9..bf85e64 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c
@@ -94,7 +94,7 @@ * infinite time left). If the value is negative, the timeout time has already * elapsed. * @param data the transfer to check on - * @param nowp timestamp to use for calculdation, NULL to use Curl_now() + * @param nowp timestamp to use for calculation, NULL to use Curl_now() * @param duringconnect TRUE iff connect timeout is also taken into account. * @unittest: 1303 */ @@ -145,19 +145,26 @@ /* Copies connection info into the transfer handle to make it available when the transfer handle is no longer associated with the connection. */ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, - char *local_ip, int local_port) + struct ip_quadruple *ip) { - memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN); - if(local_ip && local_ip[0]) - memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN); - else - data->info.conn_local_ip[0] = 0; + if(ip) + data->info.primary = *ip; + else { + memset(&data->info.primary, 0, sizeof(data->info.primary)); + data->info.primary.remote_port = -1; + data->info.primary.local_port = -1; + } data->info.conn_scheme = conn->handler->scheme; /* conn_protocol can only provide "old" protocols */ data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; - data->info.conn_primary_port = conn->port; data->info.conn_remote_port = conn->remote_port; - data->info.conn_local_port = local_port; + data->info.used_proxy = +#ifdef CURL_DISABLE_PROXY + 0 +#else + conn->bits.proxy +#endif + ; } static const struct Curl_addrinfo * @@ -188,7 +195,7 @@ char *addr, int *port) { struct sockaddr_in *si = NULL; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 *si6 = NULL; #endif #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) @@ -207,7 +214,7 @@ return TRUE; } break; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: si6 = (struct sockaddr_in6 *)(void *) sa; if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, @@ -394,7 +401,7 @@ return CURLE_OUT_OF_MEMORY; baller->name = ((ai_family == AF_INET)? "ipv4" : ( -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 (ai_family == AF_INET6)? "ipv6" : #endif "ip")); @@ -721,7 +728,7 @@ failf(data, "Failed to connect to %s port %u after " "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", - hostname, conn->port, + hostname, conn->primary.remote_port, Curl_timediff(now, data->progress.t_startsingle), curl_easy_strerror(result)); @@ -772,7 +779,7 @@ /* any IP version is allowed */ ai_family0 = remotehost->addr? remotehost->addr->ai_family : 0; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 ai_family1 = ai_family0 == AF_INET6 ? AF_INET : AF_INET6; #else @@ -783,7 +790,7 @@ /* only one IP version is allowed */ ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ? AF_INET : -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 AF_INET6; #else AF_UNSPEC; @@ -911,7 +918,7 @@ if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ - Curl_verboseconnect(data, cf->conn); + Curl_verboseconnect(data, cf->conn, cf->sockindex); data->info.numconnects++; /* to track the # of connections made */ } break; @@ -1110,7 +1117,7 @@ #endif struct transport_provider transport_providers[] = { { TRNSPRT_TCP, Curl_cf_tcp_create }, -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 { TRNSPRT_QUIC, Curl_cf_quic_create }, #endif #ifndef CURL_DISABLE_TFTP
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 58264bd..00efe6f 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h
@@ -30,6 +30,7 @@ #include "timeval.h" struct Curl_dns_entry; +struct ip_quadruple; /* generic function that returns how much time there's left to run, according to the timeouts set */ @@ -52,7 +53,7 @@ char *addr, int *port); void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, - char *local_ip, int local_port); + struct ip_quadruple *ip); /* * Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index 06c161d..50338c7 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -300,7 +300,7 @@ struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ - if(!(type & CLIENTWRITE_BODY)) + if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); /* Set the compressed input when this function is called */ @@ -457,7 +457,7 @@ struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ - if(!(type & CLIENTWRITE_BODY)) + if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); if(zp->zlib_init == ZLIB_INIT_GZIP) { @@ -669,7 +669,7 @@ CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; - if(!(type & CLIENTWRITE_BODY)) + if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); if(!bp->br) @@ -762,7 +762,7 @@ ZSTD_outBuffer out; size_t errorCode; - if(!(type & CLIENTWRITE_BODY)) + if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); if(!zp->decomp) { @@ -916,7 +916,7 @@ (void) buf; (void) nbytes; - if(!(type & CLIENTWRITE_BODY)) + if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); failf(data, "Unrecognized content encoding type. " @@ -978,6 +978,7 @@ do { const char *name; size_t namelen; + bool is_chunked = FALSE; /* Parse a single encoding name. */ while(ISBLANK(*enclist) || *enclist == ',') @@ -993,10 +994,11 @@ const struct Curl_cwtype *cwt; struct Curl_cwriter *writer; + is_chunked = (is_transfer && (namelen == 7) && + strncasecompare(name, "chunked", 7)); /* if we skip the decoding in this phase, do not look further. * Exception is "chunked" transfer-encoding which always must happen */ - if((is_transfer && !data->set.http_transfer_encoding && - (namelen != 7 || !strncasecompare(name, "chunked", 7))) || + if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) || (!is_transfer && data->set.http_ce_skip)) { /* not requested, ignore */ return CURLE_OK; @@ -1009,6 +1011,31 @@ } cwt = find_unencode_writer(name, namelen, phase); + if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { + /* A 'chunked' transfer encoding has already been added. + * Ignore duplicates. See #13451. + * Also RFC 9112, ch. 6.1: + * "A sender MUST NOT apply the chunked transfer coding more than + * once to a message body." + */ + return CURLE_OK; + } + + if(is_transfer && !is_chunked && + Curl_cwriter_get_by_name(data, "chunked")) { + /* RFC 9112, ch. 6.1: + * "If any transfer coding other than chunked is applied to a + * response's content, the sender MUST either apply chunked as the + * final transfer coding or terminate the message by closing the + * connection." + * "chunked" must be the last added to be the first in its phase, + * reject this. + */ + failf(data, "Reject response due to 'chunked' not being the last " + "Transfer-Encoding"); + return CURLE_BAD_CONTENT_ENCODING; + } + if(!cwt) cwt = &error_writer; /* Defer error at use. */
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index dc319b6..837caaa 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c
@@ -426,6 +426,7 @@ } } +#ifndef USE_LIBPSL /* Make sure domain contains a dot or is localhost. */ static bool bad_domain(const char *domain, size_t len) { @@ -443,6 +444,7 @@ } return TRUE; } +#endif /* RFC 6265 section 4.1.1 says a server should accept this range: @@ -884,7 +886,8 @@ * Now loop through the fields and init the struct we already have * allocated */ - for(ptr = firstptr, fields = 0; ptr && !badcookie; + fields = 0; + for(ptr = firstptr; ptr && !badcookie; ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { switch(fields) { case 0: @@ -1040,7 +1043,7 @@ Curl_psl_release(data); } else - acceptable = !bad_domain(domain, strlen(domain)); + infof(data, "libpsl problem, rejecting cookie for satety"); } if(!acceptable) { @@ -1205,7 +1208,6 @@ bool newsession) { struct CookieInfo *c; - char *line = NULL; FILE *handle = NULL; if(!inc) { @@ -1241,16 +1243,14 @@ c->running = FALSE; /* this is not running, this is init */ if(fp) { - - line = malloc(MAX_COOKIE_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { - char *lineptr = line; + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_COOKIE_LINE); + while(Curl_get_line(&buf, fp)) { + char *lineptr = Curl_dyn_ptr(&buf); bool headerline = FALSE; - if(checkprefix("Set-Cookie:", line)) { + if(checkprefix("Set-Cookie:", lineptr)) { /* This is a cookie line, get it! */ - lineptr = &line[11]; + lineptr += 11; headerline = TRUE; while(*lineptr && ISBLANK(*lineptr)) lineptr++; @@ -1258,7 +1258,7 @@ Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); } - free(line); /* free the line buffer */ + Curl_dyn_free(&buf); /* free the line buffer */ /* * Remove expired cookies from the hash. We must make sure to run this @@ -1274,18 +1274,6 @@ c->running = TRUE; /* now, we're running */ return c; - -fail: - free(line); - /* - * Only clean up if we allocated it here, as the original could still be in - * use by a share handle. - */ - if(!inc) - Curl_cookie_cleanup(c); - if(handle) - fclose(handle); - return NULL; /* out of memory */ } /*
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index f9211d3..c32f24d 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -130,7 +130,7 @@ /* settle family-specific sockaddr structure size. */ if(ai->ai_family == AF_INET) ss_size = sizeof(struct sockaddr_in); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else if(ai->ai_family == AF_INET6) ss_size = sizeof(struct sockaddr_in6); #endif @@ -259,7 +259,7 @@ struct Curl_addrinfo *prevai = NULL; struct Curl_addrinfo *firstai = NULL; struct sockaddr_in *addr; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif CURLcode result = CURLE_OK; @@ -275,7 +275,7 @@ for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { size_t ss_size; size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */ -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(he->h_addrtype == AF_INET6) ss_size = sizeof(struct sockaddr_in6); else @@ -321,7 +321,7 @@ addr->sin_port = htons((unsigned short)port); break; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: addr6 = (void *)ai->ai_addr; /* storage area for this info */ @@ -348,7 +348,7 @@ struct hostent hostentry; union { struct in_addr ina4; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct in6_addr ina6; #endif } addrentry; @@ -401,7 +401,7 @@ addrentry = (void *)&buf->addrentry.ina4; memcpy(addrentry, inaddr, sizeof(struct in_addr)); break; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: addrsize = sizeof(struct in6_addr); addrentry = (void *)&buf->addrentry.ina6; @@ -447,7 +447,7 @@ if(Curl_inet_pton(AF_INET, address, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ return Curl_ip2addr(AF_INET, &in, address, port); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 { struct in6_addr in6; if(Curl_inet_pton(AF_INET6, address, &in6) > 0) @@ -570,7 +570,7 @@ { struct Curl_addrinfo *ca; struct sockaddr_in *addr; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif for(ca = addrinfo; ca != NULL; ca = ca->ai_next) { @@ -580,7 +580,7 @@ addr->sin_port = htons((unsigned short)port); break; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: addr6 = (void *)ca->ai_addr; /* storage area for this info */ addr6->sin6_port = htons((unsigned short)port);
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index 98b787f..8d48837 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -156,7 +156,7 @@ #cmakedefine USE_WIN32_LDAP 1 /* Define if you want to enable IPv6 support */ -#cmakedefine ENABLE_IPV6 1 +#cmakedefine USE_IPV6 1 /* Define to 1 if you have the alarm function. */ #cmakedefine HAVE_ALARM 1 @@ -192,6 +192,12 @@ /* Define to 1 if you have the `closesocket' function. */ #cmakedefine HAVE_CLOSESOCKET 1 +/* Define to 1 if you have the <dirent.h> header file. */ +#cmakedefine HAVE_DIRENT_H 1 + +/* Define to 1 if you have the `opendir' function. */ +#cmakedefine HAVE_OPENDIR 1 + /* Define to 1 if you have the fcntl function. */ #cmakedefine HAVE_FCNTL 1 @@ -713,6 +719,9 @@ /* if OpenSSL is in use */ #cmakedefine USE_OPENSSL 1 +/* if librtmp/rtmpdump is in use */ +#cmakedefine USE_LIBRTMP 1 + /* Define to 1 if you don't want the OpenSSL configuration to be loaded automatically */ #cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1 @@ -729,6 +738,9 @@ /* to enable quiche */ #cmakedefine USE_QUICHE 1 +/* to enable openssl + nghttp3 */ +#cmakedefine USE_OPENSSL_QUIC 1 + /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 @@ -770,12 +782,6 @@ /* Type to use in place of in_addr_t when system does not provide it. */ #cmakedefine in_addr_t ${in_addr_t} -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - /* Define to `unsigned int' if <sys/types.h> does not define. */ #cmakedefine size_t ${size_t} @@ -798,6 +804,9 @@ /* to enable Windows IDN */ #cmakedefine USE_WIN32_IDN 1 +/* to enable Apple IDN */ +#cmakedefine USE_APPLE_IDN 1 + /* Define to 1 to enable websocket support. */ #cmakedefine USE_WEBSOCKETS 1 @@ -809,3 +818,9 @@ /* Define to 1 to enable TLS-SRP support. */ #cmakedefine USE_TLS_SRP 1 + +/* Define to 1 to query for HTTPSRR when using DoH */ +#cmakedefine USE_HTTPSRR 1 + +/* if ECH support is available */ +#cmakedefine USE_ECH 1
diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c index b77763f..f8d2b2c 100644 --- a/Utilities/cmcurl/lib/curl_des.c +++ b/Utilities/cmcurl/lib/curl_des.c
@@ -36,7 +36,7 @@ * Curl_des_set_odd_parity() * * This is used to apply odd parity to the given byte array. It is typically - * used by when a cryptography engines doesn't have it's own version. + * used by when a cryptography engine doesn't have its own version. * * The function is a port of the Java based oddParity() function over at: *
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c index 686abe7..1002073 100644 --- a/Utilities/cmcurl/lib/curl_get_line.c +++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -33,14 +33,16 @@ #include "memdebug.h" /* - * Curl_get_line() makes sure to only return complete whole lines that fit in - * 'len' bytes and end with a newline. + * Curl_get_line() makes sure to only return complete whole lines that end + * newlines. */ -char *Curl_get_line(char *buf, int len, FILE *input) +int Curl_get_line(struct dynbuf *buf, FILE *input) { - bool partial = FALSE; + CURLcode result; + char buffer[128]; + Curl_dyn_reset(buf); while(1) { - char *b = fgets(buf, len, input); + char *b = fgets(buffer, sizeof(buffer), input); if(b) { size_t rlen = strlen(b); @@ -48,39 +50,28 @@ if(!rlen) break; - if(b[rlen-1] == '\n') { - /* b is \n terminated */ - if(partial) { - partial = FALSE; - continue; - } - return b; - } - else if(feof(input)) { - if(partial) - /* Line is already too large to return, ignore rest */ - break; + result = Curl_dyn_addn(buf, b, rlen); + if(result) + /* too long line or out of memory */ + return 0; /* error */ - if(rlen + 1 < (size_t) len) { - /* b is EOF terminated, insert missing \n */ - b[rlen] = '\n'; - b[rlen + 1] = '\0'; - return b; - } - else - /* Maximum buffersize reached + EOF - * This line is impossible to add a \n to so we'll ignore it - */ - break; + else if(b[rlen-1] == '\n') + /* end of the line */ + return 1; /* all good */ + + else if(feof(input)) { + /* append a newline */ + result = Curl_dyn_addn(buf, "\n", 1); + if(result) + /* too long line or out of memory */ + return 0; /* error */ + return 1; /* all good */ } - else - /* Maximum buffersize reached */ - partial = TRUE; } else break; } - return NULL; + return 0; } #endif /* if not disabled */
diff --git a/Utilities/cmcurl/lib/curl_get_line.h b/Utilities/cmcurl/lib/curl_get_line.h index 0ff32c5..7907cde 100644 --- a/Utilities/cmcurl/lib/curl_get_line.h +++ b/Utilities/cmcurl/lib/curl_get_line.h
@@ -24,8 +24,9 @@ * ***************************************************************************/ -/* get_line() makes sure to only return complete whole lines that fit in 'len' - * bytes and end with a newline. */ -char *Curl_get_line(char *buf, int len, FILE *input); +#include "dynbuf.h" + +/* Curl_get_line() returns complete lines that end with a newline. */ +int Curl_get_line(struct dynbuf *buf, FILE *input); #endif /* HEADER_CURL_GET_LINE_H */
diff --git a/Utilities/cmcurl/lib/curl_gethostname.c b/Utilities/cmcurl/lib/curl_gethostname.c index 706b2e6..bd9b220 100644 --- a/Utilities/cmcurl/lib/curl_gethostname.c +++ b/Utilities/cmcurl/lib/curl_gethostname.c
@@ -68,7 +68,7 @@ /* Override host name when environment variable CURL_GETHOSTNAME is set */ const char *force_hostname = getenv("CURL_GETHOSTNAME"); if(force_hostname) { - strncpy(name, force_hostname, namelen); + strncpy(name, force_hostname, namelen - 1); err = 0; } else {
diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c index ff21098..86ac74f 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.c +++ b/Utilities/cmcurl/lib/curl_multibyte.c
@@ -159,21 +159,4 @@ #endif } -int curlx_win32_access(const char *path, int mode) -{ -#if defined(_UNICODE) - int result = -1; - wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); - if(path_w) { - result = _waccess(path_w, mode); - curlx_unicodefree(path_w); - } - else - errno = EINVAL; - return result; -#else - return _access(path, mode); -#endif -} - #endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c deleted file mode 100644 index 0c7892a..0000000 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.c +++ /dev/null
@@ -1,500 +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_NTLM) && \ - defined(NTLM_WB_ENABLED) - -/* - * NTLM details: - * - * https://davenport.sourceforge.net/ntlm.html - * https://www.innovation.ch/java/ntlm.html - */ - -#define DEBUG_ME 0 - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif -#include <signal.h> -#ifdef HAVE_PWD_H -#include <pwd.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "select.h" -#include "vauth/ntlm.h" -#include "curl_ntlm_core.h" -#include "curl_ntlm_wb.h" -#include "url.h" -#include "strerror.h" -#include "strdup.h" -#include "strcase.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if DEBUG_ME -# define DEBUG_OUT(x) x -#else -# define DEBUG_OUT(x) Curl_nop_stmt -#endif - -/* Portable 'sclose_nolog' used only in child process instead of 'sclose' - to avoid fooling the socket leak detector */ -#ifdef HAVE_PIPE -# define sclose_nolog(x) close((x)) -#elif defined(HAVE_CLOSESOCKET) -# define sclose_nolog(x) closesocket((x)) -#elif defined(HAVE_CLOSESOCKET_CAMEL) -# define sclose_nolog(x) CloseSocket((x)) -#else -# define sclose_nolog(x) close((x)) -#endif - -static void ntlm_wb_cleanup(struct ntlmdata *ntlm) -{ - if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) { - sclose(ntlm->ntlm_auth_hlpr_socket); - ntlm->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; - } - - if(ntlm->ntlm_auth_hlpr_pid) { - int i; - for(i = 0; i < 4; i++) { - pid_t ret = waitpid(ntlm->ntlm_auth_hlpr_pid, NULL, WNOHANG); - if(ret == ntlm->ntlm_auth_hlpr_pid || errno == ECHILD) - break; - switch(i) { - case 0: - kill(ntlm->ntlm_auth_hlpr_pid, SIGTERM); - break; - case 1: - /* Give the process another moment to shut down cleanly before - bringing down the axe */ - Curl_wait_ms(1); - break; - case 2: - kill(ntlm->ntlm_auth_hlpr_pid, SIGKILL); - break; - case 3: - break; - } - } - ntlm->ntlm_auth_hlpr_pid = 0; - } - - Curl_safefree(ntlm->challenge); - Curl_safefree(ntlm->response); -} - -static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, - const char *userp) -{ - curl_socket_t sockfds[2]; - pid_t child_pid; - const char *username; - char *slash, *domain = NULL; - const char *ntlm_auth = NULL; - char *ntlm_auth_alloc = NULL; -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - struct passwd pw, *pw_res; - char pwbuf[1024]; -#endif - char buffer[STRERROR_LEN]; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - /* Return if communication with ntlm_auth already set up */ - if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD || - ntlm->ntlm_auth_hlpr_pid) - return CURLE_OK; - - username = userp; - /* The real ntlm_auth really doesn't like being invoked with an - empty username. It won't make inferences for itself, and expects - the client to do so (mostly because it's really designed for - servers like squid to use for auth, and client support is an - afterthought for it). So try hard to provide a suitable username - if we don't already have one. But if we can't, provide the - empty one anyway. Perhaps they have an implementation of the - ntlm_auth helper which *doesn't* need it so we might as well try */ - if(!username || !username[0]) { - username = getenv("NTLMUSER"); - if(!username || !username[0]) - username = getenv("LOGNAME"); - if(!username || !username[0]) - username = getenv("USER"); -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - if((!username || !username[0]) && - !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) && - pw_res) { - username = pw.pw_name; - } -#endif - if(!username || !username[0]) - username = userp; - } - slash = strpbrk(username, "\\/"); - if(slash) { - domain = strdup(username); - if(!domain) - return CURLE_OUT_OF_MEMORY; - slash = domain + (slash - username); - *slash = '\0'; - username = username + (slash - domain) + 1; - } - - /* For testing purposes, when DEBUGBUILD is defined and environment - variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform - NTLM challenge/response which only accepts commands and output - strings pre-written in test case definitions */ -#ifdef DEBUGBUILD - ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE"); - if(ntlm_auth_alloc) - ntlm_auth = ntlm_auth_alloc; - else -#endif - ntlm_auth = NTLM_WB_FILE; - - if(access(ntlm_auth, X_OK) != 0) { - failf(data, "Could not access ntlm_auth: %s errno %d: %s", - ntlm_auth, errno, Curl_strerror(errno, buffer, sizeof(buffer))); - goto done; - } - - if(wakeup_create(sockfds)) { - failf(data, "Could not open socket pair. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - goto done; - } - - child_pid = fork(); - if(child_pid == -1) { - wakeup_close(sockfds[0]); - wakeup_close(sockfds[1]); - failf(data, "Could not fork. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - goto done; - } - else if(!child_pid) { - /* - * child process - */ - - /* Don't use sclose in the child since it fools the socket leak detector */ - sclose_nolog(sockfds[0]); - if(dup2(sockfds[1], STDIN_FILENO) == -1) { - failf(data, "Could not redirect child stdin. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - exit(1); - } - - if(dup2(sockfds[1], STDOUT_FILENO) == -1) { - failf(data, "Could not redirect child stdout. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - exit(1); - } - - if(domain) - execl(ntlm_auth, ntlm_auth, - "--helper-protocol", "ntlmssp-client-1", - "--use-cached-creds", - "--username", username, - "--domain", domain, - NULL); - else - execl(ntlm_auth, ntlm_auth, - "--helper-protocol", "ntlmssp-client-1", - "--use-cached-creds", - "--username", username, - NULL); - - sclose_nolog(sockfds[1]); - failf(data, "Could not execl(). errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - exit(1); - } - - sclose(sockfds[1]); - ntlm->ntlm_auth_hlpr_socket = sockfds[0]; - ntlm->ntlm_auth_hlpr_pid = child_pid; - free(domain); - free(ntlm_auth_alloc); - return CURLE_OK; - -done: - free(domain); - free(ntlm_auth_alloc); - return CURLE_REMOTE_ACCESS_DENIED; -} - -/* if larger than this, something is seriously wrong */ -#define MAX_NTLM_WB_RESPONSE 100000 - -static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, - const char *input, curlntlm state) -{ - size_t len_in = strlen(input), len_out = 0; - struct dynbuf b; - char *ptr = NULL; - usigned char buf[1024] - Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE); - - while(len_in > 0) { - ssize_t written = wakeup_write(ntlm->ntlm_auth_hlpr_socket, input, len_in); - if(written == -1) { - /* Interrupted by a signal, retry it */ - if(errno == EINTR) - continue; - /* write failed if other errors happen */ - goto done; - } - input += written; - len_in -= written; - } - /* Read one line */ - while(1) { - ssize_t size = - wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, sizeof(buf)); - if(size == -1) { - if(errno == EINTR) - continue; - goto done; - } - else if(size == 0) - goto done; - - if(Curl_dyn_addn(&b, buf, size)) - goto done; - - len_out = Curl_dyn_len(&b); - ptr = Curl_dyn_ptr(&b); - if(len_out && ptr[len_out - 1] == '\n') { - ptr[len_out - 1] = '\0'; - break; /* done! */ - } - /* loop */ - } - - /* Samba/winbind installed but not configured */ - if(state == NTLMSTATE_TYPE1 && - len_out == 3 && - ptr[0] == 'P' && ptr[1] == 'W') - goto done; - /* invalid response */ - if(len_out < 4) - goto done; - if(state == NTLMSTATE_TYPE1 && - (ptr[0]!='Y' || ptr[1]!='R' || ptr[2]!=' ')) - goto done; - if(state == NTLMSTATE_TYPE2 && - (ptr[0]!='K' || ptr[1]!='K' || ptr[2]!=' ') && - (ptr[0]!='A' || ptr[1]!='F' || ptr[2]!=' ')) - goto done; - - ntlm->response = strdup(ptr + 3); - Curl_dyn_free(&b); - if(!ntlm->response) - return CURLE_OUT_OF_MEMORY; - return CURLE_OK; -done: - Curl_dyn_free(&b); - return CURLE_REMOTE_ACCESS_DENIED; -} - -CURLcode Curl_input_ntlm_wb(struct Curl_easy *data, - struct connectdata *conn, - bool proxy, - const char *header) -{ - struct ntlmdata *ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; - curlntlm *state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; - - (void) data; /* In case it gets unused by nop log macros. */ - - if(!checkprefix("NTLM", header)) - return CURLE_BAD_CONTENT_ENCODING; - - header += strlen("NTLM"); - while(*header && ISSPACE(*header)) - header++; - - if(*header) { - ntlm->challenge = strdup(header); - if(!ntlm->challenge) - return CURLE_OUT_OF_MEMORY; - - *state = NTLMSTATE_TYPE2; /* We got a type-2 message */ - } - else { - if(*state == NTLMSTATE_LAST) { - infof(data, "NTLM auth restarted"); - Curl_http_auth_cleanup_ntlm_wb(conn); - } - else if(*state == NTLMSTATE_TYPE3) { - infof(data, "NTLM handshake rejected"); - Curl_http_auth_cleanup_ntlm_wb(conn); - *state = NTLMSTATE_NONE; - return CURLE_REMOTE_ACCESS_DENIED; - } - else if(*state >= NTLMSTATE_TYPE1) { - infof(data, "NTLM handshake failure (internal error)"); - return CURLE_REMOTE_ACCESS_DENIED; - } - - *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */ - } - - return CURLE_OK; -} - -/* - * This is for creating ntlm header output by delegating challenge/response - * to Samba's winbind daemon helper ntlm_auth. - */ -CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn, - bool proxy) -{ - /* point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for an HTTP proxy */ - char **allocuserpwd; - /* point to the name and password for this */ - const char *userp; - struct ntlmdata *ntlm; - curlntlm *state; - struct auth *authp; - - CURLcode res = CURLE_OK; - - DEBUGASSERT(conn); - DEBUGASSERT(data); - - if(proxy) { -#ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->state.aptr.proxyuserpwd; - userp = conn->http_proxy.user; - ntlm = &conn->proxyntlm; - state = &conn->proxy_ntlm_state; - authp = &data->state.authproxy; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - else { - allocuserpwd = &data->state.aptr.userpwd; - userp = conn->user; - ntlm = &conn->ntlm; - state = &conn->http_ntlm_state; - authp = &data->state.authhost; - } - authp->done = FALSE; - - /* not set means empty */ - if(!userp) - userp = ""; - - switch(*state) { - case NTLMSTATE_TYPE1: - default: - /* Use Samba's 'winbind' daemon to support NTLM authentication, - * by delegating the NTLM challenge/response protocol to a helper - * in ntlm_auth. - * https://web.archive.org/web/20190925164737 - * /devel.squid-cache.org/ntlm/squid_helper_protocol.html - * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html - * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html - * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this - * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute - * filename of ntlm_auth helper. - * If NTLM authentication using winbind fails, go back to original - * request handling process. - */ - /* Create communication with ntlm_auth */ - res = ntlm_wb_init(data, ntlm, userp); - if(res) - return res; - res = ntlm_wb_response(data, ntlm, "YR\n", *state); - if(res) - return res; - - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - ntlm->response); - DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); - Curl_safefree(ntlm->response); - if(!*allocuserpwd) - return CURLE_OUT_OF_MEMORY; - break; - - case NTLMSTATE_TYPE2: { - char *input = aprintf("TT %s\n", ntlm->challenge); - if(!input) - return CURLE_OUT_OF_MEMORY; - res = ntlm_wb_response(data, ntlm, input, *state); - free(input); - if(res) - return res; - - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - ntlm->response); - DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); - *state = NTLMSTATE_TYPE3; /* we sent a type-3 */ - authp->done = TRUE; - Curl_http_auth_cleanup_ntlm_wb(conn); - if(!*allocuserpwd) - return CURLE_OUT_OF_MEMORY; - break; - } - case NTLMSTATE_TYPE3: - /* connection is already authenticated, - * don't send a header in future requests */ - *state = NTLMSTATE_LAST; - FALLTHROUGH(); - case NTLMSTATE_LAST: - Curl_safefree(*allocuserpwd); - authp->done = TRUE; - break; - } - - return CURLE_OK; -} - -void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn) -{ - ntlm_wb_cleanup(&conn->ntlm); - ntlm_wb_cleanup(&conn->proxyntlm); -} - -#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.h b/Utilities/cmcurl/lib/curl_ntlm_wb.h deleted file mode 100644 index 37704c0..0000000 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.h +++ /dev/null
@@ -1,45 +0,0 @@ -#ifndef HEADER_CURL_NTLM_WB_H -#define HEADER_CURL_NTLM_WB_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_NTLM) && \ - defined(NTLM_WB_ENABLED) - -/* this is for ntlm header input */ -CURLcode Curl_input_ntlm_wb(struct Curl_easy *data, - struct connectdata *conn, bool proxy, - const char *header); - -/* this is for creating ntlm header output */ -CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn, - bool proxy); - -void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn); - -#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ - -#endif /* HEADER_CURL_NTLM_WB_H */
diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c index 856423d..144f880 100644 --- a/Utilities/cmcurl/lib/curl_path.c +++ b/Utilities/cmcurl/lib/curl_path.c
@@ -98,8 +98,8 @@ return CURLE_OK; } -/* The get_pathname() function is being borrowed from OpenSSH sftp.c - version 4.6p1. */ +/* The original get_pathname() function came from OpenSSH sftp.c version + 4.6p1. */ /* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * @@ -115,38 +115,37 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir) + +#define MAX_PATHLENGTH 65535 /* arbitrary long */ + +CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir) { const char *cp = *cpp, *end; char quot; - unsigned int i, j; - size_t fullPathLength, pathLength; - bool relativePath = false; + unsigned int i; static const char WHITESPACE[] = " \t\r\n"; + struct dynbuf out; + CURLcode result; DEBUGASSERT(homedir); - if(!*cp || !homedir) { - *cpp = NULL; - *path = NULL; + *path = NULL; + *cpp = NULL; + if(!*cp || !homedir) return CURLE_QUOTE_ERROR; - } + + Curl_dyn_init(&out, MAX_PATHLENGTH); + /* Ignore leading whitespace */ cp += strspn(cp, WHITESPACE); - /* Allocate enough space for home directory and filename + separator */ - fullPathLength = strlen(cp) + strlen(homedir) + 2; - *path = malloc(fullPathLength); - if(!*path) - return CURLE_OUT_OF_MEMORY; /* Check for quoted filenames */ if(*cp == '\"' || *cp == '\'') { quot = *cp++; /* Search for terminating quote, unescape some chars */ - for(i = j = 0; i <= strlen(cp); i++) { + for(i = 0; i <= strlen(cp); i++) { if(cp[i] == quot) { /* Found quote */ i++; - (*path)[j] = '\0'; break; } if(cp[i] == '\0') { /* End of string */ @@ -159,40 +158,45 @@ goto fail; } } - (*path)[j++] = cp[i]; + result = Curl_dyn_addn(&out, &cp[i], 1); + if(result) + return result; } - if(j == 0) { + if(!Curl_dyn_len(&out)) goto fail; - } - *cpp = cp + i + strspn(cp + i, WHITESPACE); + + /* return pointer to second parameter if it exists */ + *cpp = &cp[i] + strspn(&cp[i], WHITESPACE); } else { /* Read to end of filename - either to whitespace or terminator */ end = strpbrk(cp, WHITESPACE); if(!end) end = strchr(cp, '\0'); + /* return pointer to second parameter if it exists */ *cpp = end + strspn(end, WHITESPACE); - pathLength = 0; - relativePath = (cp[0] == '/' && cp[1] == '~' && cp[2] == '/'); + /* Handling for relative path - prepend home directory */ - if(relativePath) { - strcpy(*path, homedir); - pathLength = strlen(homedir); - (*path)[pathLength++] = '/'; - (*path)[pathLength] = '\0'; + if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') { + result = Curl_dyn_add(&out, homedir); + if(!result) + result = Curl_dyn_addn(&out, "/", 1); + if(result) + return result; cp += 3; } /* Copy path name up until first "whitespace" */ - memcpy(&(*path)[pathLength], cp, (int)(end - cp)); - pathLength += (int)(end - cp); - (*path)[pathLength] = '\0'; + result = Curl_dyn_addn(&out, cp, (end - cp)); + if(result) + return result; } + *path = Curl_dyn_ptr(&out); return CURLE_OK; fail: - Curl_safefree(*path); + Curl_dyn_free(&out); return CURLE_QUOTE_ERROR; }
diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h index cbe51c2..6fdb2fd 100644 --- a/Utilities/cmcurl/lib/curl_path.h +++ b/Utilities/cmcurl/lib/curl_path.h
@@ -45,5 +45,5 @@ char *homedir, char **path); -CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir); +CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir); #endif /* HEADER_CURL_PATH_H */
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 147b12a..76eff78 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c
@@ -35,8 +35,10 @@ #include "warnless.h" #include <curl/curl.h> #include <librtmp/rtmp.h> + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" #include "curl_memory.h" -/* The last #include file should be: */ #include "memdebug.h" #if defined(_WIN32) && !defined(USE_LWIPSOCK) @@ -66,7 +68,7 @@ */ const struct Curl_handler Curl_handler_rtmp = { - "RTMP", /* scheme */ + "rtmp", /* scheme */ rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ @@ -80,6 +82,7 @@ ZERO_NULL, /* perform_getsock */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTMP, /* defport */ @@ -89,7 +92,7 @@ }; const struct Curl_handler Curl_handler_rtmpt = { - "RTMPT", /* scheme */ + "rtmpt", /* scheme */ rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ @@ -103,6 +106,7 @@ ZERO_NULL, /* perform_getsock */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTMPT, /* defport */ @@ -112,7 +116,7 @@ }; const struct Curl_handler Curl_handler_rtmpe = { - "RTMPE", /* scheme */ + "rtmpe", /* scheme */ rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ @@ -126,6 +130,7 @@ ZERO_NULL, /* perform_getsock */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTMP, /* defport */ @@ -135,7 +140,7 @@ }; const struct Curl_handler Curl_handler_rtmpte = { - "RTMPTE", /* scheme */ + "rtmpte", /* scheme */ rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ @@ -149,6 +154,7 @@ ZERO_NULL, /* perform_getsock */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTMPT, /* defport */ @@ -158,7 +164,7 @@ }; const struct Curl_handler Curl_handler_rtmps = { - "RTMPS", /* scheme */ + "rtmps", /* scheme */ rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ @@ -172,6 +178,7 @@ ZERO_NULL, /* perform_getsock */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTMPS, /* defport */ @@ -181,7 +188,7 @@ }; const struct Curl_handler Curl_handler_rtmpts = { - "RTMPTS", /* scheme */ + "rtmpts", /* scheme */ rtmp_setup_connection, /* setup_connection */ rtmp_do, /* do_it */ rtmp_done, /* done */ @@ -195,6 +202,7 @@ ZERO_NULL, /* perform_getsock */ rtmp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTMPS, /* defport */ @@ -265,10 +273,10 @@ if(data->state.upload) { Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); } else - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); *done = TRUE; return CURLE_OK; } @@ -335,4 +343,20 @@ return num; } + +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'; + + 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 index 9b93ee0..339d3a4 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.h +++ b/Utilities/cmcurl/lib/curl_rtmp.h
@@ -30,6 +30,8 @@ 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 66639cb..ba8911b 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -376,7 +376,7 @@ sasl->authused = SASL_MECH_EXTERNAL; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_external_message(conn->user, &resp); + Curl_auth_create_external_message(conn->user, &resp); } else if(data->state.aptr.user) { #if defined(USE_KERBEROS5) @@ -498,7 +498,7 @@ sasl->authused = SASL_MECH_LOGIN; if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_login_message(conn->user, &resp); + Curl_auth_create_login_message(conn->user, &resp); } } @@ -576,14 +576,14 @@ conn->user, conn->passwd, &resp); break; case SASL_LOGIN: - result = Curl_auth_create_login_message(conn->user, &resp); + Curl_auth_create_login_message(conn->user, &resp); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: - result = Curl_auth_create_login_message(conn->passwd, &resp); + Curl_auth_create_login_message(conn->passwd, &resp); break; case SASL_EXTERNAL: - result = Curl_auth_create_external_message(conn->user, &resp); + Curl_auth_create_external_message(conn->user, &resp); break; #ifdef USE_GSASL case SASL_GSASL:
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index f856f07..f7ba7e9 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -72,6 +72,11 @@ # endif #endif +/* Compatibility */ +#if defined(ENABLE_IPV6) +# define USE_IPV6 1 +#endif + /* * Include configuration script results or hand-crafted * configuration file for platforms which lack config tool. @@ -270,6 +275,13 @@ #include <curl/system.h> +/* Helper macro to expand and concatenate two macros. + * Direct macros concatenation does not work because macros + * are not expanded before direct concatenation. + */ +#define CURL_CONC_MACROS_(A,B) A ## B +#define CURL_CONC_MACROS(A,B) CURL_CONC_MACROS_(A,B) + /* curl uses its own printf() function internally. It understands the GNU * format. Use this format, so that is matches the GNU format attribute we * use with the mingw compiler, allowing it to verify them at compile-time. @@ -283,7 +295,8 @@ /* based on logic in "curl/mprintf.h" */ -#if (defined(__GNUC__) || defined(__clang__)) && \ +#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__) @@ -306,7 +319,7 @@ #include <TargetConditionals.h> #define USE_RESOLVE_ON_IPS 1 # if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \ - defined(ENABLE_IPV6) + defined(USE_IPV6) # define CURL_MACOS_CALL_COPYPROXIES 1 # endif #endif @@ -412,11 +425,9 @@ # define LSEEK_ERROR (__int64)-1 # define open curlx_win32_open # define fopen(fname,mode) curlx_win32_fopen(fname, mode) -# define access(fname,mode) curlx_win32_access(fname, mode) int curlx_win32_open(const char *filename, int oflag, ...); int curlx_win32_stat(const char *path, struct_stat *buffer); FILE *curlx_win32_fopen(const char *filename, const char *mode); - int curlx_win32_access(const char *path, int mode); #endif /* @@ -435,11 +446,9 @@ # define struct_stat struct _stat # define open curlx_win32_open # define fopen(fname,mode) curlx_win32_fopen(fname, mode) -# define access(fname,mode) curlx_win32_access(fname, mode) int curlx_win32_stat(const char *path, struct_stat *buffer); int curlx_win32_open(const char *filename, int oflag, ...); FILE *curlx_win32_fopen(const char *filename, const char *mode); - int curlx_win32_access(const char *path, int mode); # endif # define LSEEK_ERROR (long)-1 #endif @@ -510,6 +519,20 @@ #endif #define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) +#if (SIZEOF_CURL_OFF_T != 8) +# error "curl_off_t must be exactly 64 bits" +#else + typedef unsigned CURL_TYPEOF_CURL_OFF_T curl_uint64_t; + typedef CURL_TYPEOF_CURL_OFF_T curl_int64_t; +# ifndef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU must be defined" +# endif +# define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU +# define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX) +# define CURL_PRId64 CURL_FORMAT_CURL_OFF_T +# define CURL_PRIu64 CURL_FORMAT_CURL_OFF_TU +#endif + #if (SIZEOF_TIME_T == 4) # ifdef HAVE_TIME_T_UNSIGNED # define TIME_T_MAX UINT_MAX @@ -614,9 +637,9 @@ * Mutually exclusive CURLRES_* definitions. */ -#if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO) +#if defined(USE_IPV6) && defined(HAVE_GETADDRINFO) # define CURLRES_IPV6 -#elif defined(ENABLE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__)) +#elif defined(USE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__)) /* assume on Windows that IPv6 without getaddrinfo is a broken build */ # error "Unexpected build: IPv6 is enabled but getaddrinfo was not found." #else @@ -638,13 +661,14 @@ /* ---------------------------------------------------------------- */ -#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN) +#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && \ + !defined(USE_WIN32_IDN) && !defined(USE_APPLE_IDN) /* The lib and header are present */ #define USE_LIBIDN2 #endif -#if defined(USE_LIBIDN2) && defined(USE_WIN32_IDN) -#error "Both libidn2 and WinIDN are enabled, choose one." +#if defined(USE_LIBIDN2) && (defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)) +#error "libidn2 cannot be enabled with WinIDN or AppleIDN, choose one." #endif #define LIBIDN_REQUIRED_VERSION "0.4.1" @@ -698,6 +722,13 @@ ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7))) # define UNUSED_PARAM __attribute__((__unused__)) # define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#elif defined(__IAR_SYSTEMS_ICC__) +# define UNUSED_PARAM __attribute__((__unused__)) +# if (__VER__ >= 9040001) +# define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define WARN_UNUSED_RESULT +# endif #else # define UNUSED_PARAM /* NOTHING */ # define WARN_UNUSED_RESULT @@ -706,7 +737,8 @@ /* noreturn attribute */ #if !defined(CURL_NORETURN) -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__) # define CURL_NORETURN __attribute__((__noreturn__)) #elif defined(_MSC_VER) && (_MSC_VER >= 1200) # define CURL_NORETURN __declspec(noreturn) @@ -849,7 +881,6 @@ #error "Multi-SSL combined with QUIC is not supported" #endif -#define ENABLE_QUIC #define USE_HTTP3 #endif @@ -880,4 +911,26 @@ #define OPENSSL_SUPPRESS_DEPRECATED #endif +#if defined(inline) + /* 'inline' is defined as macro and assumed to be correct */ + /* No need for 'inline' replacement */ +#elif defined(__cplusplus) + /* The code is compiled with C++ compiler. + C++ always supports 'inline'. */ + /* No need for 'inline' replacement */ +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 + /* C99 (and later) supports 'inline' keyword */ + /* No need for 'inline' replacement */ +#elif defined(__GNUC__) && __GNUC__ >= 3 + /* GCC supports '__inline__' as an extension */ +# define inline __inline__ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + /* MSC supports '__inline' from VS 2005 (or even earlier) */ +# define inline __inline +#else + /* Probably 'inline' is not supported by compiler. + Define to the empty string to be on the safe side. */ +# define inline /* empty */ +#endif + #endif /* HEADER_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index bf0ee66..40c7bcb 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h
@@ -164,9 +164,7 @@ (RECV_TYPE_ARG4)(0)) #else /* HAVE_RECV */ #ifndef sread - /* */ - Error Missing_definition_of_macro_sread - /* */ +#error "Missing definition of macro sread!" #endif #endif /* HAVE_RECV */ @@ -184,9 +182,7 @@ (SEND_TYPE_ARG4)(SEND_4TH_ARG)) #else /* HAVE_SEND */ #ifndef swrite - /* */ - Error Missing_definition_of_macro_swrite - /* */ +#error "Missing definition of macro swrite!" #endif #endif /* HAVE_SEND */
diff --git a/Utilities/cmcurl/lib/curl_sha512_256.c b/Utilities/cmcurl/lib/curl_sha512_256.c new file mode 100644 index 0000000..5f2e995 --- /dev/null +++ b/Utilities/cmcurl/lib/curl_sha512_256.c
@@ -0,0 +1,850 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>. + * + * 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_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256) + +#include "curl_sha512_256.h" +#include "warnless.h" + +/* The recommended order of the TLS backends: + * * OpenSSL + * * GnuTLS + * * wolfSSL + * * Schannel SSPI + * * SecureTransport (Darwin) + * * mbedTLS + * * BearSSL + * * rustls + * Skip the backend if it does not support the required algorithm */ + +#if defined(USE_OPENSSL) +# include <openssl/opensslv.h> +# if (!defined(LIBRESSL_VERSION_NUMBER) && \ + defined(OPENSSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER >= 0x10101000L)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + (LIBRESSL_VERSION_NUMBER >= 0x3080000fL)) +# include <openssl/opensslconf.h> +# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512) +# include <openssl/evp.h> +# define USE_OPENSSL_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# ifdef __NetBSD__ +/* Some NetBSD versions has a bug in SHA-512/256. + * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 + * The problematic versions: + * - NetBSD before 9.4 + * - NetBSD 9 all development versions (9.99.x) + * - NetBSD 10 development versions (10.99.x) before 10.99.11 + * 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. */ +# include <sys/param.h> +# if __NetBSD_Version__ < 904000000 || \ + (__NetBSD_Version__ >= 999000000 && \ + __NetBSD_Version__ < 1000000000) || \ + (__NetBSD_Version__ >= 1099000000 && \ + __NetBSD_Version__ < 1099001100) +# define NEED_NETBSD_SHA512_256_WORKAROUND 1 +# include <string.h> +# endif +# endif +# endif +# endif +#endif /* USE_OPENSSL */ + + +#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) +# include <nettle/sha.h> +# if defined(SHA512_256_DIGEST_SIZE) +# define USE_GNUTLS_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# endif +#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */ + +#if defined(USE_OPENSSL_SHA512_256) + +/* OpenSSL does not provide macros for SHA-512/256 sizes */ + +/** + * Size of the SHA-512/256 single processing block in bytes. + */ +#define SHA512_256_BLOCK_SIZE 128 + +/** + * Size of the SHA-512/256 resulting digest in bytes. + * This is the final digest size, not intermediate hash. + */ +#define SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_LENGTH + +/** + * Context type used for SHA-512/256 calculations + */ +typedef EVP_MD_CTX *Curl_sha512_256_ctx; + +/** + * Initialise structure for SHA-512/256 calculation. + * + * @param context the calculation context + * @return CURLE_OK if succeed, + * error code otherwise + */ +static CURLcode +Curl_sha512_256_init(void *context) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + *ctx = EVP_MD_CTX_create(); + if(!*ctx) + return CURLE_OUT_OF_MEMORY; + + if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) { + /* Check whether the header and this file use the same numbers */ + DEBUGASSERT(EVP_MD_CTX_size(*ctx) == SHA512_256_DIGEST_SIZE); + /* Check whether the block size is correct */ + DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == SHA512_256_BLOCK_SIZE); + + return CURLE_OK; /* Success */ + } + + /* Cleanup */ + EVP_MD_CTX_destroy(*ctx); + return CURLE_FAILED_INIT; +} + + +/** + * Process portion of bytes. + * + * @param context the calculation context + * @param data bytes to add to hash + * @return CURLE_OK if succeed, + * error code otherwise + */ +static CURLcode +Curl_sha512_256_update(void *context, + const unsigned char *data, + size_t length) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + if(!EVP_DigestUpdate(*ctx, data, length)) + return CURLE_SSL_CIPHER; + + return CURLE_OK; +} + + +/** + * Finalise SHA-512/256 calculation, return digest. + * + * @param context the calculation context + * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @return CURLE_OK if succeed, + * error code otherwise + */ +static CURLcode +Curl_sha512_256_finish(unsigned char *digest, + void *context) +{ + CURLcode ret; + 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[SHA512_256_DIGEST_SIZE * 2]; + ret = EVP_DigestFinal_ex(*ctx, + tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + if(ret == CURLE_OK) + memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE); + 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; +#endif /* ! NEED_NETBSD_SHA512_256_WORKAROUND */ + + EVP_MD_CTX_destroy(*ctx); + *ctx = NULL; + + return ret; +} + +#elif defined(USE_GNUTLS_SHA512_256) + +/** + * Context type used for SHA-512/256 calculations + */ +typedef struct sha512_256_ctx Curl_sha512_256_ctx; + +/** + * Initialise structure for SHA-512/256 calculation. + * + * @param context the calculation context + * @return always CURLE_OK + */ +static CURLcode +Curl_sha512_256_init(void *context) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + /* Check whether the header and this file use the same numbers */ + DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE); + + sha512_256_init(ctx); + + return CURLE_OK; +} + + +/** + * Process portion of bytes. + * + * @param context the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + * @return always CURLE_OK + */ +static CURLcode +Curl_sha512_256_update(void *context, + const unsigned char *data, + size_t length) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + DEBUGASSERT((data != NULL) || (length == 0)); + + sha512_256_update(ctx, length, (const uint8_t *)data); + + return CURLE_OK; +} + + +/** + * Finalise SHA-512/256 calculation, return digest. + * + * @param context the calculation context + * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @return always CURLE_OK + */ +static CURLcode +Curl_sha512_256_finish(unsigned char *digest, + void *context) +{ + Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + + sha512_256_digest(ctx, (size_t)SHA512_256_DIGEST_SIZE, (uint8_t *)digest); + + return CURLE_OK; +} + +#else /* No system or TLS backend SHA-512/256 implementation available */ + +/* Use local implementation */ +#define HAS_SHA512_256_IMPLEMENTATION 1 + +/* ** 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 optimisations. Performance ** * + * ** can be significantly improved. Big-endian store and load macros ** * + * ** are obvious targets for optimisation. ** */ + +#ifdef __GNUC__ +# if defined(__has_attribute) && defined(__STDC_VERSION__) +# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901 +# define MHDX_INLINE inline __attribute__((always_inline)) +# endif +# endif +#endif + +#if !defined(MHDX_INLINE) && \ + defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__) +# if _MSC_VER >= 1400 +# define MHDX_INLINE __forceinline +# endif +#endif + +#if !defined(MHDX_INLINE) + /* Assume that 'inline' keyword works or the + * macro was already defined correctly. */ +# define MHDX_INLINE inline +#endif + +/* Bits manipulation macros and functions. + Can be moved to other headers to reuse. */ + +#define MHDX_GET_64BIT_BE(ptr) \ + ( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[3]) << 32) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[4]) << 24) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[5]) << 16) | \ + ((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \ + (curl_uint64_t)(((const unsigned char*)(ptr))[7]) ) + +#define MHDX_PUT_64BIT_BE(ptr,val) do { \ + ((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \ + ((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \ + ((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \ + ((unsigned char*)(ptr))[4]=(unsigned char)(((curl_uint64_t)(val)) >> 24); \ + ((unsigned char*)(ptr))[3]=(unsigned char)(((curl_uint64_t)(val)) >> 32); \ + ((unsigned char*)(ptr))[2]=(unsigned char)(((curl_uint64_t)(val)) >> 40); \ + ((unsigned char*)(ptr))[1]=(unsigned char)(((curl_uint64_t)(val)) >> 48); \ + ((unsigned char*)(ptr))[0]=(unsigned char)(((curl_uint64_t)(val)) >> 56); \ + } while(0) + +/* Defined as a function. The macro version may duplicate the binary code + * size as each argument is used twice, so if any calculation is used + * as an argument, the calculation could be done twice. */ +static MHDX_INLINE curl_uint64_t +MHDx_rotr64(curl_uint64_t value, unsigned int bits) +{ + bits %= 64; + if(0 == bits) + return value; + /* Defined in a form which modern compiler could optimise. */ + return (value >> bits) | (value << (64 - bits)); +} + +/* SHA-512/256 specific data */ + +/** + * Number of bits in a single SHA-512/256 word. + */ +#define SHA512_256_WORD_SIZE_BITS 64 + +/** + * Number of bytes in a single SHA-512/256 word. + */ +#define SHA512_256_BYTES_IN_WORD (SHA512_256_WORD_SIZE_BITS / 8) + +/** + * Hash is kept internally as 8 64-bit words. + * This is the intermediate hash size, used during computing the final digest. + */ +#define SHA512_256_HASH_SIZE_WORDS 8 + +/** + * Size of the SHA-512/256 resulting digest in words. + * This is the final digest size, not intermediate hash. + */ +#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS / 2) + +/** + * Size of the SHA-512/256 resulting digest in bytes + * This is the final digest size, not intermediate hash. + */ +#define SHA512_256_DIGEST_SIZE \ + (SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD) + +/** + * Size of the SHA-512/256 single processing block in bits. + */ +#define SHA512_256_BLOCK_SIZE_BITS 1024 + +/** + * Size of the SHA-512/256 single processing block in bytes. + */ +#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8) + +/** + * Size of the SHA-512/256 single processing block in words. + */ +#define SHA512_256_BLOCK_SIZE_WORDS \ + (SHA512_256_BLOCK_SIZE_BITS / SHA512_256_WORD_SIZE_BITS) + +/** + * SHA-512/256 calculation context + */ +struct mhdx_sha512_256ctx +{ + /** + * Intermediate hash value. The variable is properly aligned. Smart + * compilers may automatically use fast load/store instruction for big + * endian data on little endian machine. + */ + curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS]; + /** + * SHA-512/256 input data buffer. The buffer is properly aligned. Smart + * compilers may automatically use fast load/store instruction for big + * endian data on little endian machine. + */ + curl_uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS]; + /** + * The number of bytes, lower part + */ + curl_uint64_t count; + /** + * The number of bits, high part. Unlike lower part, this counts the number + * of bits, not bytes. + */ + curl_uint64_t count_bits_hi; +}; + +/** + * Context type used for SHA-512/256 calculations + */ +typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx; + + +/** + * Initialise structure for SHA-512/256 calculation. + * + * @param context the calculation context + * @return always CURLE_OK + */ +static CURLcode +MHDx_sha512_256_init(void *context) +{ + struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context; + + /* Check whether the header and this file use the same numbers */ + DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE); + + DEBUGASSERT(sizeof(curl_uint64_t) == 8); + + /* Initial hash values, see FIPS PUB 180-4 section 5.3.6.2 */ + /* Values generated by "IV Generation Function" as described in + * section 5.3.6 */ + ctx->H[0] = CURL_UINT64_C(0x22312194FC2BF72C); + ctx->H[1] = CURL_UINT64_C(0x9F555FA3C84C64C2); + ctx->H[2] = CURL_UINT64_C(0x2393B86B6F53B151); + ctx->H[3] = CURL_UINT64_C(0x963877195940EABD); + ctx->H[4] = CURL_UINT64_C(0x96283EE2A88EFFE3); + ctx->H[5] = CURL_UINT64_C(0xBE5E1E2553863992); + ctx->H[6] = CURL_UINT64_C(0x2B0199FC2C85B8AA); + ctx->H[7] = CURL_UINT64_C(0x0EB72DDC81C52CA2); + + /* Initialise number of bytes and high part of number of bits. */ + ctx->count = CURL_UINT64_C(0); + ctx->count_bits_hi = CURL_UINT64_C(0); + + return CURLE_OK; +} + + +/** + * Base of the SHA-512/256 transformation. + * Gets a full 128 bytes block of data and updates hash values; + * @param H hash values + * @param data the data buffer with #SHA512_256_BLOCK_SIZE bytes block + */ +static void +MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], + const void *data) +{ + /* Working variables, + see FIPS PUB 180-4 section 6.7, 6.4. */ + curl_uint64_t a = H[0]; + curl_uint64_t b = H[1]; + curl_uint64_t c = H[2]; + curl_uint64_t d = H[3]; + curl_uint64_t e = H[4]; + curl_uint64_t f = H[5]; + curl_uint64_t g = H[6]; + curl_uint64_t h = H[7]; + + /* Data buffer, used as a cyclic buffer. + See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */ + curl_uint64_t W[16]; + + /* 'Ch' and 'Maj' macro functions are defined with widely-used optimisation. + See FIPS PUB 180-4 formulae 4.8, 4.9. */ +#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) ) +#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) ) + + /* Four 'Sigma' macro functions. + See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ +#define SIG0(x) \ + ( MHDx_rotr64((x), 28) ^ MHDx_rotr64((x), 34) ^ MHDx_rotr64((x), 39) ) +#define SIG1(x) \ + ( MHDx_rotr64((x), 14) ^ MHDx_rotr64((x), 18) ^ MHDx_rotr64((x), 41) ) +#define sig0(x) \ + ( MHDx_rotr64((x), 1) ^ MHDx_rotr64((x), 8) ^ ((x) >> 7) ) +#define sig1(x) \ + ( MHDx_rotr64((x), 19) ^ MHDx_rotr64((x), 61) ^ ((x) >> 6) ) + + if(1) { + unsigned int t; + /* K constants array. + See FIPS PUB 180-4 section 4.2.3 for K values. */ + static const curl_uint64_t K[80] = { + CURL_UINT64_C(0x428a2f98d728ae22), CURL_UINT64_C(0x7137449123ef65cd), + CURL_UINT64_C(0xb5c0fbcfec4d3b2f), CURL_UINT64_C(0xe9b5dba58189dbbc), + CURL_UINT64_C(0x3956c25bf348b538), CURL_UINT64_C(0x59f111f1b605d019), + CURL_UINT64_C(0x923f82a4af194f9b), CURL_UINT64_C(0xab1c5ed5da6d8118), + CURL_UINT64_C(0xd807aa98a3030242), CURL_UINT64_C(0x12835b0145706fbe), + CURL_UINT64_C(0x243185be4ee4b28c), CURL_UINT64_C(0x550c7dc3d5ffb4e2), + CURL_UINT64_C(0x72be5d74f27b896f), CURL_UINT64_C(0x80deb1fe3b1696b1), + CURL_UINT64_C(0x9bdc06a725c71235), CURL_UINT64_C(0xc19bf174cf692694), + CURL_UINT64_C(0xe49b69c19ef14ad2), CURL_UINT64_C(0xefbe4786384f25e3), + CURL_UINT64_C(0x0fc19dc68b8cd5b5), CURL_UINT64_C(0x240ca1cc77ac9c65), + CURL_UINT64_C(0x2de92c6f592b0275), CURL_UINT64_C(0x4a7484aa6ea6e483), + CURL_UINT64_C(0x5cb0a9dcbd41fbd4), CURL_UINT64_C(0x76f988da831153b5), + CURL_UINT64_C(0x983e5152ee66dfab), CURL_UINT64_C(0xa831c66d2db43210), + CURL_UINT64_C(0xb00327c898fb213f), CURL_UINT64_C(0xbf597fc7beef0ee4), + CURL_UINT64_C(0xc6e00bf33da88fc2), CURL_UINT64_C(0xd5a79147930aa725), + CURL_UINT64_C(0x06ca6351e003826f), CURL_UINT64_C(0x142929670a0e6e70), + CURL_UINT64_C(0x27b70a8546d22ffc), CURL_UINT64_C(0x2e1b21385c26c926), + CURL_UINT64_C(0x4d2c6dfc5ac42aed), CURL_UINT64_C(0x53380d139d95b3df), + CURL_UINT64_C(0x650a73548baf63de), CURL_UINT64_C(0x766a0abb3c77b2a8), + CURL_UINT64_C(0x81c2c92e47edaee6), CURL_UINT64_C(0x92722c851482353b), + CURL_UINT64_C(0xa2bfe8a14cf10364), CURL_UINT64_C(0xa81a664bbc423001), + CURL_UINT64_C(0xc24b8b70d0f89791), CURL_UINT64_C(0xc76c51a30654be30), + CURL_UINT64_C(0xd192e819d6ef5218), CURL_UINT64_C(0xd69906245565a910), + CURL_UINT64_C(0xf40e35855771202a), CURL_UINT64_C(0x106aa07032bbd1b8), + CURL_UINT64_C(0x19a4c116b8d2d0c8), CURL_UINT64_C(0x1e376c085141ab53), + CURL_UINT64_C(0x2748774cdf8eeb99), CURL_UINT64_C(0x34b0bcb5e19b48a8), + CURL_UINT64_C(0x391c0cb3c5c95a63), CURL_UINT64_C(0x4ed8aa4ae3418acb), + CURL_UINT64_C(0x5b9cca4f7763e373), CURL_UINT64_C(0x682e6ff3d6b2b8a3), + CURL_UINT64_C(0x748f82ee5defb2fc), CURL_UINT64_C(0x78a5636f43172f60), + CURL_UINT64_C(0x84c87814a1f0ab72), CURL_UINT64_C(0x8cc702081a6439ec), + CURL_UINT64_C(0x90befffa23631e28), CURL_UINT64_C(0xa4506cebde82bde9), + CURL_UINT64_C(0xbef9a3f7b2c67915), CURL_UINT64_C(0xc67178f2e372532b), + CURL_UINT64_C(0xca273eceea26619c), CURL_UINT64_C(0xd186b8c721c0c207), + CURL_UINT64_C(0xeada7dd6cde0eb1e), CURL_UINT64_C(0xf57d4f7fee6ed178), + CURL_UINT64_C(0x06f067aa72176fba), CURL_UINT64_C(0x0a637dc5a2c898a6), + CURL_UINT64_C(0x113f9804bef90dae), CURL_UINT64_C(0x1b710b35131c471b), + CURL_UINT64_C(0x28db77f523047d84), CURL_UINT64_C(0x32caab7b40c72493), + CURL_UINT64_C(0x3c9ebe0a15c9bebc), CURL_UINT64_C(0x431d67c49c100d4c), + CURL_UINT64_C(0x4cc5d4becb3e42b6), CURL_UINT64_C(0x597f299cfc657e2a), + CURL_UINT64_C(0x5fcb6fab3ad6faec), CURL_UINT64_C(0x6c44198c4a475817) + }; + + /* One step of SHA-512/256 computation, + see FIPS PUB 180-4 section 6.4.2 step 3. + * Note: this macro updates working variables in-place, without rotation. + * Note: the first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in + FIPS PUB 180-4 section 6.4.2 step 3. + the second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in + FIPS PUB 180-4 section 6.4.2 step 3. + * Note: 'wt' must be used exactly one time in this macro as macro for + 'wt' calculation may change other data as well every time when + used. */ +#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ + (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \ + (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0) + + /* One step of SHA-512/256 computation with working variables rotation, + see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns + all working variables on each step. */ +#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ + curl_uint64_t tmp_h_ = (vH); \ + SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \ + (vH) = (vG); \ + (vG) = (vF); \ + (vF) = (vE); \ + (vE) = (vD); \ + (vD) = (vC); \ + (vC) = (vB); \ + (vB) = (vA); \ + (vA) = tmp_h_; } while(0) + + /* Get value of W(t) from input data buffer for 0 <= t <= 15, + See FIPS PUB 180-4 section 6.2. + 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) \ + MHDX_GET_64BIT_BE( \ + ((const unsigned char*) (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 + stored in the array of W elements. */ + for(t = 0; t < 16; ++t) { + SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \ + W[t] = SHA512_GET_W_FROM_DATA(data, t)); + } + + /* 'W' generation and assignment for 16 <= t <= 79. + See FIPS PUB 180-4 section 6.4.2. + As only the last 16 'W' are used in calculations, it is possible to + use 16 elements array of W as a cyclic buffer. + Note: ((t-16) & 15) have same value as (t & 15) */ +#define Wgen(w,t) \ + (curl_uint64_t)( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15]) \ + + (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) ) + + /* During the last 64 steps, before making any calculation on each step, + current W element is generated from other W elements of the cyclic + buffer and the generated value is stored back in the cyclic buffer. */ + for(t = 16; t < 80; ++t) { + SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \ + W[t & 15] = Wgen(W, t)); + } + } + + /* Compute and store the intermediate hash. + See FIPS PUB 180-4 section 6.4.2 step 4. */ + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; +} + + +/** + * Process portion of bytes. + * + * @param context the calculation context + * @param data bytes to add to hash + * @param length number of bytes in @a data + * @return always CURLE_OK + */ +static CURLcode +MHDx_sha512_256_update(void *context, + const unsigned char *data, + size_t length) +{ + unsigned int bytes_have; /**< Number of bytes in the context buffer */ + struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context; + /* the void pointer here is required to mute Intel compiler warning */ + void *const ctx_buf = ctx->buffer; + + DEBUGASSERT((data != NULL) || (length == 0)); + + if(0 == length) + return CURLE_OK; /* Shortcut, do nothing */ + + /* Note: (count & (SHA512_256_BLOCK_SIZE-1)) + equals (count % SHA512_256_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1)); + ctx->count += length; + if(length > ctx->count) + ctx->count_bits_hi += 1U << 3; /* Value wrap */ + ctx->count_bits_hi += ctx->count >> 61; + ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF); + + if(0 != bytes_have) { + unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have; + if(length >= bytes_left) { + /* Combine new data with data in the buffer and process the full + block. */ + memcpy(((unsigned char *) ctx_buf) + bytes_have, + data, + bytes_left); + data += bytes_left; + length -= bytes_left; + MHDx_sha512_256_transform(ctx->H, ctx->buffer); + bytes_have = 0; + } + } + + while(SHA512_256_BLOCK_SIZE <= length) { + /* Process any full blocks of new data directly, + without copying to the buffer. */ + MHDx_sha512_256_transform(ctx->H, data); + data += SHA512_256_BLOCK_SIZE; + length -= SHA512_256_BLOCK_SIZE; + } + + if(0 != length) { + /* Copy incomplete block of new data (if any) + to the buffer. */ + memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length); + } + + return CURLE_OK; +} + + + +/** + * Size of "length" insertion in bits. + * See FIPS PUB 180-4 section 5.1.2. + */ +#define SHA512_256_SIZE_OF_LEN_ADD_BITS 128 + +/** + * Size of "length" insertion in bytes. + */ +#define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8) + +/** + * Finalise SHA-512/256 calculation, return digest. + * + * @param context the calculation context + * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @return always CURLE_OK + */ +static CURLcode +MHDx_sha512_256_finish(unsigned char *digest, + void *context) +{ + struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context; + curl_uint64_t num_bits; /**< Number of processed bits */ + unsigned int bytes_have; /**< Number of bytes in the context buffer */ + /* the void pointer here is required to mute Intel compiler warning */ + void *const ctx_buf = ctx->buffer; + + /* Memorise the number of processed bits. + The padding and other data added here during the postprocessing must + not change the amount of hashed data. */ + num_bits = ctx->count << 3; + + /* Note: (count & (SHA512_256_BLOCK_SIZE-1)) + equals (count % SHA512_256_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1)); + + /* Input data must be padded with a single bit "1", then with zeros and + the finally the length of data in bits must be added as the final bytes + of the last block. + See FIPS PUB 180-4 section 5.1.2. */ + + /* Data is always processed in form of bytes (not by individual bits), + therefore position of the first padding bit in byte is always + predefined (0x80). */ + /* Buffer always have space at least for one byte (as full buffers are + processed when formed). */ + ((unsigned char *) ctx_buf)[bytes_have++] = 0x80U; + + if(SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) { + /* No space in the current block to put the total length of message. + Pad the current block with zeros and process it. */ + if(bytes_have < SHA512_256_BLOCK_SIZE) + memset(((unsigned char *) ctx_buf) + bytes_have, 0, + SHA512_256_BLOCK_SIZE - bytes_have); + /* Process the full block. */ + MHDx_sha512_256_transform(ctx->H, ctx->buffer); + /* Start the new block. */ + bytes_have = 0; + } + + /* Pad the rest of the buffer with zeros. */ + memset(((unsigned char *) ctx_buf) + bytes_have, 0, + SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have); + /* Put high part of number of bits in processed message and then lower + part of number of bits as big-endian values. + See FIPS PUB 180-4 section 5.1.2. */ + /* Note: the target location is predefined and buffer is always aligned */ + MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + + SHA512_256_BLOCK_SIZE \ + - SHA512_256_SIZE_OF_LEN_ADD, \ + ctx->count_bits_hi); + MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ + + SHA512_256_BLOCK_SIZE \ + - SHA512_256_SIZE_OF_LEN_ADD \ + + SHA512_256_BYTES_IN_WORD, \ + num_bits); + /* Process the full final block. */ + MHDx_sha512_256_transform(ctx->H, ctx->buffer); + + /* Put in BE mode the leftmost part of the hash as the final digest. + See FIPS PUB 180-4 section 6.7. */ + + MHDX_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]); + MHDX_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]); + MHDX_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]); + MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]); + + /* Erase potentially sensitive data. */ + memset(ctx, 0, sizeof(struct mhdx_sha512_256ctx)); + + return CURLE_OK; +} + +/* Map to the local implementation */ +#define Curl_sha512_256_init MHDx_sha512_256_init +#define Curl_sha512_256_update MHDx_sha512_256_update +#define Curl_sha512_256_finish MHDx_sha512_256_finish + +#endif /* Local SHA-512/256 code */ + + +/** + * Compute SHA-512/256 hash for the given data in one function call + * @param[out] output the pointer to put the hash + * @param[in] input the pointer to the data to process + * @param input_size the size of the data pointed by @a input + * @return always #CURLE_OK + */ +CURLcode +Curl_sha512_256it(unsigned char *output, const unsigned char *input, + size_t input_size) +{ + Curl_sha512_256_ctx ctx; + CURLcode res; + + res = Curl_sha512_256_init(&ctx); + if(res != CURLE_OK) + return res; + + res = Curl_sha512_256_update(&ctx, (const void *) input, input_size); + + if(res != CURLE_OK) { + (void) Curl_sha512_256_finish(output, &ctx); + return res; + } + + return Curl_sha512_256_finish(output, &ctx); +} + +/* Wrapper function, takes 'unsigned int' as length type, returns void */ +static void +Curl_sha512_256_update_i(void *context, + const unsigned char *data, + unsigned int length) +{ + /* Hypothetically the function may fail, but assume it does not */ + (void) Curl_sha512_256_update(context, data, length); +} + +/* Wrapper function, returns void */ +static void +Curl_sha512_256_finish_v(unsigned char *result, + void *context) +{ + /* Hypothetically the function may fail, but assume it does not */ + (void) Curl_sha512_256_finish(result, context); +} + +/* Wrapper function, takes 'unsigned int' as length type, returns void */ + +const struct HMAC_params Curl_HMAC_SHA512_256[] = { + { + /* Initialize context procedure. */ + Curl_sha512_256_init, + /* Update context with data. */ + Curl_sha512_256_update_i, + /* Get final result procedure. */ + Curl_sha512_256_finish_v, + /* Context structure size. */ + sizeof(Curl_sha512_256_ctx), + /* Maximum key length (bytes). */ + SHA512_256_BLOCK_SIZE, + /* Result length (bytes). */ + SHA512_256_DIGEST_SIZE + } +}; + +#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */
diff --git a/Utilities/cmcurl/lib/curl_sha512_256.h b/Utilities/cmcurl/lib/curl_sha512_256.h new file mode 100644 index 0000000..30a9f14 --- /dev/null +++ b/Utilities/cmcurl/lib/curl_sha512_256.h
@@ -0,0 +1,44 @@ +#ifndef HEADER_CURL_SHA512_256_H +#define HEADER_CURL_SHA512_256_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256) + +#include <curl/curl.h> +#include "curl_hmac.h" + +#define CURL_HAVE_SHA512_256 + +extern const struct HMAC_params Curl_HMAC_SHA512_256[1]; + +#define SHA512_256_DIGEST_LENGTH 32 + +CURLcode +Curl_sha512_256it(unsigned char *output, const unsigned char *input, + size_t input_size); + +#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */ + +#endif /* HEADER_CURL_SHA256_H */
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index 222d936..93fa2da 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -131,7 +131,8 @@ void Curl_thread_destroy(curl_thread_t hnd) { - CloseHandle(hnd); + if(hnd != curl_thread_t_null) + CloseHandle(hnd); } int Curl_thread_join(curl_thread_t *hnd)
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c index b8dccc4..f017e21 100644 --- a/Utilities/cmcurl/lib/curl_trc.c +++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -36,6 +36,7 @@ #include "cf-socket.h" #include "connect.h" +#include "doh.h" #include "http2.h" #include "http_proxy.h" #include "cf-h1-proxy.h" @@ -110,19 +111,30 @@ /* Curl_infof() is for info message along the way */ #define MAXINFO 2048 +static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, + const char * const fmt, va_list ap) CURL_PRINTF(3, 0); + +static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, + const char * const fmt, va_list ap) +{ + int len = 0; + char buffer[MAXINFO + 2]; + if(feat) + len = msnprintf(buffer, MAXINFO, "[%s] ", feat->name); + len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); + buffer[len++] = '\n'; + buffer[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, buffer, len); +} + void Curl_infof(struct Curl_easy *data, const char *fmt, ...) { DEBUGASSERT(!strchr(fmt, '\n')); - if(data && data->set.verbose) { + if(Curl_trc_is_verbose(data)) { va_list ap; - int len; - char buffer[MAXINFO + 2]; va_start(ap, fmt); - len = mvsnprintf(buffer, MAXINFO, fmt, ap); + trc_infof(data, data->state.feat, fmt, ap); va_end(ap); - buffer[len++] = '\n'; - buffer[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, buffer, len); } } @@ -132,9 +144,16 @@ DEBUGASSERT(cf); if(Curl_trc_cf_is_verbose(cf, data)) { va_list ap; - int len; + int len = 0; char buffer[MAXINFO + 2]; - len = msnprintf(buffer, MAXINFO, "[%s] ", cf->cft->name); + if(data->state.feat) + len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", + data->state.feat->name); + if(cf->sockindex) + len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ", + cf->cft->name, cf->sockindex); + else + len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name); va_start(ap, fmt); len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); va_end(ap); @@ -144,6 +163,66 @@ } } +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, +}; + +void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_read, fmt, ap); + va_end(ap); + } +} + +void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_write, fmt, ap); + va_end(ap); + } +} + +#ifndef CURL_DISABLE_FTP +struct curl_trc_feat Curl_trc_feat_ftp = { + "FTP", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_ftp, fmt, ap); + va_end(ap); + } +} +#endif /* !CURL_DISABLE_FTP */ + +static struct curl_trc_feat *trc_feats[] = { + &Curl_trc_feat_read, + &Curl_trc_feat_write, +#ifndef CURL_DISABLE_FTP + &Curl_trc_feat_ftp, +#endif +#ifndef CURL_DISABLE_DOH + &Curl_doh_trc, +#endif + NULL, +}; static struct Curl_cftype *cf_types[] = { &Curl_cft_tcp, @@ -172,7 +251,7 @@ &Curl_cft_haproxy, &Curl_cft_socks_proxy, #endif /* !CURL_DISABLE_PROXY */ -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 &Curl_cft_http3, #endif #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) @@ -215,6 +294,15 @@ break; } } + for(i = 0; trc_feats[i]; ++i) { + if(strcasecompare(token, "all")) { + trc_feats[i]->log_level = lvl; + } + else if(strcasecompare(token, trc_feats[i]->name)) { + trc_feats[i]->log_level = lvl; + break; + } + } token = strtok_r(NULL, ", ", &tok_buf); } free(tmp);
diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h index 3a5387a..3d38018 100644 --- a/Utilities/cmcurl/lib/curl_trc.h +++ b/Utilities/cmcurl/lib/curl_trc.h
@@ -77,19 +77,52 @@ #define CURL_TRC_CF(data, cf, ...) \ do { if(Curl_trc_cf_is_verbose(cf, data)) \ Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) +#define CURL_TRC_WRITE(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) \ + Curl_trc_write(data, __VA_ARGS__); } while(0) +#define CURL_TRC_READ(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \ + Curl_trc_read(data, __VA_ARGS__); } while(0) -#else +#ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \ + Curl_trc_ftp(data, __VA_ARGS__); } while(0) +#endif /* !CURL_DISABLE_FTP */ + +#else /* CURL_HAVE_C99 */ + #define infof Curl_infof #define CURL_TRC_CF Curl_trc_cf_infof +#define CURL_TRC_WRITE Curl_trc_write +#define CURL_TRC_READ Curl_trc_read + +#ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP Curl_trc_ftp #endif +#endif /* !CURL_HAVE_C99 */ + #ifndef CURL_DISABLE_VERBOSE_STRINGS /* informational messages enabled */ -#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) +struct curl_trc_feat { + const char *name; + int log_level; +}; +extern struct curl_trc_feat Curl_trc_feat_read; +extern struct curl_trc_feat Curl_trc_feat_write; + +#define Curl_trc_is_verbose(data) \ + ((data) && (data)->set.verbose && \ + (!(data)->state.feat || \ + ((data)->state.feat->log_level >= CURL_LOG_LVL_INFO))) #define Curl_trc_cf_is_verbose(cf, data) \ - ((data) && (data)->set.verbose && \ - (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) + (Curl_trc_is_verbose(data) && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) +#define Curl_trc_ft_is_verbose(data, ft) \ + (Curl_trc_is_verbose(data) && \ + (ft)->log_level >= CURL_LOG_LVL_INFO) /** * Output an informational message when transfer's verbose logging is enabled. @@ -103,12 +136,26 @@ */ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) CURL_PRINTF(3, 4); +void Curl_trc_ft_infof(struct Curl_easy *data, struct curl_trc_feat *ft, + const char *fmt, ...) CURL_PRINTF(3, 4); +void Curl_trc_write(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +void Curl_trc_read(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); + +#ifndef CURL_DISABLE_FTP +extern struct curl_trc_feat Curl_trc_feat_ftp; +void Curl_trc_ftp(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif + #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ /* All informational messages are not compiled in for size savings */ -#define Curl_trc_is_verbose(d) ((void)(d), FALSE) -#define Curl_trc_cf_is_verbose(x,y) ((void)(x), (void)(y), FALSE) +#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) static void Curl_infof(struct Curl_easy *data, const char *fmt, ...) { @@ -122,6 +169,32 @@ (void)data; (void)cf; (void)fmt; } +struct curl_trc_feat; + +static void Curl_trc_ft_infof(struct Curl_easy *data, + struct curl_trc_feat *ft, + const char *fmt, ...) +{ + (void)data; (void)ft; (void)fmt; +} + +static void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +static void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} + +#ifndef CURL_DISABLE_FTP +static void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} +#endif + #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ #endif /* HEADER_CURL_TRC_H */
diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h index 7a753d6..54e4279 100644 --- a/Utilities/cmcurl/lib/curlx.h +++ b/Utilities/cmcurl/lib/curlx.h
@@ -77,7 +77,6 @@ */ -#define curlx_getenv curl_getenv #define curlx_mvsnprintf curl_mvsnprintf #define curlx_msnprintf curl_msnprintf #define curlx_maprintf curl_maprintf
diff --git a/Utilities/cmcurl/lib/cw-out.c b/Utilities/cmcurl/lib/cw-out.c new file mode 100644 index 0000000..4e56c6a --- /dev/null +++ b/Utilities/cmcurl/lib/cw-out.c
@@ -0,0 +1,474 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#include <curl/curl.h> + +#include "urldata.h" +#include "cfilters.h" +#include "headers.h" +#include "multiif.h" +#include "sendf.h" +#include "cw-out.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +/** + * OVERALL DESIGN of this client writer + * + * The 'cw-out' writer is supposed to be the last writer in a transfer's + * stack. It is always added when that stack is initialized. Its purpose + * is to pass BODY and HEADER bytes to the client-installed callback + * functions. + * + * These callback may return `CURL_WRITEFUNC_PAUSE` to indicate that the + * data had not been written and the whole transfer should stop receiving + * new data. Or at least, stop calling the functions. When the transfer + * is "unpaused" by the client, the previous data shall be passed as + * if nothing happened. + * + * The `cw-out` writer therefore manages buffers for bytes that could + * not be written. Data that was already in flight from the server also + * needs buffering on paused transfer when it arrives. + * + * In addition, the writer allows buffering of "small" body writes, + * so client functions are called less often. That is only enabled on a + * number of conditions. + * + * HEADER and BODY data may arrive in any order. For paused transfers, + * a list of `struct cw_out_buf` is kept for `cw_out_type` types. The + * list may be: [BODY]->[HEADER]->[BODY]->[HEADER].... + * When unpausing, this list is "played back" to the client callbacks. + * + * The amount of bytes being buffered is limited by `DYN_PAUSE_BUFFER` + * and when that is exceeded `CURLE_TOO_LARGE` is returned as error. + */ +typedef enum { + CW_OUT_NONE, + CW_OUT_BODY, + CW_OUT_HDS +} cw_out_type; + +struct cw_out_buf { + struct cw_out_buf *next; + struct dynbuf b; + cw_out_type type; +}; + +static struct cw_out_buf *cw_out_buf_create(cw_out_type otype) +{ + struct cw_out_buf *cwbuf = calloc(1, sizeof(*cwbuf)); + if(cwbuf) { + cwbuf->type = otype; + Curl_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER); + } + return cwbuf; +} + +static void cw_out_buf_free(struct cw_out_buf *cwbuf) +{ + if(cwbuf) { + Curl_dyn_free(&cwbuf->b); + free(cwbuf); + } +} + +struct cw_out_ctx { + struct Curl_cwriter super; + struct cw_out_buf *buf; + BIT(paused); + 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); + +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) +{ + struct cw_out_ctx *ctx = writer->ctx; + (void)data; + ctx->buf = NULL; + return CURLE_OK; +} + +static void cw_out_bufs_free(struct cw_out_ctx *ctx) +{ + while(ctx->buf) { + struct cw_out_buf *next = ctx->buf->next; + cw_out_buf_free(ctx->buf); + ctx->buf = next; + } +} + +static size_t cw_out_bufs_len(struct cw_out_ctx *ctx) +{ + struct cw_out_buf *cwbuf = ctx->buf; + size_t len = 0; + while(cwbuf) { + len += Curl_dyn_len(&cwbuf->b); + cwbuf = cwbuf->next; + } + return len; +} + +static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer) +{ + struct cw_out_ctx *ctx = writer->ctx; + + (void)data; + cw_out_bufs_free(ctx); +} + +/** + * Return the current curl_write_callback and user_data for the buf type + */ +static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype, + curl_write_callback *pwcb, void **pwcb_data, + size_t *pmax_write, size_t *pmin_write) +{ + switch(otype) { + case CW_OUT_BODY: + *pwcb = data->set.fwrite_func; + *pwcb_data = data->set.out; + *pmax_write = CURL_MAX_WRITE_SIZE; + /* if we ever want buffering of BODY output, we can set `min_write` + * the preferred size. The default should always be to pass data + * to the client as it comes without delay */ + *pmin_write = 0; + break; + case CW_OUT_HDS: + *pwcb = data->set.fwrite_header? data->set.fwrite_header : + (data->set.writeheader? data->set.fwrite_func : NULL); + *pwcb_data = data->set.writeheader; + *pmax_write = 0; /* do not chunk-write headers, write them as they are */ + *pmin_write = 0; + break; + default: + *pwcb = NULL; + *pwcb_data = NULL; + *pmax_write = CURL_MAX_WRITE_SIZE; + *pmin_write = 0; + } +} + +static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, + struct Curl_easy *data, + cw_out_type otype, + bool flush_all, + const char *buf, size_t blen, + size_t *pconsumed) +{ + curl_write_callback wcb; + void *wcb_data; + size_t max_write, min_write; + size_t wlen, nwritten; + + /* If we errored once, we do not invoke the client callback again */ + if(ctx->errored) + return CURLE_WRITE_ERROR; + + /* write callbacks may get NULLed by the client between calls. */ + cw_get_writefunc(data, otype, &wcb, &wcb_data, &max_write, &min_write); + if(!wcb) { + *pconsumed = blen; + return CURLE_OK; + } + + *pconsumed = 0; + while(blen && !ctx->paused) { + if(!flush_all && blen < min_write) + break; + wlen = max_write? CURLMIN(blen, max_write) : blen; + Curl_set_in_callback(data, TRUE); + nwritten = wcb((char *)buf, 1, wlen, wcb_data); + Curl_set_in_callback(data, FALSE); + CURL_TRC_WRITE(data, "cw_out, wrote %zu %s bytes -> %zu", + wlen, (otype == CW_OUT_BODY)? "body" : "header", + nwritten); + if(CURL_WRITEFUNC_PAUSE == nwritten) { + if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) { + /* Protocols that work without network cannot be paused. This is + actually only FILE:// just now, and it can't pause since the + transfer isn't done using the "normal" procedure. */ + failf(data, "Write callback asked for PAUSE when not supported"); + return CURLE_WRITE_ERROR; + } + /* mark the connection as RECV paused */ + data->req.keepon |= KEEP_RECV_PAUSE; + ctx->paused = TRUE; + CURL_TRC_WRITE(data, "cw_out, PAUSE requested by client"); + break; + } + else if(CURL_WRITEFUNC_ERROR == nwritten) { + failf(data, "client returned ERROR on write of %zu bytes", wlen); + return CURLE_WRITE_ERROR; + } + else if(nwritten != wlen) { + failf(data, "Failure writing output to destination, " + "passed %zu returned %zd", wlen, nwritten); + return CURLE_WRITE_ERROR; + } + *pconsumed += nwritten; + blen -= nwritten; + buf += nwritten; + } + return CURLE_OK; +} + +static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx, + struct Curl_easy *data, + struct cw_out_buf *cwbuf, + bool flush_all) +{ + CURLcode result = CURLE_OK; + + if(Curl_dyn_len(&cwbuf->b)) { + size_t consumed; + + result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all, + Curl_dyn_ptr(&cwbuf->b), + Curl_dyn_len(&cwbuf->b), + &consumed); + if(result) + return result; + + if(consumed) { + if(consumed == Curl_dyn_len(&cwbuf->b)) { + Curl_dyn_free(&cwbuf->b); + } + else { + DEBUGASSERT(consumed < Curl_dyn_len(&cwbuf->b)); + result = Curl_dyn_tail(&cwbuf->b, Curl_dyn_len(&cwbuf->b) - consumed); + if(result) + return result; + } + } + } + return result; +} + +static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx, + struct Curl_easy *data, + struct cw_out_buf **pcwbuf, + bool flush_all) +{ + struct cw_out_buf *cwbuf = *pcwbuf; + CURLcode result; + + if(!cwbuf) + return CURLE_OK; + if(ctx->paused) + return CURLE_OK; + + /* write the end of the chain until it blocks or gets empty */ + while(cwbuf->next) { + struct cw_out_buf **plast = &cwbuf->next; + while((*plast)->next) + plast = &(*plast)->next; + result = cw_out_flush_chain(ctx, data, plast, flush_all); + if(result) + return result; + if(*plast) { + /* could not write last, paused again? */ + DEBUGASSERT(ctx->paused); + return CURLE_OK; + } + } + + result = cw_out_buf_flush(ctx, data, cwbuf, flush_all); + if(result) + return result; + if(!Curl_dyn_len(&cwbuf->b)) { + cw_out_buf_free(cwbuf); + *pcwbuf = NULL; + } + return CURLE_OK; +} + +static CURLcode cw_out_append(struct cw_out_ctx *ctx, + cw_out_type otype, + const char *buf, size_t blen) +{ + if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER) + return CURLE_TOO_LARGE; + + /* 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 */ + if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) { + struct cw_out_buf *cwbuf = cw_out_buf_create(otype); + if(!cwbuf) + return CURLE_OUT_OF_MEMORY; + cwbuf->next = ctx->buf; + ctx->buf = cwbuf; + } + DEBUGASSERT(ctx->buf && (ctx->buf->type == otype)); + return Curl_dyn_addn(&ctx->buf->b, buf, blen); +} + +static CURLcode cw_out_do_write(struct cw_out_ctx *ctx, + struct Curl_easy *data, + cw_out_type otype, + bool flush_all, + const char *buf, size_t blen) +{ + CURLcode result = CURLE_OK; + + /* if we have buffered data and it is a different type than what + * we are writing now, try to flush all */ + if(ctx->buf && ctx->buf->type != otype) { + result = cw_out_flush_chain(ctx, data, &ctx->buf, TRUE); + if(result) + goto out; + } + + if(ctx->buf) { + /* still have buffered data, append and flush */ + result = cw_out_append(ctx, otype, buf, blen); + if(result) + return result; + result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); + if(result) + goto out; + } + else { + /* nothing buffered, try direct write */ + size_t consumed; + result = cw_out_ptr_flush(ctx, data, otype, flush_all, + buf, blen, &consumed); + if(result) + return result; + if(consumed < blen) { + /* did not write all, append the rest */ + result = cw_out_append(ctx, otype, buf + consumed, blen - consumed); + if(result) + goto out; + } + } + +out: + if(result) { + /* We do not want to invoked client callbacks a second time after + * encountering an error. See issue #13337 */ + ctx->errored = TRUE; + cw_out_bufs_free(ctx); + } + return result; +} + +static CURLcode cw_out_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + struct cw_out_ctx *ctx = writer->ctx; + CURLcode result; + bool flush_all; + + flush_all = (type & CLIENTWRITE_EOS)? TRUE:FALSE; + if((type & CLIENTWRITE_BODY) || + ((type & CLIENTWRITE_HEADER) && data->set.include_header)) { + result = cw_out_do_write(ctx, data, CW_OUT_BODY, flush_all, buf, blen); + if(result) + return result; + } + + if(type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) { + result = cw_out_do_write(ctx, data, CW_OUT_HDS, flush_all, buf, blen); + if(result) + return result; + } + + return CURLE_OK; +} + +bool Curl_cw_out_is_paused(struct Curl_easy *data) +{ + struct Curl_cwriter *cw_out; + struct cw_out_ctx *ctx; + + cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); + if(!cw_out) + return FALSE; + + ctx = (struct cw_out_ctx *)cw_out; + CURL_TRC_WRITE(data, "cw-out is%spaused", ctx->paused? "" : " not"); + return ctx->paused; +} + +static CURLcode cw_out_flush(struct Curl_easy *data, + bool unpause, bool flush_all) +{ + struct Curl_cwriter *cw_out; + CURLcode result = CURLE_OK; + + cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out); + if(cw_out) { + struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out; + if(ctx->errored) + return CURLE_WRITE_ERROR; + if(unpause && ctx->paused) + ctx->paused = FALSE; + if(ctx->paused) + return CURLE_OK; /* not doing it */ + + result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); + if(result) { + ctx->errored = TRUE; + cw_out_bufs_free(ctx); + return result; + } + } + return result; +} + +CURLcode Curl_cw_out_unpause(struct Curl_easy *data) +{ + CURL_TRC_WRITE(data, "cw-out unpause"); + return cw_out_flush(data, TRUE, FALSE); +} + +CURLcode Curl_cw_out_done(struct Curl_easy *data) +{ + CURL_TRC_WRITE(data, "cw-out done"); + return cw_out_flush(data, FALSE, TRUE); +}
diff --git a/Utilities/cmcurl/lib/cw-out.h b/Utilities/cmcurl/lib/cw-out.h new file mode 100644 index 0000000..ca4c2e4 --- /dev/null +++ b/Utilities/cmcurl/lib/cw-out.h
@@ -0,0 +1,53 @@ +#ifndef HEADER_CURL_CW_OUT_H +#define HEADER_CURL_CW_OUT_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 "sendf.h" + +/** + * The client writer type "cw-out" that does the actual writing to + * the client callbacks. Intended to be the last installed in the + * client writer stack of a transfer. + */ +extern struct Curl_cwtype Curl_cwt_out; + +/** + * Return TRUE iff 'cw-out' client write has paused data. + */ +bool Curl_cw_out_is_paused(struct Curl_easy *data); + +/** + * Flush any buffered date to the client, chunk collation still applies. + */ +CURLcode Curl_cw_out_unpause(struct Curl_easy *data); + +/** + * Mark EndOfStream reached and flush ALL data to the client. + */ +CURLcode Curl_cw_out_done(struct Curl_easy *data); + +#endif /* HEADER_CURL_CW_OUT_H */
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c index 3239848..a26a28b 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c
@@ -76,7 +76,7 @@ */ const struct Curl_handler Curl_handler_dict = { - "DICT", /* scheme */ + "dict", /* scheme */ ZERO_NULL, /* setup_connection */ dict_do, /* do_it */ ZERO_NULL, /* done */ @@ -90,6 +90,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_DICT, /* defport */ @@ -122,13 +123,12 @@ } /* sendf() sends formatted data to the server */ -static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data, - const char *fmt, ...) CURL_PRINTF(3, 4); +static CURLcode sendf(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); -static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data, - const char *fmt, ...) +static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) { - ssize_t bytes_written; + size_t bytes_written; size_t write_len; CURLcode result = CURLE_OK; char *s; @@ -146,7 +146,7 @@ for(;;) { /* Write the buffer to the socket */ - result = Curl_write(data, sockfd, sptr, write_len, &bytes_written); + result = Curl_xfer_send(data, sptr, write_len, &bytes_written); if(result) break; @@ -178,8 +178,6 @@ char *nthdef = NULL; /* This is not part of the protocol, but required by RFC 2229 */ CURLcode result; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; char *path; @@ -228,7 +226,7 @@ goto error; } - result = sendf(sockfd, data, + result = sendf(data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "MATCH " "%s " /* database */ @@ -243,7 +241,7 @@ failf(data, "Failed sending DICT request"); goto error; } - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */ + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */ } else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || @@ -276,7 +274,7 @@ goto error; } - result = sendf(sockfd, data, + result = sendf(data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "DEFINE " "%s " /* database */ @@ -289,7 +287,7 @@ failf(data, "Failed sending DICT request"); goto error; } - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); } else { @@ -302,7 +300,7 @@ if(ppath[i] == ':') ppath[i] = ' '; } - result = sendf(sockfd, data, + result = sendf(data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" "%s\r\n" "QUIT\r\n", ppath); @@ -311,7 +309,7 @@ goto error; } - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); } }
diff --git a/Utilities/cmcurl/lib/dllmain.c b/Utilities/cmcurl/lib/dllmain.c new file mode 100644 index 0000000..41e97b3 --- /dev/null +++ b/Utilities/cmcurl/lib/dllmain.c
@@ -0,0 +1,81 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_OPENSSL +#include <openssl/crypto.h> +#endif + +/* The fourth-to-last include */ +#ifdef __CYGWIN__ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#ifdef _WIN32 +#undef _WIN32 +#endif +#endif + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* DllMain() must only be defined for Windows and Cygwin DLL builds. */ +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(CURL_STATICLIB) + +#if defined(USE_OPENSSL) && \ + !defined(OPENSSL_IS_AWSLC) && \ + !defined(OPENSSL_IS_BORINGSSL) && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + (OPENSSL_VERSION_NUMBER >= 0x10100000L) +#define PREVENT_OPENSSL_MEMLEAK +#endif + +#ifdef PREVENT_OPENSSL_MEMLEAK +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + (void)hinstDLL; + (void)lpvReserved; + + switch(fdwReason) { + case DLL_PROCESS_ATTACH: + break; + case DLL_PROCESS_DETACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + /* Call OPENSSL_thread_stop to prevent a memory leak in case OpenSSL is + linked statically. + https://github.com/curl/curl/issues/12327#issuecomment-1826405944 */ + OPENSSL_thread_stop(); + break; + } + return TRUE; +} +#endif /* OpenSSL */ + +#endif /* DLL build */
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index ef32d50..03317ae 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c
@@ -42,9 +42,13 @@ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" +#include "escape.h" #define DNS_CLASS_IN 0x01 +/* local_print_buf truncates if the hex string will be more than this */ +#define LOCAL_PB_HEXMAX 400 + #ifndef CURL_DISABLE_VERBOSE_STRINGS static const char * const errors[]={ "", @@ -69,7 +73,12 @@ return errors[code]; return "bad error code"; } -#endif + +struct curl_trc_feat Curl_doh_trc = { + "DoH", + CURL_LOG_LVL_NONE, +}; +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ /* @unittest 1655 */ @@ -182,6 +191,26 @@ return realsize; } +#if defined(USE_HTTPSRR) && defined(CURLDEBUG) +static void local_print_buf(struct Curl_easy *data, + const char *prefix, + unsigned char *buf, size_t len) +{ + unsigned char hexstr[LOCAL_PB_HEXMAX]; + size_t hlen = LOCAL_PB_HEXMAX; + bool truncated = false; + + if(len > (LOCAL_PB_HEXMAX / 2)) + truncated = true; + Curl_hexencode(buf, len, hexstr, hlen); + if(!truncated) + 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.c when this DoH transfer is complete */ static int doh_done(struct Curl_easy *doh, CURLcode result) { @@ -189,9 +218,9 @@ struct dohdata *dohp = data->req.doh; /* so one of the DoH request done for the 'data' transfer is now complete! */ dohp->pending--; - infof(data, "a DoH request is completed, %u to go", dohp->pending); + infof(doh, "a DoH request is completed, %u to go", dohp->pending); if(result) - infof(data, "DoH request %s", curl_easy_strerror(result)); + infof(doh, "DoH request %s", curl_easy_strerror(result)); if(!dohp->pending) { /* DoH completed */ @@ -242,6 +271,9 @@ the gcc typecheck helpers */ struct dynbuf *resp = &p->serverdoh; doh->state.internal = true; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + doh->state.feat = &Curl_doh_trc; +#endif ERROR_CHECK_SETOPT(CURLOPT_URL, url); ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); @@ -264,7 +296,7 @@ ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); if(data->set.err && data->set.err != stderr) ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); - if(data->set.verbose) + if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); if(data->set.no_signal) ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); @@ -371,6 +403,12 @@ int slot; struct dohdata *dohp; struct connectdata *conn = data->conn; +#ifdef USE_HTTPSRR + /* for now, this is only used when ECH is enabled */ +# ifdef USE_ECH + char *qname = NULL; +# endif +#endif *waitp = FALSE; (void)hostname; (void)port; @@ -400,7 +438,7 @@ goto error; dohp->pending++; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* create IPv6 DoH request */ result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6], @@ -411,6 +449,37 @@ dohp->pending++; } #endif + +#ifdef USE_HTTPSRR + /* + * TODO: Figure out the conditions under which we want to make + * a request for an HTTPS RR when we are not doing ECH. For now, + * making this request breaks a bunch of DoH tests, e.g. test2100, + * where the additional request doesn't match the pre-cooked data + * files, so there's a bit of work attached to making the request + * in a non-ECH use-case. For the present, we'll only make the + * request when ECH is enabled in the build and is being used for + * the curl operation. + */ +# ifdef USE_ECH + if(data->set.tls_ech & CURLECH_ENABLE + || data->set.tls_ech & CURLECH_HARD) { + if(port == 443) + qname = strdup(hostname); + else + qname = aprintf("_%d._https.%s", port, hostname); + if(!qname) + goto error; + result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS], + DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH], + data->multi, dohp->headers); + free(qname); + if(result) + goto error; + dohp->pending++; + } +# endif +#endif *waitp = TRUE; /* this never returns synchronously */ return NULL; @@ -493,6 +562,25 @@ return DOH_OK; } +#ifdef USE_HTTPSRR +static DOHcode store_https(const unsigned char *doh, + int index, + struct dohentry *d, + uint16_t len) +{ + /* silently ignore RRs over the limit */ + if(d->numhttps_rrs < DOH_MAX_HTTPS) { + struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs]; + h->val = Curl_memdup(&doh[index], len); + if(!h->val) + return DOH_OUT_OF_MEM; + h->len = len; + d->numhttps_rrs++; + } + return DOH_OK; +} +#endif + static DOHcode store_cname(const unsigned char *doh, size_t dohlen, unsigned int index, @@ -555,7 +643,8 @@ /* RDATA - A (TYPE 1): 4 bytes - AAAA (TYPE 28): 16 bytes - - NS (TYPE 2): N bytes */ + - NS (TYPE 2): N bytes + - HTTPS (TYPE 65): N bytes */ DOHcode rc; switch(type) { @@ -573,6 +662,13 @@ if(rc) return rc; break; +#ifdef USE_HTTPSRR + case DNS_TYPE_HTTPS: + rc = store_https(doh, index, d, rdlength); + if(rc) + return rc; + break; +#endif case DNS_TYPE_CNAME: rc = store_cname(doh, dohlen, index, d); if(rc) @@ -729,7 +825,11 @@ if(index != dohlen) return DOH_DNS_MALFORMAT; /* something is wrong */ +#ifdef USE_HTTTPS + if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs) +#else if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr) +#endif /* nothing stored! */ return DOH_NO_CONTENT; @@ -741,11 +841,11 @@ const struct dohentry *d) { int i; - infof(data, "TTL: %u seconds", d->ttl); + infof(data, "[DoH] TTL: %u seconds", d->ttl); for(i = 0; i < d->numaddr; i++) { const struct dohaddr *a = &d->addr[i]; if(a->type == DNS_TYPE_A) { - infof(data, "DoH A: %u.%u.%u.%u", + infof(data, "[DoH] A: %u.%u.%u.%u", a->ip.v4[0], a->ip.v4[1], a->ip.v4[2], a->ip.v4[3]); } @@ -754,9 +854,9 @@ char buffer[128]; char *ptr; size_t len; - msnprintf(buffer, 128, "DoH AAAA: "); - ptr = &buffer[10]; - len = 118; + len = msnprintf(buffer, 128, "[DoH] AAAA: "); + ptr = &buffer[len]; + len = sizeof(buffer) - len; for(j = 0; j < 16; j += 2) { size_t l; msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j], @@ -768,6 +868,16 @@ infof(data, "%s", buffer); } } +#ifdef USE_HTTPSRR + for(i = 0; i < d->numhttps_rrs; i++) { +# ifdef CURLDEBUG + local_print_buf(data, "DoH HTTPS", + d->https_rrs[i].val, d->https_rrs[i].len); +# else + infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); +# endif + } +#endif for(i = 0; i < d->numcname; i++) { infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i])); } @@ -796,7 +906,7 @@ struct Curl_addrinfo *prevai = NULL; struct Curl_addrinfo *firstai = NULL; struct sockaddr_in *addr; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif CURLcode result = CURLE_OK; @@ -812,7 +922,7 @@ size_t ss_size; CURL_SA_FAMILY_T addrtype; if(de->addr[i].type == DNS_TYPE_AAAA) { -#ifndef ENABLE_IPV6 +#ifndef USE_IPV6 /* we can't handle IPv6 addresses */ continue; #else @@ -861,7 +971,7 @@ addr->sin_port = htons((unsigned short)port); break; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: addr6 = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); @@ -887,7 +997,18 @@ #ifndef CURL_DISABLE_VERBOSE_STRINGS static const char *type2name(DNStype dnstype) { - return (dnstype == DNS_TYPE_A)?"A":"AAAA"; + switch(dnstype) { + case DNS_TYPE_A: + return "A"; + case DNS_TYPE_AAAA: + return "AAAA"; +#ifdef USE_HTTPSRR + case DNS_TYPE_HTTPS: + return "HTTPS"; +#endif + default: + return "unknown"; + } } #endif @@ -897,8 +1018,270 @@ for(i = 0; i < d->numcname; i++) { Curl_dyn_free(&d->cname[i]); } +#ifdef USE_HTTPSRR + for(i = 0; i < d->numhttps_rrs; i++) + free(d->https_rrs[i].val); +#endif } +#ifdef USE_HTTPSRR + +/* + * @brief decode the DNS name in a binary RRData + * @param buf points to the buffer (in/out) + * @param remaining points to the remaining buffer length (in/out) + * @param dnsname returns the string form name on success + * @return is 1 for success, error otherwise + * + * The encoding here is defined in + * https://tools.ietf.org/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's why it's an "unsigned char **" :-) + */ +static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining, + char **dnsname) +{ + unsigned char *cp = NULL; + int rem = 0; + unsigned char clen = 0; /* chunk len */ + struct dynbuf thename; + + DEBUGASSERT(buf && remaining && dnsname); + if(!buf || !remaining || !dnsname) + return CURLE_OUT_OF_MEMORY; + rem = (int)*remaining; + if(rem <= 0) { + Curl_dyn_free(&thename); + return CURLE_OUT_OF_MEMORY; + } + Curl_dyn_init(&thename, CURL_MAXLEN_host_name); + cp = *buf; + clen = *cp++; + if(clen == 0) { + /* special case - return "." as name */ + if(Curl_dyn_addn(&thename, ".", 1)) + return CURLE_OUT_OF_MEMORY; + } + while(clen) { + if(clen >= rem) { + Curl_dyn_free(&thename); + return CURLE_OUT_OF_MEMORY; + } + if(Curl_dyn_addn(&thename, cp, clen) || + Curl_dyn_addn(&thename, ".", 1)) + return CURLE_TOO_LARGE; + + cp += clen; + rem -= (clen + 1); + if(rem <= 0) { + Curl_dyn_free(&thename); + return CURLE_OUT_OF_MEMORY; + } + clen = *cp++; + } + *buf = cp; + *remaining = rem - 1; + *dnsname = Curl_dyn_ptr(&thename); + return CURLE_OK; +} + +static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len, + char **alpns) +{ + /* + * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1 + * encoding is catenated list of strings each preceded by a one + * octet length + * output is comma-sep list of the strings + * implementations may or may not handle quoting of comma within + * string values, so we might see a comma within the wire format + * version of a string, in which case we'll precede that by a + * backslash - same goes for a backslash character, and of course + * we need to use two backslashes in strings when we mean one;-) + */ + int remaining = (int) len; + char *oval; + size_t i; + unsigned char *cp = rrval; + struct dynbuf dval; + + if(!alpns) + return CURLE_OUT_OF_MEMORY; + Curl_dyn_init(&dval, DYN_DOH_RESPONSE); + remaining = (int)len; + cp = rrval; + while(remaining > 0) { + size_t tlen = (size_t) *cp++; + + /* if not 1st time, add comma */ + if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1)) + goto err; + remaining--; + if(tlen > (size_t)remaining) + goto err; + /* add escape char if needed, clunky but easier to read */ + for(i = 0; i != tlen; i++) { + if('\\' == *cp || ',' == *cp) { + if(Curl_dyn_addn(&dval, "\\", 1)) + goto err; + } + if(Curl_dyn_addn(&dval, cp++, 1)) + goto err; + } + remaining -= (int)tlen; + } + /* this string is always null terminated */ + oval = Curl_dyn_ptr(&dval); + if(!oval) + goto err; + *alpns = oval; + return CURLE_OK; +err: + Curl_dyn_free(&dval); + return CURLE_BAD_CONTENT_ENCODING; +} + +#ifdef CURLDEBUG +static CURLcode test_alpn_escapes(void) +{ + /* we'll use an example from draft-ietf-dnsop-svcb, figure 10 */ + static unsigned char example[] = { + 0x08, /* length 8 */ + 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */ + 0x02, /* length 2 */ + 0x68, 0x32 /* value "h2" */ + }; + size_t example_len = sizeof(example); + char *aval = NULL; + static const char *expected = "f\\\\oo\\,bar,h2"; + + if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK) + return CURLE_BAD_CONTENT_ENCODING; + if(strlen(aval) != strlen(expected)) + return CURLE_BAD_CONTENT_ENCODING; + if(memcmp(aval, expected, strlen(aval))) + return CURLE_BAD_CONTENT_ENCODING; + return CURLE_OK; +} +#endif + +static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, + struct Curl_https_rrinfo **hrr) +{ + size_t remaining = len; + unsigned char *cp = rrval; + uint16_t pcode = 0, plen = 0; + struct Curl_https_rrinfo *lhrr = NULL; + char *dnsname = NULL; + +#ifdef CURLDEBUG + /* a few tests of escaping, shouldn't be here but ok for now */ + if(test_alpn_escapes() != CURLE_OK) + return CURLE_OUT_OF_MEMORY; +#endif + lhrr = calloc(1, sizeof(struct Curl_https_rrinfo)); + if(!lhrr) + return CURLE_OUT_OF_MEMORY; + lhrr->val = Curl_memdup(rrval, len); + if(!lhrr->val) + goto err; + lhrr->len = len; + if(remaining <= 2) + goto err; + lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]); + cp += 2; + remaining -= (uint16_t)2; + if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK) + goto err; + lhrr->target = dnsname; + while(remaining >= 4) { + pcode = (uint16_t)((*cp << 8) + (*(cp + 1))); + cp += 2; + plen = (uint16_t)((*cp << 8) + (*(cp + 1))); + cp += 2; + remaining -= 4; + if(pcode == HTTPS_RR_CODE_ALPN) { + if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK) + goto err; + } + if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN) + lhrr->no_def_alpn = TRUE; + else if(pcode == HTTPS_RR_CODE_IPV4) { + lhrr->ipv4hints = Curl_memdup(cp, plen); + if(!lhrr->ipv4hints) + goto err; + lhrr->ipv4hints_len = (size_t)plen; + } + else if(pcode == HTTPS_RR_CODE_ECH) { + lhrr->echconfiglist = Curl_memdup(cp, plen); + if(!lhrr->echconfiglist) + goto err; + lhrr->echconfiglist_len = (size_t)plen; + } + else if(pcode == HTTPS_RR_CODE_IPV6) { + lhrr->ipv6hints = Curl_memdup(cp, plen); + if(!lhrr->ipv6hints) + goto err; + lhrr->ipv6hints_len = (size_t)plen; + } + if(plen > 0 && plen <= remaining) { + cp += plen; + remaining -= plen; + } + } + DEBUGASSERT(!remaining); + *hrr = lhrr; + return CURLE_OK; +err: + if(lhrr) { + free(lhrr->target); + free(lhrr->echconfiglist); + free(lhrr->val); + free(lhrr); + } + return CURLE_OUT_OF_MEMORY; +} + +# ifdef CURLDEBUG +static void local_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); + if(hrr->alpns) + infof(data, "HTTPS RR: alpns %s", hrr->alpns); + else + infof(data, "HTTPS RR: no alpns"); + if(hrr->no_def_alpn) + infof(data, "HTTPS RR: no_def_alpn set"); + else + infof(data, "HTTPS RR: no_def_alpn not set"); + if(hrr->ipv4hints) { + local_print_buf(data, "HTTPS RR: ipv4hints", + hrr->ipv4hints, hrr->ipv4hints_len); + } + else + infof(data, "HTTPS RR: no ipv4hints"); + if(hrr->echconfiglist) { + local_print_buf(data, "HTTPS RR: ECHConfigList", + hrr->echconfiglist, hrr->echconfiglist_len); + } + else + infof(data, "HTTPS RR: no ECHConfigList"); + if(hrr->ipv6hints) { + local_print_buf(data, "HTTPS RR: ipv6hint", + hrr->ipv6hints, hrr->ipv6hints_len); + } + else + infof(data, "HTTPS RR: no ipv6hints"); + return; +} +# endif +#endif + CURLcode Curl_doh_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dnsp) { @@ -915,9 +1298,15 @@ CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { +#ifndef USE_HTTPSRR DOHcode rc[DOH_PROBE_SLOTS] = { DOH_OK, DOH_OK }; +#else + DOHcode rc[DOH_PROBE_SLOTS] = { + DOH_OK, DOH_OK, DOH_OK + }; +#endif struct dohentry de; int slot; /* remove DoH handles from multi handle and close them */ @@ -950,8 +1339,11 @@ struct Curl_dns_entry *dns; struct Curl_addrinfo *ai; - infof(data, "DoH Host name: %s", dohp->host); - showdoh(data, &de); + + if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) { + infof(data, "[DoH] Host name: %s", dohp->host); + showdoh(data, &de); + } result = doh2ai(&de, dohp->host, dohp->port, &ai); if(result) { @@ -980,6 +1372,22 @@ } /* address processing done */ /* Now process any build-specific attributes retrieved from DNS */ +#ifdef USE_HTTPSRR + if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) { + struct Curl_https_rrinfo *hrr = NULL; + result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len, + &hrr); + if(result) { + infof(data, "Failed to decode HTTPS RR"); + return result; + } + infof(data, "Some HTTPS RR to process"); +# ifdef CURLDEBUG + local_print_httpsrr(data, hrr); +# endif + (*dnsp)->hinfo = hrr; + } +#endif /* All done */ de_cleanup(&de);
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h index 7d7b694..bb881ec 100644 --- a/Utilities/cmcurl/lib/doh.h +++ b/Utilities/cmcurl/lib/doh.h
@@ -26,6 +26,9 @@ #include "urldata.h" #include "curl_addrinfo.h" +#ifdef USE_HTTPSRR +# include <stdint.h> +#endif #ifndef CURL_DISABLE_DOH @@ -51,7 +54,8 @@ DNS_TYPE_NS = 2, DNS_TYPE_CNAME = 5, DNS_TYPE_AAAA = 28, - DNS_TYPE_DNAME = 39 /* RFC6672 */ + DNS_TYPE_DNAME = 39, /* RFC6672 */ + DNS_TYPE_HTTPS = 65 } DNStype; /* one of these for each DoH request */ @@ -84,10 +88,9 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dns); -int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks); - #define DOH_MAX_ADDR 24 #define DOH_MAX_CNAME 4 +#define DOH_MAX_HTTPS 4 struct dohaddr { int type; @@ -97,12 +100,44 @@ } ip; }; +#ifdef USE_HTTPSRR + +/* + * These are the code points for DNS wire format SvcParams as + * per draft-ietf-dnsop-svcb-https + * Not all are supported now, and even those that are may need + * more work in future to fully support the spec. + */ +#define HTTPS_RR_CODE_ALPN 0x01 +#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02 +#define HTTPS_RR_CODE_PORT 0x03 +#define HTTPS_RR_CODE_IPV4 0x04 +#define HTTPS_RR_CODE_ECH 0x05 +#define HTTPS_RR_CODE_IPV6 0x06 + +/* + * These may need escaping when found within an alpn string + * value. + */ +#define COMMA_CHAR ',' +#define BACKSLASH_CHAR '\\' + +struct dohhttps_rr { + uint16_t len; /* raw encoded length */ + unsigned char *val; /* raw encoded octets */ +}; +#endif + struct dohentry { struct dynbuf cname[DOH_MAX_CNAME]; struct dohaddr addr[DOH_MAX_ADDR]; int numaddr; unsigned int ttl; int numcname; +#ifdef USE_HTTPSRR + struct dohhttps_rr https_rrs[DOH_MAX_HTTPS]; + int numhttps_rrs; +#endif }; @@ -120,6 +155,8 @@ void de_cleanup(struct dohentry *d); #endif +extern struct curl_trc_feat Curl_doh_trc; + #else /* if DoH is disabled */ #define Curl_doh(a,b,c,d) NULL #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c index a4c599d..3b62eaf 100644 --- a/Utilities/cmcurl/lib/dynbuf.c +++ b/Utilities/cmcurl/lib/dynbuf.c
@@ -213,7 +213,7 @@ } /* If we failed, we cleanup the whole buffer and return error */ Curl_dyn_free(s); - return CURLE_OK; + return CURLE_OUT_OF_MEMORY; #endif }
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index 067b6d7..a04dbed 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c
@@ -728,6 +728,8 @@ /* clear this as early as possible */ data->set.errorbuffer[0] = 0; + data->state.os_errno = 0; + if(data->multi) { failf(data, "easy handle already used in multi handle"); return CURLE_FAILED_INIT; @@ -741,7 +743,6 @@ multi = Curl_multi_handle(1, 3, 7); if(!multi) return CURLE_OUT_OF_MEMORY; - data->multi_easy = multi; } if(multi->in_callback) @@ -750,15 +751,18 @@ /* Copy the MAXCONNECTS option to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); + data->multi_easy = NULL; /* pretend it does not exist */ mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); - data->multi_easy = NULL; if(mcode == CURLM_OUT_OF_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_FAILED_INIT; } + /* assign this after curl_multi_add_handle() */ + data->multi_easy = multi; + sigpipe_ignore(data, &pipe_st); /* run the transfer */ @@ -1021,7 +1025,6 @@ #ifndef CURL_DISABLE_COOKIES free(outcurl->cookies); #endif - free(outcurl->state.buffer); Curl_dyn_free(&outcurl->state.headerb); Curl_altsvc_cleanup(&outcurl->asi); Curl_hsts_cleanup(&outcurl->hsts); @@ -1038,7 +1041,7 @@ */ void curl_easy_reset(struct Curl_easy *data) { - Curl_free_request_state(data); + Curl_req_hard_reset(&data->req, data); /* zero out UserDefined data: */ Curl_freeset(data); @@ -1084,6 +1087,7 @@ int oldstate; int newstate; bool recursive = FALSE; + bool keep_changed, unpause_read, not_all_paused; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, don't continue */ @@ -1099,60 +1103,47 @@ ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); - if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) { - /* Not changing any pause state, return */ - DEBUGF(infof(data, "pause: no change, early return")); - return CURLE_OK; - } + keep_changed = ((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) != oldstate); + not_all_paused = (newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != + (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE); + unpause_read = ((k->keepon & ~newstate & KEEP_SEND_PAUSE) && + (data->mstate == MSTATE_PERFORMING || + data->mstate == MSTATE_RATELIMITING)); + /* Unpausing writes is detected on the next run in + * transfer.c:Curl_readwrite(). This is because this may result + * in a transfer error if the application's callbacks fail */ - /* Unpause parts in active mime tree. */ - if((k->keepon & ~newstate & KEEP_SEND_PAUSE) && - (data->mstate == MSTATE_PERFORMING || - data->mstate == MSTATE_RATELIMITING) && - data->state.fread_func == (curl_read_callback) Curl_mime_read) { - Curl_mime_unpause(data->state.in); - } - - /* put it back in the keepon */ + /* Set the new keepon state, so it takes effect no matter what error + * may happen afterwards. */ k->keepon = newstate; - if(!(newstate & KEEP_RECV_PAUSE)) { - Curl_conn_ev_data_pause(data, FALSE); - result = Curl_client_unpause(data); - if(result) - return result; - } - -#ifdef USE_HYPER - if(!(newstate & KEEP_SEND_PAUSE)) { - /* need to wake the send body waker */ - if(data->hyp.send_body_waker) { - hyper_waker_wake(data->hyp.send_body_waker); - data->hyp.send_body_waker = NULL; - } - } -#endif - - /* if there's no error and we're not pausing both directions, we want - to have this handle checked soon */ - if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != - (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) { - Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */ - + /* If not completely pausing both directions now, run again in any case. */ + if(not_all_paused) { + Curl_expire(data, 0, EXPIRE_RUN_NOW); /* reset the too-slow time keeper */ data->state.keeps_speed.tv_sec = 0; - - if(!data->state.tempcount) - /* if not pausing again, force a recv/send check of this connection as - the data might've been read off the socket already */ - data->state.select_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT; - if(data->multi) { - if(Curl_update_timer(data->multi)) - return CURLE_ABORTED_BY_CALLBACK; + /* Simulate socket events on next run for unpaused directions */ + if(!(newstate & KEEP_SEND_PAUSE)) + data->state.select_bits |= CURL_CSELECT_OUT; + if(!(newstate & KEEP_RECV_PAUSE)) + data->state.select_bits |= CURL_CSELECT_IN; + /* On changes, tell application to update its timers. */ + if(keep_changed && data->multi) { + if(Curl_update_timer(data->multi)) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } } } - if(!data->state.done) + if(unpause_read) { + result = Curl_creader_unpause(data); + if(result) + goto out; + } + +out: + if(!result && !data->state.done && keep_changed) /* This transfer may have been moved in or out of the bundle, update the corresponding socket callback, if used */ result = Curl_updatesocket(data); @@ -1166,9 +1157,11 @@ } -static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd, +static CURLcode easy_connection(struct Curl_easy *data, struct connectdata **connp) { + curl_socket_t sfd; + if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1178,9 +1171,9 @@ return CURLE_UNSUPPORTED_PROTOCOL; } - *sfd = Curl_getconnectinfo(data, connp); + sfd = Curl_getconnectinfo(data, connp); - if(*sfd == CURL_SOCKET_BAD) { + if(sfd == CURL_SOCKET_BAD) { failf(data, "Failed to get recent socket"); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -1196,7 +1189,6 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, size_t *n) { - curl_socket_t sfd; CURLcode result; ssize_t n1; struct connectdata *c; @@ -1204,7 +1196,7 @@ if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; - result = easy_connection(data, &sfd, &c); + result = easy_connection(data, &c); if(result) return result; @@ -1214,7 +1206,7 @@ Curl_attach_connection(data, c); *n = 0; - result = Curl_read(data, sfd, buffer, buflen, &n1); + result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1); if(result) return result; @@ -1226,11 +1218,10 @@ #ifdef USE_WEBSOCKETS CURLcode Curl_connect_only_attach(struct Curl_easy *data) { - curl_socket_t sfd; CURLcode result; struct connectdata *c = NULL; - result = easy_connection(data, &sfd, &c); + result = easy_connection(data, &c); if(result) return result; @@ -1249,15 +1240,14 @@ * This is the private internal version of curl_easy_send() */ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, - size_t buflen, ssize_t *n) + size_t buflen, size_t *n) { - curl_socket_t sfd; CURLcode result; - ssize_t n1; struct connectdata *c = NULL; SIGPIPE_VARIABLE(pipe_st); - result = easy_connection(data, &sfd, &c); + *n = 0; + result = easy_connection(data, &c); if(result) return result; @@ -1266,20 +1256,12 @@ needs to be reattached */ Curl_attach_connection(data, c); - *n = 0; sigpipe_ignore(data, &pipe_st); - result = Curl_write(data, sfd, buffer, buflen, &n1); + result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, n); sigpipe_restore(&pipe_st); - if(n1 == -1) + if(result && result != CURLE_AGAIN) return CURLE_SEND_ERROR; - - /* detect EAGAIN */ - if(!result && !n1) - return CURLE_AGAIN; - - *n = n1; - return result; } @@ -1290,13 +1272,13 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, size_t buflen, size_t *n) { - ssize_t written = 0; + size_t written = 0; CURLcode result; if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; result = Curl_senddata(data, buffer, buflen, &written); - *n = (size_t)written; + *n = written; return result; } @@ -1321,7 +1303,7 @@ conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); } else { - /* Do the generic action on the FIRSTSOCKE filter chain */ + /* Do the generic action on the FIRSTSOCKET filter chain */ Curl_conn_keep_alive(data, conn, FIRSTSOCKET); } Curl_detach_connection(data);
diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c index 2b8a521..a0239a8 100644 --- a/Utilities/cmcurl/lib/easygetopt.c +++ b/Utilities/cmcurl/lib/easygetopt.c
@@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * ___|___/|_| ______| * - * Copyright (C) Daniel Stenberg, <daniel.se>, et al. + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h index 6448952..6ce3483 100644 --- a/Utilities/cmcurl/lib/easyif.h +++ b/Utilities/cmcurl/lib/easyif.h
@@ -28,7 +28,7 @@ * Prototypes for library-wide functions provided by easy.c */ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, - size_t buflen, ssize_t *n); + size_t buflen, size_t *n); #ifdef USE_WEBSOCKETS CURLcode Curl_connect_only_attach(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c index da4c611..c79d136 100644 --- a/Utilities/cmcurl/lib/easyoptions.c +++ b/Utilities/cmcurl/lib/easyoptions.c
@@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Daniel Stenberg, <daniel.se>, et al. + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -86,6 +86,7 @@ {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0}, {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0}, + {"ECH", CURLOPT_ECH, CURLOT_STRING, 0}, {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0}, {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS}, {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0}, @@ -375,6 +376,6 @@ */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (324 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (325 + 1)); } #endif
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index b7ce3a8..db86022 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c
@@ -50,6 +50,14 @@ #include <fcntl.h> #endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_DIRENT_H +#include <dirent.h> +#endif + #include "strtoofft.h" #include "urldata.h" #include <curl/curl.h> @@ -59,6 +67,7 @@ #include "file.h" #include "speedcheck.h" #include "getinfo.h" +#include "multiif.h" #include "transfer.h" #include "url.h" #include "parsedate.h" /* for the week day and month names */ @@ -100,7 +109,7 @@ */ const struct Curl_handler Curl_handler_file = { - "FILE", /* scheme */ + "file", /* scheme */ file_setup_connection, /* setup_connection */ file_do, /* do_it */ file_done, /* done */ @@ -114,6 +123,7 @@ ZERO_NULL, /* perform_getsock */ file_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ 0, /* defport */ @@ -290,10 +300,12 @@ int fd; int mode; CURLcode result = CURLE_OK; - char buffer[8*1024], *uphere_save; + char *xfer_ulbuf; + size_t xfer_ulblen; curl_off_t bytecount = 0; struct_stat file_stat; const char *sendbuf; + bool eos = FALSE; /* * Since FILE: doesn't do the full init, we need to provide some extra @@ -337,15 +349,16 @@ data->state.resume_from = (curl_off_t)file_stat.st_size; } - /* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ - * client data to! Please, someone fix... */ - uphere_save = data->req.upload_fromhere; - while(!result) { + result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen); + if(result) + goto out; + + while(!result && !eos) { size_t nread; ssize_t nwrite; size_t readcount; - data->req.upload_fromhere = buffer; - result = Curl_fillreadbuffer(data, sizeof(buffer), &readcount); + + result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &readcount, &eos); if(result) break; @@ -359,16 +372,16 @@ if((curl_off_t)nread <= data->state.resume_from) { data->state.resume_from -= nread; nread = 0; - sendbuf = buffer; + sendbuf = xfer_ulbuf; } else { - sendbuf = buffer + data->state.resume_from; + sendbuf = xfer_ulbuf + data->state.resume_from; nread -= (size_t)data->state.resume_from; data->state.resume_from = 0; } } else - sendbuf = buffer; + sendbuf = xfer_ulbuf; /* write the data to the target */ nwrite = write(fd, sendbuf, nread); @@ -389,8 +402,9 @@ if(!result && Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; +out: close(fd); - data->req.upload_fromhere = uphere_save; + Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); return result; } @@ -419,6 +433,8 @@ bool fstated = FALSE; int fd; struct FILEPROTO *file; + char *xfer_buf; + size_t xfer_blen; *done = TRUE; /* unconditionally */ @@ -439,12 +455,9 @@ fstated = TRUE; } - if(fstated && !data->state.range && data->set.timecondition) { - if(!Curl_meets_timecondition(data, data->info.filetime)) { - *done = TRUE; - return CURLE_OK; - } - } + if(fstated && !data->state.range && data->set.timecondition && + !Curl_meets_timecondition(data, data->info.filetime)) + return CURLE_OK; if(fstated) { time_t filetime; @@ -536,49 +549,90 @@ Curl_pgrsSetDownloadSize(data, expected_size); if(data->state.resume_from) { - if(data->state.resume_from != - lseek(fd, data->state.resume_from, SEEK_SET)) - return CURLE_BAD_DOWNLOAD_RESUME; - } - - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - - while(!result) { - char tmpbuf[8*1024]; - ssize_t nread; - /* Don't fill a whole buffer if we want less than all data */ - size_t bytestoread; - - if(size_known) { - bytestoread = (expected_size < (curl_off_t)(sizeof(tmpbuf)-1)) ? - curlx_sotouz(expected_size) : (sizeof(tmpbuf)-1); + if(!S_ISDIR(statbuf.st_mode)) { + if(data->state.resume_from != + lseek(fd, data->state.resume_from, SEEK_SET)) + return CURLE_BAD_DOWNLOAD_RESUME; } - else - bytestoread = sizeof(tmpbuf)-1; - - nread = read(fd, tmpbuf, bytestoread); - - if(nread > 0) - tmpbuf[nread] = 0; - - if(nread <= 0 || (size_known && (expected_size == 0))) - break; - - if(size_known) - expected_size -= nread; - - result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread); - if(result) - return result; - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, Curl_now()); + else { + return CURLE_BAD_DOWNLOAD_RESUME; + } } + + result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); + if(result) + goto out; + + if(!S_ISDIR(statbuf.st_mode)) { + while(!result) { + ssize_t nread; + /* Don't fill a whole buffer if we want less than all data */ + size_t bytestoread; + + if(size_known) { + bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ? + curlx_sotouz(expected_size) : (xfer_blen-1); + } + else + bytestoread = xfer_blen-1; + + nread = read(fd, xfer_buf, bytestoread); + + if(nread > 0) + xfer_buf[nread] = 0; + + if(nread <= 0 || (size_known && (expected_size == 0))) + break; + + if(size_known) + expected_size -= nread; + + result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread); + if(result) + goto out; + + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, Curl_now()); + if(result) + goto out; + } + } + else { +#ifdef HAVE_OPENDIR + DIR *dir = opendir(file->path); + struct dirent *entry; + + if(!dir) { + result = CURLE_READ_ERROR; + goto out; + } + else { + while((entry = readdir(dir))) { + if(entry->d_name[0] != '.') { + result = Curl_client_write(data, CLIENTWRITE_BODY, + entry->d_name, strlen(entry->d_name)); + if(result) + break; + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); + if(result) + break; + } + } + closedir(dir); + } +#else + failf(data, "Directory listing not yet implemented on this platform."); + result = CURLE_READ_ERROR; +#endif + } + if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; +out: + Curl_multi_xfer_buf_release(data, xfer_buf); return result; }
diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c index 851279f..0bdf2e1 100644 --- a/Utilities/cmcurl/lib/fopen.c +++ b/Utilities/cmcurl/lib/fopen.c
@@ -129,7 +129,12 @@ } result = CURLE_WRITE_ERROR; +#if (defined(ANDROID) || defined(__ANDROID__)) && \ + (defined(__i386__) || defined(__arm__)) + fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, (mode_t)(0600|sb.st_mode)); +#else fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); +#endif if(fd == -1) goto fail;
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 58d6e1d..81ebb78 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c
@@ -85,21 +85,99 @@ #define INET_ADDRSTRLEN 16 #endif +/* 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])) + +/* macro to check for the last line in an FTP server response */ +#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) "" +#define FTP_DSTATE(d) "" +#else /* CURL_DISABLE_VERBOSE_STRINGS */ + /* for tracing purposes */ +static const char * const ftp_state_names[]={ + "STOP", + "WAIT220", + "AUTH", + "USER", + "PASS", + "ACCT", + "PBSZ", + "PROT", + "CCC", + "PWD", + "SYST", + "NAMEFMT", + "QUOTE", + "RETR_PREQUOTE", + "STOR_PREQUOTE", + "POSTQUOTE", + "CWD", + "MKD", + "MDTM", + "TYPE", + "LIST_TYPE", + "RETR_TYPE", + "STOR_TYPE", + "SIZE", + "RETR_SIZE", + "STOR_SIZE", + "REST", + "RETR_REST", + "PORT", + "PRET", + "PASV", + "LIST", + "RETR", + "STOR", + "QUIT" +}; +#define FTP_CSTATE(c) ((c)? ftp_state_names[(c)->proto.ftpc.state] : "???") +#define FTP_DSTATE(d) (((d) && (d)->conn)? \ + ftp_state_names[(d)->conn->proto.ftpc.state] : "???") + +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + +/* This is the ONLY way to change FTP state! */ +static void _ftp_state(struct Curl_easy *data, + ftpstate newstate +#ifdef DEBUGBUILD + , int lineno #endif + ) +{ + struct connectdata *conn = data->conn; + struct ftp_conn *ftpc = &conn->proto.ftpc; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) +#ifdef DEBUGBUILD + (void)lineno; +#endif +#else /* CURL_DISABLE_VERBOSE_STRINGS */ + if(ftpc->state != newstate) +#ifdef DEBUGBUILD + CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_DSTATE(data), + ftp_state_names[newstate], lineno); +#else + CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_DSTATE(data), + ftp_state_names[newstate]); +#endif +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + + ftpc->state = newstate; +} + /* Local API functions */ #ifndef DEBUGBUILD -static void _ftp_state(struct Curl_easy *data, - ftpstate newstate); #define ftp_state(x,y) _ftp_state(x,y) -#else -static void _ftp_state(struct Curl_easy *data, - ftpstate newstate, - int lineno); +#else /* !DEBUGBUILD */ #define ftp_state(x,y) _ftp_state(x,y,__LINE__) -#endif +#endif /* DEBUGBUILD */ static CURLcode ftp_sendquote(struct Curl_easy *data, struct connectdata *conn, @@ -143,7 +221,7 @@ static void wc_data_dtor(void *ptr); static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize); static CURLcode ftp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, + int sockindex, struct pingpong *pp, int *ftpcode, size_t *size); @@ -155,7 +233,7 @@ */ const struct Curl_handler Curl_handler_ftp = { - "FTP", /* scheme */ + "ftp", /* scheme */ ftp_setup_connection, /* setup_connection */ ftp_do, /* do_it */ ftp_done, /* done */ @@ -169,6 +247,7 @@ ZERO_NULL, /* perform_getsock */ ftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_FTP, /* defport */ @@ -186,7 +265,7 @@ */ const struct Curl_handler Curl_handler_ftps = { - "FTPS", /* scheme */ + "ftps", /* scheme */ ftp_setup_connection, /* setup_connection */ ftp_do, /* do_it */ ftp_done, /* done */ @@ -200,6 +279,7 @@ ZERO_NULL, /* perform_getsock */ ftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_FTPS, /* defport */ @@ -213,6 +293,7 @@ static void close_secondarysocket(struct Curl_easy *data, struct connectdata *conn) { + CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data)); Curl_conn_close(data, SECONDARYSOCKET); Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); } @@ -247,6 +328,98 @@ Curl_safefree(ftpc->newhost); } +#ifdef CURL_DO_LINEEND_CONV +/*********************************************************************** + * + * Lineend Conversions + * On ASCII transfers, e.g. directory listings, we might get lines + * ending in '\r\n' and we prefer just '\n'. + * We might also get a lonely '\r' which we convert into a '\n'. + */ +struct ftp_cw_lc_ctx { + struct Curl_cwriter super; + bool newline_pending; +}; + +static CURLcode ftp_cw_lc_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + static const char nl = '\n'; + struct ftp_cw_lc_ctx *ctx = writer->ctx; + + if(!(type & CLIENTWRITE_BODY) || + data->conn->proto.ftpc.transfertype != 'A') + return Curl_cwriter_write(data, writer->next, type, buf, blen); + + /* ASCII mode BODY data, convert lineends */ + while(blen) { + /* do not pass EOS when writing parts */ + int chunk_type = (type & ~CLIENTWRITE_EOS); + const char *cp; + size_t chunk_len; + CURLcode result; + + if(ctx->newline_pending) { + if(buf[0] != '\n') { + /* previous chunk ended in '\r' and we do not see a '\n' in this one, + * need to write a newline. */ + result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1); + if(result) + return result; + } + /* either we just wrote the newline or it is part of the next + * chunk of bytes we write. */ + data->state.crlf_conversions++; + ctx->newline_pending = FALSE; + } + + cp = memchr(buf, '\r', blen); + if(!cp) + break; + + /* write the bytes before the '\r', excluding the '\r' */ + chunk_len = cp - buf; + if(chunk_len) { + result = Curl_cwriter_write(data, writer->next, chunk_type, + buf, chunk_len); + if(result) + return result; + } + /* skip the '\r', we now have a newline pending */ + buf = cp + 1; + blen = blen - chunk_len - 1; + ctx->newline_pending = TRUE; + } + + /* Any remaining data does not contain a '\r' */ + if(blen) { + DEBUGASSERT(!ctx->newline_pending); + return Curl_cwriter_write(data, writer->next, type, buf, blen); + } + else if(type & CLIENTWRITE_EOS) { + /* EndOfStream, if we have a trailing cr, now is the time to write it */ + if(ctx->newline_pending) { + ctx->newline_pending = FALSE; + data->state.crlf_conversions++; + return Curl_cwriter_write(data, writer->next, type, &nl, 1); + } + /* Always pass on the EOS type indicator */ + return Curl_cwriter_write(data, writer->next, type, buf, 0); + } + return CURLE_OK; +} + +static const struct Curl_cwtype ftp_cw_lc = { + "ftp-lineconv", + NULL, + Curl_cwriter_def_init, + ftp_cw_lc_write, + Curl_cwriter_def_close, + sizeof(struct ftp_cw_lc_ctx) +}; + +#endif /* CURL_DO_LINEEND_CONV */ /*********************************************************************** * * AcceptServerConnect() @@ -260,7 +433,7 @@ struct connectdata *conn = data->conn; curl_socket_t sock = conn->sock[SECONDARYSOCKET]; curl_socket_t s = CURL_SOCKET_BAD; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct Curl_sockaddr_storage add; #else struct sockaddr_in add; @@ -286,8 +459,10 @@ (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ /* Replace any filter on SECONDARY with one listening on this socket */ result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s); - if(result) + if(result) { + sclose(s); return result; + } if(data->set.fsockopt) { int error = 0; @@ -412,8 +587,32 @@ } if(response) { infof(data, "Ctrl conn has data while waiting for data conn"); + if(pp->overflow > 3) { + char *r = Curl_dyn_ptr(&pp->recvbuf); + + DEBUGASSERT((pp->overflow + pp->nfinal) <= + Curl_dyn_len(&pp->recvbuf)); + /* move over the most recently handled response line */ + r += pp->nfinal; + + if(LASTLINE(r)) { + int status = curlx_sltosi(strtol(r, NULL, 10)); + if(status == 226) { + /* funny timing situation where we get the final message on the + control connection before traffic on the data connection has been + noticed. Leave the 226 in there and use this as a trigger to read + the data socket. */ + infof(data, "Got 226 before data activity"); + *received = TRUE; + return CURLE_OK; + } + } + } + (void)Curl_GetFTPResponse(data, &nread, &ftpcode); + infof(data, "FTP code: %03d", ftpcode); + if(ftpcode/100 > 3) return CURLE_FTP_ACCEPT_FAILED; @@ -438,7 +637,7 @@ struct connectdata *conn = data->conn; bool connected; - DEBUGF(infof(data, "ftp InitiateTransfer()")); + CURL_TRC_FTP(data, "InitiateTransfer()"); if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); @@ -457,12 +656,12 @@ /* set the SO_SNDBUF for the secondary socket for those who need it */ Curl_sndbufset(conn->sock[SECONDARYSOCKET]); - Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, SECONDARYSOCKET); } else { /* FTP download: */ - Curl_setup_transfer(data, SECONDARYSOCKET, - conn->proto.ftpc.retr_size_saved, FALSE, -1); + Curl_xfer_setup(data, SECONDARYSOCKET, + conn->proto.ftpc.retr_size_saved, FALSE, -1); } conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */ @@ -521,18 +720,10 @@ } out: - DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result)); + CURL_TRC_FTP(data, "AllowServerConnect() -> %d", result); return result; } -/* 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])) - -/* macro to check for the last line in an FTP server response */ -#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) - static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, char *line, size_t len, int *code) { @@ -548,13 +739,13 @@ } static CURLcode ftp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, + int sockindex, struct pingpong *pp, int *ftpcode, /* return the ftp-code if done */ size_t *size) /* size of the response */ { int code; - CURLcode result = Curl_pp_readresp(data, sockfd, pp, &code, size); + CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size); #ifdef HAVE_GSSAPI { @@ -689,7 +880,7 @@ break; } } - result = ftp_readresp(data, sockfd, pp, ftpcode, &nread); + result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread); if(result) break; @@ -711,73 +902,6 @@ return result; } -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ -static const char * const ftp_state_names[]={ - "STOP", - "WAIT220", - "AUTH", - "USER", - "PASS", - "ACCT", - "PBSZ", - "PROT", - "CCC", - "PWD", - "SYST", - "NAMEFMT", - "QUOTE", - "RETR_PREQUOTE", - "STOR_PREQUOTE", - "POSTQUOTE", - "CWD", - "MKD", - "MDTM", - "TYPE", - "LIST_TYPE", - "RETR_TYPE", - "STOR_TYPE", - "SIZE", - "RETR_SIZE", - "STOR_SIZE", - "REST", - "RETR_REST", - "PORT", - "PRET", - "PASV", - "LIST", - "RETR", - "STOR", - "QUIT" -}; -#endif - -/* This is the ONLY way to change FTP state! */ -static void _ftp_state(struct Curl_easy *data, - ftpstate newstate -#ifdef DEBUGBUILD - , int lineno -#endif - ) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - -#if defined(DEBUGBUILD) - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) lineno; -#else - if(ftpc->state != newstate) - infof(data, "FTP %p (line %d) state change from %s to %s", - (void *)ftpc, lineno, ftp_state_names[ftpc->state], - ftp_state_names[newstate]); -#endif -#endif - - ftpc->state = newstate; -} - static CURLcode ftp_state_user(struct Curl_easy *data, struct connectdata *conn) { @@ -821,24 +945,18 @@ * remote site, or we could wait for that site to connect to us. Or just * handle ordinary commands. */ - - DEBUGF(infof(data, "ftp_domore_getsock()")); - if(conn->cfilter[SECONDARYSOCKET] - && !Curl_conn_is_connected(conn, SECONDARYSOCKET)) - return 0; + CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data)); if(FTP_STOP == ftpc->state) { - int bits = GETSOCK_READSOCK(0); - /* if stopped and still in this state, then we're also waiting for a connect on the secondary connection */ + DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || + (conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(conn, SECONDARYSOCKET))); socks[0] = conn->sock[FIRSTSOCKET]; - if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { - socks[1] = conn->sock[SECONDARYSOCKET]; - bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1); - } - - return bits; + /* An unconnected SECONDARY will add its socket by itself + * via its adjust_pollset() */ + return GETSOCK_READSOCK(0); } return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks); } @@ -917,7 +1035,7 @@ char hbuf[NI_MAXHOST]; struct sockaddr *sa = (struct sockaddr *)&ss; struct sockaddr_in * const sa4 = (void *)sa; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 * const sa6 = (void *)sa; #endif static const char mode[][5] = { "EPRT", "PORT" }; @@ -944,7 +1062,7 @@ (strlen(data->set.str[STRING_FTPPORT]) > 1)) { char *ip_end = NULL; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(*string_ftpport == '[') { /* [ipv6]:port(-range) */ char *ip_start = string_ftpport + 1; @@ -966,7 +1084,7 @@ if(ip_end) { /* either ipv6 or (ipv4|domain|interface):port(-range) */ addrlen = ip_end - string_ftpport; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { /* ipv6 */ port_min = port_max = 0; @@ -1012,7 +1130,7 @@ /* attempt to get the address of the given interface name */ switch(Curl_if2ip(conn->remote_addr->family, -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 Curl_ipv6_scope(&conn->remote_addr->sa_addr), conn->scope_id, #endif @@ -1044,7 +1162,7 @@ goto out; } switch(sa->sa_family) { -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); break; @@ -1094,7 +1212,8 @@ Curl_strerror(error, buffer, sizeof(buffer))); goto out; } - DEBUGF(infof(data, "ftp_state_use_port(), opened socket")); + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", + FTP_DSTATE(data)); /* step 3, bind to a suitable local address */ @@ -1104,7 +1223,7 @@ for(port = port_min; port <= port_max;) { if(sa->sa_family == AF_INET) sa4->sin_port = htons(port); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else sa6->sin6_port = htons(port); #endif @@ -1155,7 +1274,8 @@ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); goto out; } - DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port)); + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d", + FTP_DSTATE(data), port); /* step 4, listen on the socket */ @@ -1164,7 +1284,8 @@ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); goto out; } - DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port)); + CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d", + FTP_DSTATE(data), port); /* step 5, send the proper FTP command */ @@ -1172,13 +1293,19 @@ below */ Curl_printable_address(ai, myhost, sizeof(myhost)); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6) /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the request and enable EPRT again! */ conn->bits.ftp_use_eprt = TRUE; #endif + /* Replace any filter on SECONDARY with one listening on this socket */ + result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); + if(result) + goto out; + portsock = CURL_SOCKET_BAD; /* now held in filter */ + for(; fcmd != DONE; fcmd++) { if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) @@ -1193,7 +1320,7 @@ case AF_INET: port = ntohs(sa4->sin_port); break; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: port = ntohs(sa6->sin6_port); break; @@ -1252,11 +1379,6 @@ /* store which command was sent */ ftpc->count1 = fcmd; - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); - if(result) - goto out; - portsock = CURL_SOCKET_BAD; /* now held in filter */ ftp_state(data, FTP_PORT); out: @@ -1572,10 +1694,10 @@ append = TRUE; /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { + if(data->set.seek_func) { Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); Curl_set_in_callback(data, false); } @@ -1614,7 +1736,7 @@ infof(data, "File already completely uploaded"); /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); /* Set ->transfer so that we won't get any error in * ftp_done() because we didn't transfer anything! */ @@ -1792,7 +1914,7 @@ if(conn->bits.tunnel_proxy || conn->bits.socksproxy) return conn->host.name; #endif - return conn->primary_ip; + return conn->primary.remote_ip; } static bool match_pasv_6nums(const char *p, @@ -1929,14 +2051,14 @@ */ const char * const host_name = conn->bits.socksproxy ? conn->socks_proxy.host.name : conn->http_proxy.host.name; - rc = Curl_resolv(data, host_name, conn->port, FALSE, &addr); + rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr); if(rc == CURLRESOLV_PENDING) /* BLOCKING, ignores the return code but 'addr' will be NULL in case of failure */ (void)Curl_resolver_wait_resolv(data, &addr); - connectport = - (unsigned short)conn->port; /* we connect to the proxy's port */ + /* we connect to the proxy's port */ + connectport = (unsigned short)conn->primary.remote_port; if(!addr) { failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport); @@ -2238,7 +2360,7 @@ struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; - DEBUGF(infof(data, "ftp_state_retr()")); + CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_DSTATE(data)); if(data->set.max_filesize && (filesize > data->set.max_filesize)) { failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; @@ -2285,7 +2407,7 @@ if(ftp->downloadsize == 0) { /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); infof(data, "File already completely downloaded"); /* Set ->transfer so that we won't get any error in ftp_done() @@ -2692,7 +2814,6 @@ struct connectdata *conn) { CURLcode result; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; int ftpcode; struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; @@ -2702,7 +2823,7 @@ if(pp->sendleft) return Curl_pp_flushsend(data, pp); - result = ftp_readresp(data, sock, pp, &ftpcode, &nread); + result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread); if(result) return result; @@ -2954,7 +3075,7 @@ } } ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ - DEBUGF(infof(data, "protocol connect phase DONE")); + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data)); break; case FTP_SYST: @@ -2999,7 +3120,7 @@ } ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ - DEBUGF(infof(data, "protocol connect phase DONE")); + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data)); break; case FTP_NAMEFMT: @@ -3010,7 +3131,7 @@ } ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */ - DEBUGF(infof(data, "protocol connect phase DONE")); + CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data)); break; case FTP_QUOTE: @@ -3439,6 +3560,7 @@ /* Send any post-transfer QUOTE strings? */ if(!status && !result && !premature && data->set.postquote) result = ftp_sendquote(data, conn, data->set.postquote); + CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result); Curl_safefree(ftp->pathalloc); return result; } @@ -3702,12 +3824,13 @@ } /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ *completep = 1; - DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result)); + CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_DSTATE(data), + (int)result); } return result; @@ -3731,7 +3854,7 @@ /* this is FTP and no proxy */ CURLcode result = CURLE_OK; - DEBUGF(infof(data, "DO phase starts")); + CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_DSTATE(data)); if(data->req.no_body) { /* requested no body means no transfer... */ @@ -3751,10 +3874,15 @@ *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET); - infof(data, "ftp_perform ends with SECONDARY: %d", *connected); + if(*connected) + infof(data, "[FTP] [%s] perform, DATA connection established", + FTP_DSTATE(data)); + else + CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect", + FTP_DSTATE(data)); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete1")); + CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_DSTATE(data)); } return result; @@ -4011,6 +4139,24 @@ *done = FALSE; /* default to false */ ftpc->wait_data_conn = FALSE; /* default to no such wait */ +#ifdef CURL_DO_LINEEND_CONV + { + /* FTP data may need conversion. */ + struct Curl_cwriter *ftp_lc_writer; + + result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc, + CURL_CW_CONTENT_DECODE); + if(result) + return result; + + result = Curl_cwriter_add(data, ftp_lc_writer); + if(result) { + Curl_cwriter_free(data, ftp_lc_writer); + return result; + } + } +#endif /* CURL_DO_LINEEND_CONV */ + if(data->state.wildcardmatch) { result = wc_statemach(data); if(data->wildcard->state == CURLWC_SKIP || @@ -4287,7 +4433,7 @@ if(ftp->transfer != PPTRANSFER_BODY) /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); else if(!connected) /* since we didn't connect now, we want do_more to get called */ conn->bits.do_more = TRUE; @@ -4304,11 +4450,11 @@ CURLcode result = ftp_multi_statemach(data, dophase_done); if(result) - DEBUGF(infof(data, "DO phase failed")); + CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_DSTATE(data)); else if(*dophase_done) { result = ftp_dophase_done(data, FALSE /* not connected */); - DEBUGF(infof(data, "DO phase is complete2")); + CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_DSTATE(data)); } return result; } @@ -4432,6 +4578,7 @@ ftpc->use_ssl = data->set.use_ssl; ftpc->ccc = data->set.ftp_ccc; + CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(conn), result); return result; }
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index 82f1ea0..448f3a4 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -349,7 +349,7 @@ Curl_set_in_callback(data, false); if(add) { - Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list); + Curl_llist_append(llist, finfo, &infop->list); } else { Curl_fileinfo_cleanup(infop);
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index 2f74629..e423f0b 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c
@@ -76,10 +76,10 @@ free(info->wouldredirect); info->wouldredirect = NULL; - info->conn_primary_ip[0] = '\0'; - info->conn_local_ip[0] = '\0'; - info->conn_primary_port = 0; - info->conn_local_port = 0; + info->primary.remote_ip[0] = '\0'; + info->primary.local_ip[0] = '\0'; + info->primary.remote_port = 0; + info->primary.local_port = 0; info->retry_after = 0; info->conn_scheme = 0; @@ -153,15 +153,19 @@ break; case CURLINFO_PRIMARY_IP: /* Return the ip address of the most recent (primary) connection */ - *param_charp = data->info.conn_primary_ip; + *param_charp = data->info.primary.remote_ip; break; case CURLINFO_LOCAL_IP: /* Return the source/local ip address of the most recent (primary) connection */ - *param_charp = data->info.conn_local_ip; + *param_charp = data->info.primary.local_ip; break; case CURLINFO_RTSP_SESSION_ID: +#ifndef CURL_DISABLE_RTSP *param_charp = data->set.str[STRING_RTSP_SESSION_ID]; +#else + *param_charp = NULL; +#endif break; case CURLINFO_SCHEME: *param_charp = data->info.conn_scheme; @@ -180,7 +184,6 @@ *param_charp = NULL; #endif break; - default: return CURLE_UNKNOWN_OPTION; } @@ -285,11 +288,11 @@ break; case CURLINFO_PRIMARY_PORT: /* Return the (remote) port of the most recent (primary) connection */ - *param_longp = data->info.conn_primary_port; + *param_longp = data->info.primary.remote_port; break; case CURLINFO_LOCAL_PORT: /* Return the local port of the most recent (primary) connection */ - *param_longp = data->info.conn_local_port; + *param_longp = data->info.primary.local_port; break; case CURLINFO_PROXY_ERROR: *param_longp = (long)data->info.pxcode; @@ -334,6 +337,15 @@ case CURLINFO_PROTOCOL: *param_longp = data->info.conn_protocol; break; + case CURLINFO_USED_PROXY: + *param_longp = +#ifdef CURL_DISABLE_PROXY + 0 +#else + data->info.used_proxy +#endif + ; + break; default: return CURLE_UNKNOWN_OPTION; }
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index 9ca0828..311bd37 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c
@@ -62,7 +62,7 @@ */ const struct Curl_handler Curl_handler_gopher = { - "GOPHER", /* scheme */ + "gopher", /* scheme */ ZERO_NULL, /* setup_connection */ gopher_do, /* do_it */ ZERO_NULL, /* done */ @@ -76,6 +76,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_GOPHER, /* defport */ @@ -86,7 +87,7 @@ #ifdef USE_SSL const struct Curl_handler Curl_handler_gophers = { - "GOPHERS", /* scheme */ + "gophers", /* scheme */ ZERO_NULL, /* setup_connection */ gopher_do, /* do_it */ ZERO_NULL, /* done */ @@ -100,6 +101,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_GOPHER, /* defport */ @@ -139,8 +141,8 @@ char *sel = NULL; char *sel_org = NULL; timediff_t timeout_ms; - ssize_t amount, k; - size_t len; + ssize_t k; + size_t amount, len; int what; *done = TRUE; /* unconditionally */ @@ -185,7 +187,7 @@ if(strlen(sel) < 1) break; - result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount); + result = Curl_xfer_send(data, sel, k, &amount); if(!result) { /* Which may not have written it all! */ result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); if(result) @@ -227,7 +229,7 @@ free(sel_org); if(!result) - result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount); + result = Curl_xfer_send(data, "\r\n", 2, &amount); if(result) { failf(data, "Failed sending Gopher request"); return result; @@ -236,7 +238,7 @@ if(result) return result; - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); return CURLE_OK; } #endif /* CURL_DISABLE_GOPHER */
diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c index 30f28e2..ddbae4d 100644 --- a/Utilities/cmcurl/lib/hash.c +++ b/Utilities/cmcurl/lib/hash.c
@@ -57,7 +57,7 @@ */ void Curl_hash_init(struct Curl_hash *h, - int slots, + size_t slots, hash_function hfunc, comp_function comparator, Curl_hash_dtor dtor) @@ -111,7 +111,7 @@ DEBUGASSERT(h); DEBUGASSERT(h->slots); if(!h->table) { - int i; + size_t i; h->table = malloc(h->slots * sizeof(struct Curl_llist)); if(!h->table) return NULL; /* OOM */ @@ -132,7 +132,7 @@ he = mk_hash_element(key, key_len, p); if(he) { - Curl_llist_insert_next(l, l->tail, he, &he->list); + Curl_llist_append(l, he, &he->list); ++h->size; return p; /* return the new entry */ } @@ -192,25 +192,6 @@ return NULL; } -#if defined(DEBUGBUILD) && defined(AGGRESSIVE_TEST) -void -Curl_hash_apply(Curl_hash *h, void *user, - void (*cb)(void *user, void *ptr)) -{ - struct Curl_llist_element *le; - int i; - - for(i = 0; i < h->slots; ++i) { - for(le = (h->table[i])->head; - le; - le = le->next) { - Curl_hash_element *el = le->ptr; - cb(user, el->ptr); - } - } -} -#endif - /* Destroys all the entries in the given hash and resets its attributes, * prepping the given hash for [static|dynamic] deallocation. * @@ -222,7 +203,7 @@ Curl_hash_destroy(struct Curl_hash *h) { if(h->table) { - int i; + size_t i; for(i = 0; i < h->slots; ++i) { Curl_llist_destroy(&h->table[i], (void *) h); } @@ -250,7 +231,7 @@ struct Curl_llist_element *le; struct Curl_llist_element *lnext; struct Curl_llist *list; - int i; + size_t i; if(!h || !h->table) return; @@ -316,7 +297,7 @@ /* If we have reached the end of the list, find the next one */ if(!iter->current_element) { - int i; + size_t i; for(i = iter->slot_index; i < h->slots; i++) { if(h->table[i].head) { iter->current_element = h->table[i].head; @@ -339,7 +320,7 @@ { struct Curl_hash_iterator iter; struct Curl_hash_element *he; - int last_index = -1; + size_t last_index = ~0; if(!h) return; @@ -352,7 +333,7 @@ while(he) { if(iter.slot_index != last_index) { fprintf(stderr, "index %d:", iter.slot_index); - if(last_index >= 0) { + if(last_index != ~0) { fprintf(stderr, "\n"); } last_index = iter.slot_index; @@ -368,3 +349,25 @@ fprintf(stderr, "\n"); } #endif + +void Curl_hash_offt_init(struct Curl_hash *h, + size_t slots, + Curl_hash_dtor dtor) +{ + Curl_hash_init(h, slots, Curl_hash_str, Curl_str_key_compare, dtor); +} + +void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem) +{ + return Curl_hash_add(h, &id, sizeof(id), elem); +} + +int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id) +{ + return Curl_hash_delete(h, &id, sizeof(id)); +} + +void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id) +{ + return Curl_hash_pick(h, &id, sizeof(id)); +}
diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h index 9cfffc2..2bdc247 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h
@@ -54,7 +54,7 @@ /* Comparator function to compare keys */ comp_function comp_func; Curl_hash_dtor dtor; - int slots; + size_t slots; size_t size; }; @@ -67,12 +67,12 @@ struct Curl_hash_iterator { struct Curl_hash *hash; - int slot_index; + size_t slot_index; struct Curl_llist_element *current_element; }; void Curl_hash_init(struct Curl_hash *h, - int slots, + size_t slots, hash_function hfunc, comp_function comparator, Curl_hash_dtor dtor); @@ -80,8 +80,6 @@ void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p); int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len); -void Curl_hash_apply(struct Curl_hash *h, void *user, - void (*cb)(void *user, void *ptr)); #define Curl_hash_count(h) ((h)->size) void Curl_hash_destroy(struct Curl_hash *h); void Curl_hash_clean(struct Curl_hash *h); @@ -98,5 +96,13 @@ void Curl_hash_print(struct Curl_hash *h, void (*func)(void *)); +/* Hash for `curl_off_t` as key */ +void Curl_hash_offt_init(struct Curl_hash *h, size_t slots, + Curl_hash_dtor dtor); + +void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem); +int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id); +void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id); + #endif /* HEADER_CURL_HASH_H */
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c index 8a3264a..9a5692e 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c
@@ -27,6 +27,7 @@ #include "urldata.h" #include "strdup.h" #include "strcase.h" +#include "sendf.h" #include "headers.h" /* The last 3 #include files should be in this order */ @@ -252,7 +253,7 @@ newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); if(!newhs) return CURLE_OUT_OF_MEMORY; - /* ->name' and ->value point into ->buffer (to keep the header allocation + /* ->name and ->value point into ->buffer (to keep the header allocation in a single memory block), which now potentially have moved. Adjust them. */ newhs->name = newhs->buffer; @@ -263,8 +264,7 @@ newhs->value[olen + vlen] = 0; /* null-terminate at newline */ /* insert this node into the list of headers */ - Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, - newhs, &newhs->node); + Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node); data->state.prevhead = newhs; return CURLE_OK; } @@ -327,8 +327,7 @@ hs->request = data->state.requests; /* insert this node into the list of headers */ - Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, - hs, &hs->node); + Curl_llist_append(&data->state.httphdrs, hs, &hs->node); data->state.prevhead = hs; } else @@ -337,14 +336,70 @@ } /* - * Curl_headers_init(). Init the headers subsystem. + * Curl_headers_reset(). Reset the headers subsystem. */ -static void headers_init(struct Curl_easy *data) +static void headers_reset(struct Curl_easy *data) { Curl_llist_init(&data->state.httphdrs, NULL); data->state.prevhead = NULL; } +struct hds_cw_collect_ctx { + struct Curl_cwriter super; +}; + +static CURLcode hds_cw_collect_write(struct Curl_easy *data, + struct Curl_cwriter *writer, int type, + const char *buf, size_t blen) +{ + if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) { + unsigned char htype = (unsigned char) + (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : + (type & CLIENTWRITE_1XX ? CURLH_1XX : + (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : + CURLH_HEADER))); + CURLcode result = Curl_headers_push(data, buf, htype); + CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", + htype, blen, result); + if(result) + return result; + } + return Curl_cwriter_write(data, writer->next, type, buf, blen); +} + +static const struct Curl_cwtype hds_cw_collect = { + "hds-collect", + NULL, + Curl_cwriter_def_init, + hds_cw_collect_write, + Curl_cwriter_def_close, + sizeof(struct hds_cw_collect_ctx) +}; + +CURLcode Curl_headers_init(struct Curl_easy *data) +{ + struct Curl_cwriter *writer; + CURLcode result; + + if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) { + /* avoid installing it twice */ + if(Curl_cwriter_get_by_name(data, hds_cw_collect.name)) + return CURLE_OK; + + result = Curl_cwriter_create(&writer, data, &hds_cw_collect, + CURL_CW_PROTOCOL); + if(result) + return result; + + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + return result; + } + } + return CURLE_OK; +} + /* * Curl_headers_cleanup(). Free all stored headers and associated memory. */ @@ -358,7 +413,7 @@ n = e->next; free(hs); } - headers_init(data); + headers_reset(data); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h index a5229ea..d981338 100644 --- a/Utilities/cmcurl/lib/headers.h +++ b/Utilities/cmcurl/lib/headers.h
@@ -37,6 +37,12 @@ }; /* + * Initialize header collecting for a transfer. + * Will add a client writer that catches CLIENTWRITE_HEADER writes. + */ +CURLcode Curl_headers_init(struct Curl_easy *data); + +/* * Curl_headers_push() gets passed a full header to store. */ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, @@ -48,6 +54,7 @@ CURLcode Curl_headers_cleanup(struct Curl_easy *data); #else +#define Curl_headers_init(x) CURLE_OK #define Curl_headers_push(x,y,z) CURLE_OK #define Curl_headers_cleanup(x) Curl_nop_stmt #endif
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 4f44d34..93c36e0 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c
@@ -144,7 +144,7 @@ (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize); break; } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: { const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr; const struct in6_addr *ipaddr6 = &sa6->sin6_addr; @@ -167,17 +167,12 @@ int port, char *ptr, size_t buflen) { size_t len = nlen ? nlen : strlen(name); - size_t olen = 0; DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); if(len > (buflen - 7)) len = buflen - 7; /* store and lower case the name */ - while(len--) { - *ptr++ = Curl_raw_tolower(*name++); - olen++; - } - olen += msnprintf(ptr, 7, ":%u", port); - return olen; + Curl_strntolower(ptr, name, len); + return msnprintf(&ptr[len], 7, ":%u", port) + len; } struct hostcache_prune_data { @@ -249,7 +244,7 @@ if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - time(&now); + now = time(NULL); do { /* Remove outdated and unused entries from the hostcache */ @@ -288,7 +283,7 @@ size_t entry_len = create_hostcache_id(hostname, 0, port, entry_id, sizeof(entry_id)); - /* See if its already in our dns cache */ + /* See if it's already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); /* No entry found in cache, check if we might have a wildcard entry */ @@ -303,7 +298,7 @@ /* See whether the returned entry is stale. Done before we release lock */ struct hostcache_prune_data user; - time(&user.now); + user.now = time(NULL); user.cache_timeout = data->set.dns_cache_timeout; user.oldest = 0; @@ -523,7 +518,7 @@ return dns; } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 /* return a static IPv6 ::1 for the name */ static struct Curl_addrinfo *get_localhost6(int port, const char *name) { @@ -600,7 +595,7 @@ return ca6; } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 /* * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ @@ -632,7 +627,7 @@ return (ipv6_works>0)?TRUE:FALSE; } } -#endif /* ENABLE_IPV6 */ +#endif /* USE_IPV6 */ /* * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4 @@ -641,11 +636,11 @@ bool Curl_host_is_ipnum(const char *hostname) { struct in_addr in; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct in6_addr in6; #endif if(Curl_inet_pton(AF_INET, hostname, &in) > 0 -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 || Curl_inet_pton(AF_INET6, hostname, &in6) > 0 #endif ) @@ -760,7 +755,7 @@ if(!addr) return CURLRESOLV_ERROR; } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else { struct in6_addr in6; /* check if this is an IPv6 address string */ @@ -771,7 +766,7 @@ return CURLRESOLV_ERROR; } } -#endif /* ENABLE_IPV6 */ +#endif /* USE_IPV6 */ #else /* if USE_RESOLVE_ON_IPS */ #ifndef CURL_DISABLE_DOH @@ -779,7 +774,7 @@ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) /* This is a dotted IP address 123.123.123.123-style */ ipnum = TRUE; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else { struct in6_addr in6; /* check if this is an IPv6 address string */ @@ -787,7 +782,7 @@ /* This is an IPv6 address literal */ ipnum = TRUE; } -#endif /* ENABLE_IPV6 */ +#endif /* USE_IPV6 */ #endif /* CURL_DISABLE_DOH */ #endif /* !USE_RESOLVE_ON_IPS */ @@ -1070,6 +1065,23 @@ dns->inuse--; if(dns->inuse == 0) { Curl_freeaddrinfo(dns->addr); +#ifdef USE_HTTPSRR + if(dns->hinfo) { + if(dns->hinfo->target) + free(dns->hinfo->target); + if(dns->hinfo->alpns) + free(dns->hinfo->alpns); + if(dns->hinfo->ipv4hints) + free(dns->hinfo->ipv4hints); + if(dns->hinfo->echconfiglist) + free(dns->hinfo->echconfiglist); + if(dns->hinfo->ipv6hints) + free(dns->hinfo->ipv6hints); + if(dns->hinfo->val) + free(dns->hinfo->val); + free(dns->hinfo); + } +#endif free(dns); } } @@ -1077,7 +1089,7 @@ /* * Curl_init_dnscache() inits a new DNS cache. */ -void Curl_init_dnscache(struct Curl_hash *hash, int size) +void Curl_init_dnscache(struct Curl_hash *hash, size_t size) { Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare, freednsentry); @@ -1210,7 +1222,7 @@ memcpy(address, addr_begin, alen); address[alen] = '\0'; -#ifndef ENABLE_IPV6 +#ifndef USE_IPV6 if(strchr(address, ':')) { infof(data, "Ignoring resolve address '%s', missing IPv6 support.", address);
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index fb53a57..bf4e94d 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h
@@ -32,6 +32,10 @@ #include <setjmp.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 * required for storing all possible aliases and IP numbers is according to @@ -58,8 +62,41 @@ */ struct Curl_hash *Curl_global_host_cache_init(void); +#ifdef USE_HTTPSRR + +#define CURL_MAXLEN_host_name 253 + +struct Curl_https_rrinfo { + size_t len; /* raw encoded length */ + unsigned char *val; /* raw encoded octets */ + /* + * fields from HTTPS RR, with the mandatory fields + * first (priority, target), then the others in the + * order of the keytag numbers defined at + * https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 + */ + uint16_t priority; + char *target; + char *alpns; /* keytag = 1 */ + bool no_def_alpn; /* keytag = 2 */ + /* + * we don't support ports (keytag = 3) as we don't support + * port-switching yet + */ + unsigned char *ipv4hints; /* keytag = 4 */ + size_t ipv4hints_len; + unsigned char *echconfiglist; /* keytag = 5 */ + size_t echconfiglist_len; + unsigned char *ipv6hints; /* keytag = 6 */ + size_t ipv6hints_len; +}; +#endif + struct Curl_dns_entry { struct Curl_addrinfo *addr; +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo *hinfo; +#endif /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */ time_t timestamp; /* use-counter, use Curl_resolv_unlock to release reference */ @@ -96,7 +133,7 @@ struct Curl_dns_entry **dnsentry, timediff_t timeoutms); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 /* * Curl_ipv6works() returns TRUE if IPv6 seems to work. */ @@ -129,7 +166,7 @@ struct Curl_dns_entry *dns); /* init a new dns cache */ -void Curl_init_dnscache(struct Curl_hash *hash, int hashsize); +void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize); /* prune old entries from the DNS cache */ void Curl_hostcache_prune(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c index 8725a35..a5e7676 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c
@@ -107,11 +107,6 @@ } } -static struct stsentry *hsts_entry(void) -{ - return calloc(1, sizeof(struct stsentry)); -} - static CURLcode hsts_create(struct hsts *h, const char *hostname, bool subdomains, @@ -127,7 +122,7 @@ --hlen; if(hlen) { char *duphost; - struct stsentry *sts = hsts_entry(); + struct stsentry *sts = calloc(1, sizeof(struct stsentry)); if(!sts) return CURLE_OUT_OF_MEMORY; @@ -140,7 +135,7 @@ sts->host = duphost; sts->expires = expires; sts->includeSubDomains = subdomains; - Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node); + Curl_llist_append(&h->list, sts, &sts->node); } return CURLE_OK; } @@ -511,7 +506,6 @@ static CURLcode hsts_load(struct hsts *h, const char *file) { CURLcode result = CURLE_OK; - char *line = NULL; FILE *fp; /* we need a private copy of the file name so that the hsts cache file @@ -523,28 +517,25 @@ fp = fopen(file, FOPEN_READTEXT); if(fp) { - line = malloc(MAX_HSTS_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_HSTS_LINE, fp)) { - char *lineptr = line; + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_HSTS_LINE); + while(Curl_get_line(&buf, fp)) { + char *lineptr = Curl_dyn_ptr(&buf); while(*lineptr && ISBLANK(*lineptr)) lineptr++; - if(*lineptr == '#') - /* skip commented lines */ + /* + * 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); } - free(line); /* free the line buffer */ + Curl_dyn_free(&buf); /* free the line buffer */ fclose(fp); } return result; - -fail: - Curl_safefree(h->filename); - fclose(fp); - return CURLE_OUT_OF_MEMORY; } /*
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 679931e..2a41f80 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c
@@ -65,7 +65,6 @@ #include "vquic/vquic.h" #include "http_digest.h" #include "http_ntlm.h" -#include "curl_ntlm_wb.h" #include "http_negotiate.h" #include "http_aws_sigv4.h" #include "url.h" @@ -73,6 +72,7 @@ #include "hostip.h" #include "dynhds.h" #include "http.h" +#include "headers.h" #include "select.h" #include "parsedate.h" /* for the week day and month names */ #include "strtoofft.h" @@ -100,13 +100,16 @@ * Forward declarations. */ -static bool http_should_fail(struct Curl_easy *data); +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); /* * HTTP handler interface. */ const struct Curl_handler Curl_handler_http = { - "HTTP", /* scheme */ + "http", /* scheme */ Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ @@ -120,6 +123,7 @@ ZERO_NULL, /* perform_getsock */ 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 */ PORT_HTTP, /* defport */ @@ -134,7 +138,7 @@ * HTTPS handler interface. */ const struct Curl_handler Curl_handler_https = { - "HTTPS", /* scheme */ + "https", /* scheme */ Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ @@ -148,6 +152,7 @@ ZERO_NULL, /* perform_getsock */ 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 */ PORT_HTTPS, /* defport */ @@ -240,8 +245,6 @@ while(*start && ISSPACE(*start)) start++; - /* data is in the host encoding so - use '\r' and '\n' instead of 0x0d and 0x0a */ end = strchr(start, '\r'); if(!end) end = strchr(start, '\n'); @@ -382,8 +385,6 @@ #endif else if(avail & CURLAUTH_NTLM) pick->picked = CURLAUTH_NTLM; - else if(avail & CURLAUTH_NTLM_WB) - pick->picked = CURLAUTH_NTLM_WB; #ifndef CURL_DISABLE_BASIC_AUTH else if(avail & CURLAUTH_BASIC) pick->picked = CURLAUTH_BASIC; @@ -404,150 +405,86 @@ /* * http_perhapsrewind() * - * If we are doing POST or PUT { - * If we have more data to send { - * If we are doing NTLM { - * Keep sending since we must not disconnect - * } - * else { - * If there is more than just a little data left to send, close - * the current connection by force. - * } - * } - * If we have sent any data { - * If we don't have track of all the data { - * call app to tell it to rewind - * } - * else { - * rewind internally so that the operation can restart fine - * } - * } - * } + * The current request needs to be done again - maybe due to a follow + * or authentication negotiation. Check if: + * 1) a rewind of the data sent to the server is necessary + * 2) the current transfer should continue or be stopped early */ static CURLcode http_perhapsrewind(struct Curl_easy *data, struct connectdata *conn) { - struct HTTP *http = data->req.p.http; - curl_off_t bytessent; - curl_off_t expectsend = -1; /* default is unknown */ + curl_off_t bytessent = data->req.writebytecount; + curl_off_t expectsend = Curl_creader_total_length(data); + curl_off_t upload_remain = (expectsend >= 0)? (expectsend - bytessent) : -1; + bool little_upload_remains = (upload_remain >= 0 && upload_remain < 2000); + bool needs_rewind = Curl_creader_needs_rewind(data); + /* By default, we'd like to abort the transfer when little or + * unknown amount remains. But this may be overridden by authentications + * further below! */ + bool abort_upload = (!data->req.upload_done && !little_upload_remains); + const char *ongoing_auth = NULL; - if(!http) - /* If this is still NULL, we have not reach very far and we can safely - skip this rewinding stuff */ + /* We need a rewind before uploading client read data again. The + * checks below just 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 + * for a new connection. */ + if(needs_rewind) { + infof(data, "Need to rewind upload for next request"); + Curl_creader_set_rewind(data, TRUE); + } + + if(conn->bits.close) + /* If we already decided to close this connection, we cannot veto. */ return CURLE_OK; - switch(data->state.httpreq) { - case HTTPREQ_GET: - case HTTPREQ_HEAD: - return CURLE_OK; - default: - break; - } - - bytessent = data->req.writebytecount; - - if(conn->bits.authneg) { - /* This is a state where we are known to be negotiating and we don't send - any data then. */ - expectsend = 0; - } - else if(!conn->bits.protoconnstart) { - /* HTTP CONNECT in progress: there is no body */ - expectsend = 0; - } - else { - /* figure out how much data we are expected to send */ - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - expectsend = data->state.infilesize; - break; - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - expectsend = http->postsize; - break; - default: - break; - } - } - - data->state.rewindbeforesend = FALSE; /* default */ - - if((expectsend == -1) || (expectsend > bytessent)) { + if(abort_upload) { + /* We'd like to abort the upload - but should we? */ #if defined(USE_NTLM) - /* There is still data left to send */ if((data->state.authproxy.picked == CURLAUTH_NTLM) || - (data->state.authhost.picked == CURLAUTH_NTLM) || - (data->state.authproxy.picked == CURLAUTH_NTLM_WB) || - (data->state.authhost.picked == CURLAUTH_NTLM_WB)) { - if(((expectsend - bytessent) < 2000) || - (conn->http_ntlm_state != NTLMSTATE_NONE) || + (data->state.authhost.picked == CURLAUTH_NTLM)) { + ongoing_auth = "NTML"; + if((conn->http_ntlm_state != NTLMSTATE_NONE) || (conn->proxy_ntlm_state != NTLMSTATE_NONE)) { - /* The NTLM-negotiation has started *OR* there is just a little (<2K) - data left to send, keep on sending. */ - - /* rewind data when completely done sending! */ - if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { - data->state.rewindbeforesend = TRUE; - infof(data, "Rewind stream before next send"); - } - - return CURLE_OK; + /* The NTLM-negotiation has started, keep on sending. + * Need to do further work on same connection */ + abort_upload = FALSE; } - - if(conn->bits.close) - /* this is already marked to get closed */ - return CURLE_OK; - - infof(data, "NTLM send, close instead of sending %" - CURL_FORMAT_CURL_OFF_T " bytes", - (curl_off_t)(expectsend - bytessent)); } #endif #if defined(USE_SPNEGO) /* There is still data left to send */ if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { - if(((expectsend - bytessent) < 2000) || - (conn->http_negotiate_state != GSS_AUTHNONE) || + ongoing_auth = "NEGOTIATE"; + if((conn->http_negotiate_state != GSS_AUTHNONE) || (conn->proxy_negotiate_state != GSS_AUTHNONE)) { - /* The NEGOTIATE-negotiation has started *OR* - there is just a little (<2K) data left to send, keep on sending. */ - - /* rewind data when completely done sending! */ - if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { - data->state.rewindbeforesend = TRUE; - infof(data, "Rewind stream before next send"); - } - - return CURLE_OK; + /* The NEGOTIATE-negotiation has started, keep on sending. + * Need to do further work on same connection */ + abort_upload = FALSE; } - - if(conn->bits.close) - /* this is already marked to get closed */ - return CURLE_OK; - - infof(data, "NEGOTIATE send, close instead of sending %" - CURL_FORMAT_CURL_OFF_T " bytes", - (curl_off_t)(expectsend - bytessent)); } #endif + } - /* This is not NEGOTIATE/NTLM or many bytes left to send: close */ + if(abort_upload) { + if(upload_remain >= 0) + infof(data, "%s%sclose instead of sending %" + CURL_FORMAT_CURL_OFF_T " more bytes", + ongoing_auth? ongoing_auth : "", + ongoing_auth? " send, " : "", + upload_remain); + else + infof(data, "%s%sclose instead of sending unknown amount " + "of more bytes", + ongoing_auth? ongoing_auth : "", + ongoing_auth? " send, " : ""); + /* We decided to abort the ongoing transfer */ streamclose(conn, "Mid-auth HTTP and much data left to send"); + /* FIXME: questionable manipulation here, can we do this differently? */ data->req.size = 0; /* don't download any more than 0 bytes */ - - /* There still is data left to send, but this connection is marked for - closure so we can safely do the rewind right now */ } - - if(bytessent) { - /* mark for rewind since if we already sent something */ - data->state.rewindbeforesend = TRUE; - infof(data, "Please rewind output before next send"); - } - return CURLE_OK; } @@ -578,7 +515,7 @@ if((data->state.aptr.user || data->set.str[STRING_BEARER]) && ((data->req.httpcode == 401) || - (conn->bits.authneg && data->req.httpcode < 300))) { + (data->req.authneg && data->req.httpcode < 300))) { pickhost = pickoneauth(&data->state.authhost, authmask); if(!pickhost) data->state.authproblem = TRUE; @@ -592,7 +529,7 @@ #ifndef CURL_DISABLE_PROXY if(conn->bits.proxy_user_passwd && ((data->req.httpcode == 407) || - (conn->bits.authneg && data->req.httpcode < 300))) { + (data->req.authneg && data->req.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy, authmask & ~CURLAUTH_BEARER); if(!pickproxy) @@ -601,13 +538,10 @@ #endif if(pickhost || pickproxy) { - if((data->state.httpreq != HTTPREQ_GET) && - (data->state.httpreq != HTTPREQ_HEAD) && - !data->state.rewindbeforesend) { - result = http_perhapsrewind(data, conn); - if(result) - return result; - } + result = http_perhapsrewind(data, conn); + if(result) + return result; + /* In case this is GSS auth, the newurl field is already allocated so we must make sure to free it before allocating a new one. As figured out in bug #2284386 */ @@ -618,7 +552,7 @@ } else if((data->req.httpcode < 300) && (!data->state.authhost.done) && - conn->bits.authneg) { + data->req.authneg) { /* no (known) authentication available, authentication is not "done" yet and no authentication seems to be required and @@ -631,7 +565,7 @@ data->state.authhost.done = TRUE; } } - if(http_should_fail(data)) { + if(http_should_fail(data, data->req.httpcode)) { failf(data, "The requested URL returned error: %d", data->req.httpcode); result = CURLE_HTTP_RETURNED_ERROR; @@ -688,15 +622,6 @@ } else #endif -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) - if(authstatus->picked == CURLAUTH_NTLM_WB) { - auth = "NTLM_WB"; - result = Curl_output_ntlm_wb(data, conn, proxy); - if(result) - return result; - } - else -#endif #ifndef CURL_DISABLE_DIGEST_AUTH if(authstatus->picked == CURLAUTH_DIGEST) { auth = "Digest"; @@ -863,10 +788,10 @@ (httpreq != HTTPREQ_HEAD)) { /* Auth is required and we are not authenticated yet. Make a PUT or POST with content-length zero as a "probe". */ - conn->bits.authneg = TRUE; + data->req.authneg = TRUE; } else - conn->bits.authneg = FALSE; + data->req.authneg = FALSE; return result; } @@ -986,31 +911,15 @@ /* NTLM support requires the SSL crypto libs */ if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) { if((authp->avail & CURLAUTH_NTLM) || - (authp->avail & CURLAUTH_NTLM_WB) || Curl_auth_is_ntlm_supported()) { *availp |= CURLAUTH_NTLM; authp->avail |= CURLAUTH_NTLM; - if(authp->picked == CURLAUTH_NTLM || - authp->picked == CURLAUTH_NTLM_WB) { + if(authp->picked == CURLAUTH_NTLM) { /* NTLM authentication is picked and activated */ CURLcode result = Curl_input_ntlm(data, proxy, auth); if(!result) { data->state.authproblem = FALSE; -#ifdef NTLM_WB_ENABLED - if(authp->picked == CURLAUTH_NTLM_WB) { - *availp &= ~CURLAUTH_NTLM; - authp->avail &= ~CURLAUTH_NTLM; - *availp |= CURLAUTH_NTLM_WB; - authp->avail |= CURLAUTH_NTLM_WB; - - result = Curl_input_ntlm_wb(data, conn, proxy, auth); - if(result) { - infof(data, "Authentication problem. Ignoring this."); - data->state.authproblem = TRUE; - } - } -#endif } else { infof(data, "Authentication problem. Ignoring this."); @@ -1097,21 +1006,18 @@ } /** - * http_should_fail() determines whether an HTTP response has gotten us + * 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) +static bool http_should_fail(struct Curl_easy *data, int httpcode) { - int httpcode; DEBUGASSERT(data); DEBUGASSERT(data->conn); - httpcode = data->req.httpcode; - /* ** If we haven't been asked to fail on error, ** don't fail. @@ -1174,274 +1080,6 @@ } /* - * readmoredata() is a "fread() emulation" to provide POST and/or request - * data. It is used when a huge POST is to be made and the entire chunk wasn't - * sent in the first send(). This function will then be called from the - * transfer.c loop when more data is to be sent to the peer. - * - * Returns the amount of bytes it filled the buffer with. - */ -static size_t readmoredata(char *buffer, - size_t size, - size_t nitems, - void *userp) -{ - struct HTTP *http = (struct HTTP *)userp; - struct Curl_easy *data = http->backup.data; - size_t fullsize = size * nitems; - - if(!http->postsize) - /* nothing to return */ - return 0; - - /* make sure that an HTTP request is never sent away chunked! */ - data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; - - if(data->set.max_send_speed && - (data->set.max_send_speed < (curl_off_t)fullsize) && - (data->set.max_send_speed < http->postsize)) - /* speed limit */ - fullsize = (size_t)data->set.max_send_speed; - - else if(http->postsize <= (curl_off_t)fullsize) { - memcpy(buffer, http->postdata, (size_t)http->postsize); - fullsize = (size_t)http->postsize; - - if(http->backup.postsize) { - /* move backup data into focus and continue on that */ - http->postdata = http->backup.postdata; - http->postsize = http->backup.postsize; - data->state.fread_func = http->backup.fread_func; - data->state.in = http->backup.fread_in; - - http->sending++; /* move one step up */ - - http->backup.postsize = 0; - } - else - http->postsize = 0; - - return fullsize; - } - - memcpy(buffer, http->postdata, fullsize); - http->postdata += fullsize; - http->postsize -= fullsize; - - return fullsize; -} - -/* - * Curl_buffer_send() sends a header buffer and frees all associated - * memory. Body data may be appended to the header data if desired. - * - * Returns CURLcode - */ -CURLcode Curl_buffer_send(struct dynbuf *in, - struct Curl_easy *data, - struct HTTP *http, - /* add the number of sent bytes to this - counter */ - curl_off_t *bytes_written, - /* how much of the buffer contains body data */ - curl_off_t included_body_bytes, - int sockindex) -{ - ssize_t amount; - CURLcode result; - char *ptr; - size_t size; - struct connectdata *conn = data->conn; - size_t sendsize; - size_t headersize; - - DEBUGASSERT(sockindex <= SECONDARYSOCKET && sockindex >= 0); - - /* The looping below is required since we use non-blocking sockets, but due - to the circumstances we will just loop and try again and again etc */ - - ptr = Curl_dyn_ptr(in); - size = Curl_dyn_len(in); - - headersize = size - (size_t)included_body_bytes; /* the initial part that - isn't body is header */ - - DEBUGASSERT(size > (size_t)included_body_bytes); - - if((conn->handler->flags & PROTOPT_SSL -#ifndef CURL_DISABLE_PROXY - || IS_HTTPS_PROXY(conn->http_proxy.proxytype) -#endif - ) - && conn->httpversion < 20) { - /* Make sure this doesn't send more body bytes than what the max send - speed says. The request bytes do not count to the max speed. - */ - if(data->set.max_send_speed && - (included_body_bytes > data->set.max_send_speed)) { - curl_off_t overflow = included_body_bytes - data->set.max_send_speed; - DEBUGASSERT((size_t)overflow < size); - sendsize = size - (size_t)overflow; - } - else - sendsize = size; - - /* OpenSSL is very picky and we must send the SAME buffer pointer to the - library when we attempt to re-send this buffer. Sending the same data - is not enough, we must use the exact same address. For this reason, we - must copy the data to the uploadbuffer first, since that is the buffer - we will be using if this send is retried later. - */ - result = Curl_get_upload_buffer(data); - if(result) { - /* malloc failed, free memory and return to the caller */ - Curl_dyn_free(in); - return result; - } - /* We never send more than upload_buffer_size bytes in one single chunk - when we speak HTTPS, as if only a fraction of it is sent now, this data - needs to fit into the normal read-callback buffer later on and that - buffer is using this size. - */ - if(sendsize > (size_t)data->set.upload_buffer_size) - sendsize = (size_t)data->set.upload_buffer_size; - - memcpy(data->state.ulbuf, ptr, sendsize); - ptr = data->state.ulbuf; - } - else { -#ifdef CURLDEBUG - /* Allow debug builds to override this logic to force short initial - sends - */ - char *p = getenv("CURL_SMALLREQSEND"); - if(p) { - size_t altsize = (size_t)strtoul(p, NULL, 10); - if(altsize) - sendsize = CURLMIN(size, altsize); - else - sendsize = size; - } - else -#endif - { - /* Make sure this doesn't send more body bytes than what the max send - speed says. The request bytes do not count to the max speed. - */ - if(data->set.max_send_speed && - (included_body_bytes > data->set.max_send_speed)) { - curl_off_t overflow = included_body_bytes - data->set.max_send_speed; - DEBUGASSERT((size_t)overflow < size); - sendsize = size - (size_t)overflow; - } - else - sendsize = size; - } - - /* We currently cannot send more that this for http here: - * - if sending blocks, it return 0 as amount - * - we then whisk aside the `in` into the `http` struct - * and install our own `data->state.fread_func` that - * on subsequent calls reads `in` empty. - * - when the whisked away `in` is empty, the `fread_func` - * is restored to its original state. - * The problem is that `fread_func` can only return - * `upload_buffer_size` lengths. If the send we do here - * is larger and blocks, we do re-sending with smaller - * amounts of data and connection filters do not like - * that. - */ - if(http && (sendsize > (size_t)data->set.upload_buffer_size)) - sendsize = (size_t)data->set.upload_buffer_size; - } - - result = Curl_nwrite(data, sockindex, ptr, sendsize, &amount); - - if(!result) { - /* - * Note that we may not send the entire chunk at once, and we have a set - * number of data bytes at the end of the big buffer (out of which we may - * only send away a part). - */ - /* how much of the header that was sent */ - size_t headlen = (size_t)amount>headersize ? headersize : (size_t)amount; - size_t bodylen = amount - headlen; - - /* this data _may_ contain binary stuff */ - Curl_debug(data, CURLINFO_HEADER_OUT, ptr, headlen); - if(bodylen) - /* there was body data sent beyond the initial header part, pass that on - to the debug callback too */ - Curl_debug(data, CURLINFO_DATA_OUT, ptr + headlen, bodylen); - - /* 'amount' can never be a very large value here so typecasting it so a - signed 31 bit value should not cause problems even if ssize_t is - 64bit */ - *bytes_written += (long)amount; - - if(http) { - /* if we sent a piece of the body here, up the byte counter for it - accordingly */ - data->req.writebytecount += bodylen; - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - - if((size_t)amount != size) { - /* The whole request could not be sent in one system call. We must - queue it up and send it later when we get the chance. We must not - loop here and wait until it might work again. */ - - size -= amount; - - ptr = Curl_dyn_ptr(in) + amount; - - /* backup the currently set pointers */ - http->backup.fread_func = data->state.fread_func; - http->backup.fread_in = data->state.in; - http->backup.postdata = http->postdata; - http->backup.postsize = http->postsize; - http->backup.data = data; - - /* set the new pointers for the request-sending */ - data->state.fread_func = (curl_read_callback)readmoredata; - data->state.in = (void *)http; - http->postdata = ptr; - http->postsize = (curl_off_t)size; - - /* this much data is remaining header: */ - data->req.pendingheader = headersize - headlen; - - http->send_buffer = *in; /* copy the whole struct */ - http->sending = HTTPSEND_REQUEST; - return CURLE_OK; - } - http->sending = HTTPSEND_BODY; - /* the full buffer was sent, clean up and return */ - } - else { - if((size_t)amount != size) - /* We have no continue-send mechanism now, fail. This can only happen - when this function is used from the CONNECT sending function. We - currently (stupidly) assume that the whole request is always sent - away in the first single chunk. - - This needs FIXing. - */ - return CURLE_SEND_ERROR; - } - } - Curl_dyn_free(in); - - /* no remaining header data */ - data->req.pendingheader = 0; - return result; -} - -/* end of the add_buffer functions */ -/* ------------------------------------------------------------------------- */ - - - -/* * Curl_compareheader() * * Returns TRUE if 'headerline' contains the 'header' with given 'content'. @@ -1543,17 +1181,11 @@ data->state.authhost.multipass = FALSE; data->state.authproxy.multipass = FALSE; - /* set the proper values (possibly modified on POST) */ - conn->seek_func = data->set.seek_func; /* restore */ - conn->seek_client = data->set.seek_client; /* restore */ - if(!http) return CURLE_OK; - Curl_dyn_free(&http->send_buffer); Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); - Curl_ws_done(data); if(status) return status; @@ -1613,83 +1245,12 @@ } #endif -/* check and possibly add an Expect: header */ -static CURLcode expect100(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *req) -{ - CURLcode result = CURLE_OK; - if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) && - (conn->httpversion < 20)) { - /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an - Expect: 100-continue to the headers which actually speeds up post - operations (as there is one packet coming back from the web server) */ - const char *ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else { - result = Curl_dyn_addn(req, STRCONST("Expect: 100-continue\r\n")); - if(!result) - data->state.expect100header = TRUE; - } - } - - return result; -} - enum proxy_use { HEADER_SERVER, /* direct to server */ HEADER_PROXY, /* regular request to proxy */ HEADER_CONNECT /* sending CONNECT to a proxy */ }; -/* used to compile the provided trailers into one buffer - will return an error code if one of the headers is - not formatted correctly */ -CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, - struct dynbuf *b, - struct Curl_easy *handle) -{ - char *ptr = NULL; - CURLcode result = CURLE_OK; - const char *endofline_native = NULL; - const char *endofline_network = NULL; - - if( -#ifdef CURL_DO_LINEEND_CONV - (handle->state.prefer_ascii) || -#endif - (handle->set.crlf)) { - /* \n will become \r\n later on */ - endofline_native = "\n"; - endofline_network = "\x0a"; - } - else { - endofline_native = "\r\n"; - endofline_network = "\x0d\x0a"; - } - - while(trailers) { - /* only add correctly formatted trailers */ - ptr = strchr(trailers->data, ':'); - if(ptr && *(ptr + 1) == ' ') { - result = Curl_dyn_add(b, trailers->data); - if(result) - return result; - result = Curl_dyn_add(b, endofline_native); - if(result) - return result; - } - else - infof(handle, "Malformatted trailing header, skipping trailer"); - trailers = trailers->next; - } - result = Curl_dyn_add(b, endofline_network); - return result; -} - static bool hd_name_eq(const char *n1, size_t n1len, const char *n2, size_t n2len) { @@ -1808,7 +1369,7 @@ /* this header is sent later */ hd_name_eq(name, namelen, STRCONST("Content-Type:"))) ; - else if(conn->bits.authneg && + else if(data->req.authneg && /* while doing auth neg, don't allow the custom length since we will force length zero then */ hd_name_eq(name, namelen, STRCONST("Content-Length:"))) @@ -1954,7 +1515,7 @@ /* this header is sent later */ checkprefix("Content-Type:", compare)) ; - else if(conn->bits.authneg && + else if(data->req.authneg && /* while doing auth neg, don't allow the custom length since we will force length zero then */ checkprefix("Content-Length:", compare)) @@ -2335,18 +1896,17 @@ return result; } -CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, - Curl_HttpReq httpreq, const char **tep) +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) +static CURLcode set_post_reader(struct Curl_easy *data, Curl_HttpReq httpreq) { - CURLcode result = CURLE_OK; - const char *ptr; - struct HTTP *http = data->req.p.http; - http->postsize = 0; + CURLcode result; switch(httpreq) { +#ifndef CURL_DISABLE_MIME case HTTPREQ_POST_MIME: data->state.mimepost = &data->set.mimepost; break; +#endif #ifndef CURL_DISABLE_FORM_API case HTTPREQ_POST_FORM: /* Convert the form structure into a mime structure, then keep @@ -2368,34 +1928,164 @@ #endif default: data->state.mimepost = NULL; + break; } + switch(httpreq) { + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + /* This is form posting using mime data. */ #ifndef CURL_DISABLE_MIME - if(data->state.mimepost) { - const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); + if(data->state.mimepost) { + const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); - /* Read and seek body only. */ - data->state.mimepost->flags |= MIME_BODY_ONLY; + /* Read and seek body only. */ + data->state.mimepost->flags |= MIME_BODY_ONLY; - /* Prepare the mime structure headers & set content type. */ + /* Prepare the mime structure headers & set content type. */ - if(cthdr) - for(cthdr += 13; *cthdr == ' '; cthdr++) - ; - else if(data->state.mimepost->kind == MIMEKIND_MULTIPART) - cthdr = "multipart/form-data"; + if(cthdr) + for(cthdr += 13; *cthdr == ' '; cthdr++) + ; + else if(data->state.mimepost->kind == MIMEKIND_MULTIPART) + cthdr = "multipart/form-data"; - curl_mime_headers(data->state.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr, - NULL, MIMESTRATEGY_FORM); - curl_mime_headers(data->state.mimepost, NULL, 0); - if(!result) - result = Curl_mime_rewind(data->state.mimepost); - if(result) - return result; - http->postsize = Curl_mime_size(data->state.mimepost); - } + curl_mime_headers(data->state.mimepost, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr, + NULL, MIMESTRATEGY_FORM); + if(result) + return result; + curl_mime_headers(data->state.mimepost, NULL, 0); + result = Curl_creader_set_mime(data, data->state.mimepost); + if(result) + return result; + } + else #endif + { + result = Curl_creader_set_null(data); + } + data->state.infilesize = Curl_creader_total_length(data); + return result; + + default: + return Curl_creader_set_null(data); + } + /* never reached */ +} +#endif + +static CURLcode set_reader(struct Curl_easy *data, Curl_HttpReq httpreq) +{ + CURLcode result = CURLE_OK; + curl_off_t postsize = data->state.infilesize; + + DEBUGASSERT(data->conn); + + if(data->req.authneg) { + return Curl_creader_set_null(data); + } + + switch(httpreq) { + case HTTPREQ_PUT: /* Let's PUT the data to the server! */ + if(!postsize) + result = Curl_creader_set_null(data); + else + result = Curl_creader_set_fread(data, postsize); + return result; + +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + return set_post_reader(data, httpreq); +#endif + + case HTTPREQ_POST: + /* this is the simple POST, using x-www-form-urlencoded style */ + /* the size of the post body */ + if(!postsize) { + result = Curl_creader_set_null(data); + } + else if(data->set.postfields) { + if(postsize > 0) + result = Curl_creader_set_buf(data, data->set.postfields, + (size_t)postsize); + else + result = Curl_creader_set_null(data); + } + else { + /* we read the bytes from the callback. In case "chunked" encoding + * is forced by the application, we disregard `postsize`. This is + * a backward compatibility decision to earlier versions where + * chunking disregarded this. See issue #13229. */ + bool chunked = FALSE; + char *ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); + if(ptr) { + /* Some kind of TE is requested, check if 'chunked' is chosen */ + chunked = Curl_compareheader(ptr, STRCONST("Transfer-Encoding:"), + STRCONST("chunked")); + } + result = Curl_creader_set_fread(data, chunked? -1 : postsize); + } + return result; + + default: + /* HTTP GET/HEAD download, has no body, needs no Content-Length */ + data->state.infilesize = 0; + return Curl_creader_set_null(data); + } + /* not reached */ +} + +static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) +{ + if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) && + data->state.resume_from) { + /********************************************************************** + * Resuming upload in HTTP means that we PUT or POST and that we have + * got a resume_from value set. The resume value has already created + * a Range: header that will be passed along. We need to "fast forward" + * the file the given number of bytes and decrease the assume upload + * file size before we continue this venture in the dark lands of HTTP. + * Resuming mime/form posting at an offset > 0 has no sense and is ignored. + *********************************************************************/ + + if(data->state.resume_from < 0) { + /* + * This is meant to get the size of the present remote-file by itself. + * We don't support this now. Bail out! + */ + data->state.resume_from = 0; + } + + if(data->state.resume_from && !data->req.authneg) { + /* only act on the first request */ + CURLcode result; + result = Curl_creader_resume_from(data, data->state.resume_from); + if(result) { + failf(data, "Unable to resume from offset %" CURL_FORMAT_CURL_OFF_T, + data->state.resume_from); + return result; + } + } + } + return CURLE_OK; +} + +CURLcode Curl_http_req_set_reader(struct Curl_easy *data, + Curl_HttpReq httpreq, + const char **tep) +{ + CURLcode result = CURLE_OK; + const char *ptr; + + result = set_reader(data, httpreq); + if(result) + return result; + + result = http_resume(data, httpreq); + if(result) + return result; ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); if(ptr) { @@ -2403,20 +2093,23 @@ data->req.upload_chunky = Curl_compareheader(ptr, STRCONST("Transfer-Encoding:"), STRCONST("chunked")); + if(data->req.upload_chunky && + Curl_use_http_1_1plus(data, data->conn) && + (data->conn->httpversion >= 20)) { + infof(data, "suppressing chunked transfer encoding on connection " + "using HTTP version 2 or higher"); + data->req.upload_chunky = FALSE; + } } else { - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) && - http->postsize < 0) || - ((data->state.upload || httpreq == HTTPREQ_POST) && - data->state.infilesize == -1))) { - if(conn->bits.authneg) - /* don't enable chunked during auth neg */ - ; - else if(Curl_use_http_1_1plus(data, conn)) { - if(conn->httpversion < 20) - /* HTTP, upload, unknown file size and not HTTP 1.0 */ - data->req.upload_chunky = TRUE; + curl_off_t req_clen = Curl_creader_total_length(data); + + if(req_clen < 0) { + /* indeterminate request content length */ + if(Curl_use_http_1_1plus(data, data->conn)) { + /* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not + * need it */ + data->req.upload_chunky = (data->conn->httpversion < 20); } else { failf(data, "Chunky upload is not supported by HTTP 1.0"); @@ -2434,330 +2127,128 @@ return result; } -static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *r) +static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, + bool *announced_exp100) { - data->state.expect100header = FALSE; + CURLcode result; + char *ptr; + + *announced_exp100 = FALSE; /* Avoid Expect: 100-continue if Upgrade: is used */ - if(data->req.upgr101 == UPGR101_INIT) { - struct HTTP *http = data->req.p.http; - /* For really small puts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - char *ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), - STRCONST("100-continue")); + if(data->req.upgr101 != UPGR101_INIT) + return CURLE_OK; + + /* For really small puts we don't use Expect: headers at all, and for + the somewhat bigger ones we allow the app to disable it. Just make + sure that the expect100header is always set to the preferred value + here. */ + ptr = Curl_checkheaders(data, STRCONST("Expect")); + if(ptr) { + *announced_exp100 = + Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); + } + else if(!data->state.disableexpect && + Curl_use_http_1_1plus(data, data->conn) && + (data->conn->httpversion < 20)) { + /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an + Expect: 100-continue to the headers which actually speeds up post + operations (as there is one packet coming back from the web server) */ + curl_off_t client_len = Curl_creader_client_length(data); + if(client_len > EXPECT_100_THRESHOLD || client_len < 0) { + result = Curl_dyn_addn(r, STRCONST("Expect: 100-continue\r\n")); + if(result) + return result; + *announced_exp100 = TRUE; } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) - return expect100(data, conn, r); } return CURLE_OK; } -CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *r, Curl_HttpReq httpreq) +CURLcode Curl_http_req_complete(struct Curl_easy *data, + struct dynbuf *r, Curl_HttpReq httpreq) { -#ifndef USE_HYPER - /* Hyper always handles the body separately */ - curl_off_t included_body = 0; -#else - /* from this point down, this function should not be used */ -#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK -#endif CURLcode result = CURLE_OK; - struct HTTP *http = data->req.p.http; + curl_off_t req_clen; + bool announced_exp100 = FALSE; + DEBUGASSERT(data->conn); +#ifndef USE_HYPER + if(data->req.upload_chunky) { + result = Curl_httpchunk_add_reader(data); + if(result) + return result; + } +#endif + + /* Get the request body length that has been set up */ + req_clen = Curl_creader_total_length(data); switch(httpreq) { - case HTTPREQ_PUT: /* Let's PUT the data to the server! */ - - if(conn->bits.authneg) - http->postsize = 0; - else - http->postsize = data->state.infilesize; - - if((http->postsize != -1) && !data->req.upload_chunky && - (conn->bits.authneg || - !Curl_checkheaders(data, STRCONST("Content-Length")))) { - /* only add Content-Length if not uploading chunked */ - result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", http->postsize); - if(result) - return result; - } - - result = addexpect(data, conn, r); - if(result) - return result; - - /* end of headers */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize); - - /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending PUT request"); - else - /* prepare for transfer */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postsize?FIRSTSOCKET:-1); - if(result) - return result; - break; - + case HTTPREQ_PUT: + case HTTPREQ_POST: +#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) case HTTPREQ_POST_FORM: case HTTPREQ_POST_MIME: - /* This is form posting using mime data. */ - if(conn->bits.authneg) { - /* nothing to post! */ - result = Curl_dyn_addn(r, STRCONST("Content-Length: 0\r\n\r\n")); - if(result) - return result; - - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending POST request"); - else - /* setup variables for the upcoming transfer */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); - break; - } - - data->state.infilesize = http->postsize; - +#endif /* We only set Content-Length and allow a custom Content-Length if we don't upload data chunked, as RFC2616 forbids us to set both - kinds of headers (Transfer-Encoding: chunked and Content-Length) */ - if(http->postsize != -1 && !data->req.upload_chunky && - (!Curl_checkheaders(data, STRCONST("Content-Length")))) { + kinds of headers (Transfer-Encoding: chunked and Content-Length). + We do not override a custom "Content-Length" header, but during + authentication negotiation that header is suppressed. + */ + if(req_clen >= 0 && !data->req.upload_chunky && + (data->req.authneg || + !Curl_checkheaders(data, STRCONST("Content-Length")))) { /* we allow replacing this header if not during auth negotiation, although it isn't very wise to actually set your own */ result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", http->postsize); - if(result) - return result; + "\r\n", req_clen); } + if(result) + goto out; #ifndef CURL_DISABLE_MIME /* Output mime-generated headers. */ - { + if(data->state.mimepost && + ((httpreq == HTTPREQ_POST_FORM) || (httpreq == HTTPREQ_POST_MIME))) { struct curl_slist *hdr; for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) { result = Curl_dyn_addf(r, "%s\r\n", hdr->data); if(result) - return result; + goto out; } } #endif - - result = addexpect(data, conn, r); + if(httpreq == HTTPREQ_POST) { + if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { + result = Curl_dyn_addn(r, STRCONST("Content-Type: application/" + "x-www-form-urlencoded\r\n")); + if(result) + goto out; + } + } + result = addexpect(data, r, &announced_exp100); if(result) - return result; - - /* make the request end in a true CRLF */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize); - - /* Read from mime structure. */ - data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) data->state.mimepost; - http->sending = HTTPSEND_BODY; - - /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending POST request"); - else - /* prepare for transfer */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postsize?FIRSTSOCKET:-1); - if(result) - return result; - + goto out; break; - - case HTTPREQ_POST: - /* this is the simple POST, using x-www-form-urlencoded style */ - - if(conn->bits.authneg) - http->postsize = 0; - else - /* the size of the post body */ - http->postsize = data->state.infilesize; - - /* We only set Content-Length and allow a custom Content-Length if - we don't upload data chunked, as RFC2616 forbids us to set both - kinds of headers (Transfer-Encoding: chunked and Content-Length) */ - if((http->postsize != -1) && !data->req.upload_chunky && - (conn->bits.authneg || - !Curl_checkheaders(data, STRCONST("Content-Length")))) { - /* we allow replacing this header if not during auth negotiation, - although it isn't very wise to actually set your own */ - result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", http->postsize); - if(result) - return result; - } - - if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { - result = Curl_dyn_addn(r, STRCONST("Content-Type: application/" - "x-www-form-urlencoded\r\n")); - if(result) - return result; - } - - result = addexpect(data, conn, r); - if(result) - return result; - -#ifndef USE_HYPER - /* With Hyper the body is always passed on separately */ - if(data->set.postfields) { - if(!data->state.expect100header && - (http->postsize < MAX_INITIAL_POST_SIZE)) { - /* if we don't use expect: 100 AND - postsize is less than MAX_INITIAL_POST_SIZE - - then append the post data to the HTTP request header. This limit - is no magic limit but only set to prevent really huge POSTs to - get the data duplicated with malloc() and family. */ - - /* end of headers! */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - if(!data->req.upload_chunky) { - /* We're not sending it 'chunked', append it to the request - already now to reduce the number of send() calls */ - result = Curl_dyn_addn(r, data->set.postfields, - (size_t)http->postsize); - included_body = http->postsize; - } - else { - if(http->postsize) { - char chunk[16]; - /* Append the POST data chunky-style */ - msnprintf(chunk, sizeof(chunk), "%x\r\n", (int)http->postsize); - result = Curl_dyn_add(r, chunk); - if(!result) { - included_body = http->postsize + strlen(chunk); - result = Curl_dyn_addn(r, data->set.postfields, - (size_t)http->postsize); - if(!result) - result = Curl_dyn_addn(r, STRCONST("\r\n")); - included_body += 2; - } - } - if(!result) { - result = Curl_dyn_addn(r, STRCONST("\x30\x0d\x0a\x0d\x0a")); - /* 0 CR LF CR LF */ - included_body += 5; - } - } - if(result) - return result; - /* Make sure the progress information is accurate */ - Curl_pgrsSetUploadSize(data, http->postsize); - } - else { - /* A huge POST coming up, do data separate from the request */ - http->postdata = data->set.postfields; - http->sending = HTTPSEND_BODY; - http->backup.data = data; - data->state.fread_func = (curl_read_callback)readmoredata; - data->state.in = (void *)http; - - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize); - - /* end of headers! */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - } - } - else -#endif - { - /* end of headers! */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - if(data->req.upload_chunky && conn->bits.authneg) { - /* Chunky upload is selected and we're negotiating auth still, send - end-of-data only */ - result = Curl_dyn_addn(r, (char *)STRCONST("\x30\x0d\x0a\x0d\x0a")); - /* 0 CR LF CR LF */ - if(result) - return result; - } - - else if(data->state.infilesize) { - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize?http->postsize:-1); - - /* set the pointer to mark that we will send the post body using the - read callback, but only if we're not in authenticate negotiation */ - if(!conn->bits.authneg) - http->postdata = (char *)&http->postdata; - } - } - /* issue the request */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, included_body, - FIRSTSOCKET); - - if(result) - failf(data, "Failed sending HTTP POST request"); - else - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postdata?FIRSTSOCKET:-1); - break; - default: - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - /* issue the request */ - result = Curl_buffer_send(r, data, data->req.p.http, - &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending HTTP request"); -#ifdef USE_WEBSOCKETS - else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) && - !(data->set.connect_only)) - /* Set up the transfer for two-way since without CONNECT_ONLY set, this - request probably wants to send data too post upgrade */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); -#endif - else - /* HTTP GET/HEAD download: */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); + break; } + /* end of headers */ + result = Curl_dyn_addn(r, STRCONST("\r\n")); + if(!result) { + Curl_pgrsSetUploadSize(data, req_clen); + if(announced_exp100) + result = http_exp100_add_reader(data); + } + +out: + if(!result) { + /* setup variables for the upcoming transfer */ + Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); + } return result; } @@ -2857,7 +2348,7 @@ } else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && !Curl_checkheaders(data, STRCONST("Content-Range"))) { - + curl_off_t req_clen = Curl_creader_total_length(data); /* if a line like this was already allocated, free the previous one */ free(data->state.aptr.rangeline); @@ -2868,25 +2359,28 @@ data->state.aptr.rangeline = aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "\r\n", - data->state.infilesize - 1, data->state.infilesize); + req_clen - 1, req_clen); } else if(data->state.resume_from) { /* This is because "resume" was selected */ - curl_off_t total_expected_size = - data->state.resume_from + data->state.infilesize; + /* TODO: not sure if we want to send this header during authentication + * negotiation, but test1084 checks for it. In which case we have a + * "null" client reader installed that gives an unexpected length. */ + curl_off_t total_len = data->req.authneg? + data->state.infilesize : + (data->state.resume_from + req_clen); data->state.aptr.rangeline = aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "\r\n", - data->state.range, total_expected_size-1, - total_expected_size); + data->state.range, total_len-1, total_len); } else { /* Range was selected and then we just pass the incoming range and append total size */ data->state.aptr.rangeline = aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n", - data->state.range, data->state.infilesize); + data->state.range, req_clen); } if(!data->state.aptr.rangeline) return CURLE_OUT_OF_MEMORY; @@ -2895,100 +2389,17 @@ return CURLE_OK; } -CURLcode Curl_http_resume(struct Curl_easy *data, - struct connectdata *conn, - Curl_HttpReq httpreq) +CURLcode Curl_http_firstwrite(struct Curl_easy *data) { - if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) && - data->state.resume_from) { - /********************************************************************** - * Resuming upload in HTTP means that we PUT or POST and that we have - * got a resume_from value set. The resume value has already created - * a Range: header that will be passed along. We need to "fast forward" - * the file the given number of bytes and decrease the assume upload - * file size before we continue this venture in the dark lands of HTTP. - * Resuming mime/form posting at an offset > 0 has no sense and is ignored. - *********************************************************************/ - - if(data->state.resume_from < 0) { - /* - * This is meant to get the size of the present remote-file by itself. - * We don't support this now. Bail out! - */ - data->state.resume_from = 0; - } - - if(data->state.resume_from && !data->state.followlocation) { - /* only act on the first request */ - - /* Now, let's read off the proper amount of bytes from the - input. */ - int seekerr = CURL_SEEKFUNC_CANTSEEK; - if(conn->seek_func) { - Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - Curl_set_in_callback(data, false); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_READ_ERROR; - } - /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - do { - char scratch[4*1024]; - size_t readthisamountnow = - (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? - sizeof(scratch) : - curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread = - data->state.fread_func(scratch, 1, readthisamountnow, - data->state.in); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T - " bytes from the input", passed); - return CURLE_READ_ERROR; - } - } while(passed < data->state.resume_from); - } - - /* now, decrease the size of the read */ - if(data->state.infilesize>0) { - data->state.infilesize -= data->state.resume_from; - - if(data->state.infilesize <= 0) { - failf(data, "File already completely uploaded"); - return CURLE_PARTIAL_FILE; - } - } - /* we've passed, proceed as normal */ - } - } - return CURLE_OK; -} - -CURLcode Curl_http_firstwrite(struct Curl_easy *data, - struct connectdata *conn, - bool *done) -{ + struct connectdata *conn = data->conn; struct SingleRequest *k = &data->req; - *done = FALSE; if(data->req.newurl) { if(conn->bits.close) { /* Abort after the headers if "follow Location" is set and we're set to close anyway. */ k->keepon &= ~KEEP_RECV; - *done = TRUE; + k->done = TRUE; return CURLE_OK; } /* We have a new url to load, but since we want to be able to reuse this @@ -3007,7 +2418,7 @@ streamclose(conn, "already downloaded"); /* Abort download */ k->keepon &= ~KEEP_RECV; - *done = TRUE; + k->done = TRUE; return CURLE_OK; } @@ -3025,7 +2436,7 @@ action for an HTTP/1.1 client */ if(!Curl_meets_timecondition(data, k->timeofdoc)) { - *done = TRUE; + k->done = TRUE; /* We're simulating an HTTP 304 from server so we return what should have been returned from the server */ data->info.httpcode = 304; @@ -3083,7 +2494,6 @@ { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - struct HTTP *http; Curl_HttpReq httpreq; const char *te = ""; /* transfer-encoding */ const char *request; @@ -3128,8 +2538,12 @@ break; } - http = data->req.p.http; - DEBUGASSERT(http); + /* Add collecting of headers written to client. For a new connection, + * we might have done that already, but reuse + * or multiplex needs it here as well. */ + result = Curl_headers_init(data); + if(result) + goto fail; result = Curl_http_host(data, conn); if(result) @@ -3181,17 +2595,13 @@ goto fail; #endif - result = Curl_http_body(data, conn, httpreq, &te); + result = Curl_http_req_set_reader(data, httpreq, &te); if(result) goto fail; p_accept = Curl_checkheaders(data, STRCONST("Accept"))?NULL:"Accept: */*\r\n"; - result = Curl_http_resume(data, conn, httpreq); - if(result) - goto fail; - result = Curl_http_range(data, httpreq); if(result) goto fail; @@ -3243,8 +2653,12 @@ httpstring, (data->state.aptr.host?data->state.aptr.host:""), +#ifndef CURL_DISABLE_PROXY data->state.aptr.proxyuserpwd? data->state.aptr.proxyuserpwd:"", +#else + "", +#endif data->state.aptr.userpwd?data->state.aptr.userpwd:"", (data->state.use_range && data->state.aptr.rangeline)? data->state.aptr.rangeline:"", @@ -3278,7 +2692,9 @@ /* 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 free(altused); if(result) { @@ -3309,46 +2725,14 @@ result = Curl_add_custom_headers(data, FALSE, &req); if(!result) { - http->postdata = NULL; /* nothing to post at this point */ - if((httpreq == HTTPREQ_GET) || - (httpreq == HTTPREQ_HEAD)) - Curl_pgrsSetUploadSize(data, 0); /* nothing */ - - /* bodysend takes ownership of the 'req' memory on success */ - result = Curl_http_bodysend(data, conn, &req, httpreq); + /* req_send takes ownership of the 'req' memory on success */ + result = Curl_http_req_complete(data, &req, httpreq); + if(!result) + result = Curl_req_send(data, &req); } - if(result) { - Curl_dyn_free(&req); + Curl_dyn_free(&req); + if(result) goto fail; - } - - if((http->postsize > -1) && - (http->postsize <= data->req.writebytecount) && - (http->sending != HTTPSEND_REQUEST)) - data->req.upload_done = TRUE; - - if(data->req.writebytecount) { - /* if a request-body has been sent off, we make sure this progress is noted - properly */ - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - - if(!http->postsize) { - /* already sent the entire request body, mark the "upload" as - complete */ - infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T - " out of %" CURL_FORMAT_CURL_OFF_T " bytes", - data->req.writebytecount, http->postsize); - data->req.upload_done = TRUE; - data->req.keepon &= ~KEEP_SEND; /* we're done writing */ - data->req.exp100 = EXP100_SEND_DATA; /* already sent */ - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - } - } - - if(data->req.upload_done) - Curl_conn_ev_data_done_send(data); if((conn->httpversion >= 20) && data->req.upload_chunky) /* upload_chunky was set above to set up the request in a chunky fashion, @@ -3433,325 +2817,373 @@ return checkhttpprefix(data, s, len); } +/* 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))) + +#define HD_VAL(hd, hdlen, n) \ + ((((hdlen) >= (sizeof(n)-1)) && \ + 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) */ +#define HD_IS_AND_SAYS(hd, hdlen, n, v) \ + (HD_IS(hd, hdlen, n) && \ + ((hdlen) > ((sizeof(n)-1) + (sizeof(v)-1))) && \ + Curl_compareheader(hd, STRCONST(n), STRCONST(v))) + /* * Curl_http_header() parses a single response header. */ -CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, - char *headp) +CURLcode Curl_http_header(struct Curl_easy *data, + const char *hd, size_t hdlen) { + struct connectdata *conn = data->conn; CURLcode result; struct SingleRequest *k = &data->req; - /* Check for Content-Length: header lines to get size */ - if(!k->http_bodyless && - !data->set.ignorecl && checkprefix("Content-Length:", headp)) { - curl_off_t contentlength; - CURLofft offt = curlx_strtoofft(headp + strlen("Content-Length:"), - NULL, 10, &contentlength); + const char *v; - if(offt == CURL_OFFT_OK) { - k->size = contentlength; - k->maxdownload = k->size; - } - else if(offt == CURL_OFFT_FLOW) { - /* out of range */ - if(data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - streamclose(conn, "overflow content-length"); - infof(data, "Overflow Content-Length: value"); - } - else { - /* negative or just rubbish - bad HTTP */ - failf(data, "Invalid Content-Length: value"); - return CURLE_WEIRD_SERVER_REPLY; - } - } - /* check for Content-Type: header lines to get the MIME-type */ - else if(checkprefix("Content-Type:", headp)) { - char *contenttype = Curl_copy_header_value(headp); - if(!contenttype) - return CURLE_OUT_OF_MEMORY; - if(!*contenttype) - /* ignore empty data */ - free(contenttype); - else { - Curl_safefree(data->info.contenttype); - data->info.contenttype = contenttype; - } - } -#ifndef CURL_DISABLE_PROXY - else if((conn->httpversion == 10) && - conn->bits.httpproxy && - Curl_compareheader(headp, - STRCONST("Proxy-Connection:"), - STRCONST("keep-alive"))) { - /* - * When an HTTP/1.0 reply comes when using a proxy, the - * 'Proxy-Connection: keep-alive' line tells us the - * connection will be kept alive for our pleasure. - * Default action for 1.0 is to close. - */ - connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */ - infof(data, "HTTP/1.0 proxy connection set to keep alive"); - } - else if((conn->httpversion == 11) && - conn->bits.httpproxy && - Curl_compareheader(headp, - STRCONST("Proxy-Connection:"), - STRCONST("close"))) { - /* - * We get an HTTP/1.1 response from a proxy and it says it'll - * close down after this transfer. - */ - connclose(conn, "Proxy-Connection: asked to close after done"); - infof(data, "HTTP/1.1 proxy connection set close"); - } + switch(hd[0]) { + case 'a': + case 'A': +#ifndef CURL_DISABLE_ALTSVC + v = (data->asi && + ((data->conn->handler->flags & PROTOPT_SSL) || +#ifdef CURLDEBUG + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 #endif - else if((conn->httpversion == 10) && - Curl_compareheader(headp, - STRCONST("Connection:"), - STRCONST("keep-alive"))) { - /* - * An HTTP/1.0 reply with the 'Connection: keep-alive' line - * tells us the connection will be kept alive for our - * pleasure. Default action for 1.0 is to close. - * - * [RFC2068, section 19.7.1] */ - connkeep(conn, "Connection keep-alive"); - infof(data, "HTTP/1.0 connection set to keep alive"); - } - else if(Curl_compareheader(headp, - STRCONST("Connection:"), STRCONST("close"))) { - /* - * [RFC 2616, section 8.1.2.1] - * "Connection: close" is HTTP/1.1 language and means that - * the connection will close when this request has been - * served. - */ - streamclose(conn, "Connection: close used"); - } - else if(!k->http_bodyless && checkprefix("Transfer-Encoding:", headp)) { - /* One or more encodings. We check for chunked and/or a compression - algorithm. */ - /* - * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding - * means that the server will send a series of "chunks". Each - * chunk starts with line with info (including size of the - * coming block) (terminated with CRLF), then a block of data - * with the previously mentioned size. There can be any amount - * of chunks, and a chunk-data set to zero signals the - * end-of-chunks. */ - - result = Curl_build_unencoding_stack(data, - headp + strlen("Transfer-Encoding:"), - TRUE); - if(result) - return result; - if(!k->chunk && data->set.http_transfer_encoding) { - /* if this isn't chunked, only close can signal the end of this transfer - as Content-Length is said not to be trusted for transfer-encoding! */ - connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); - k->ignore_cl = TRUE; + ))? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL; + if(v) { + /* the ALPN of the current request */ + enum alpnid id = (conn->httpversion == 30)? ALPN_h3 : + (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, + curlx_uitous((unsigned int)conn->remote_port)); } - } - else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) && - data->set.str[STRING_ENCODING]) { - /* - * 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 - */ - result = Curl_build_unencoding_stack(data, - headp + strlen("Content-Encoding:"), - FALSE); - if(result) - return result; - } - else if(checkprefix("Retry-After:", headp)) { - /* Retry-After = HTTP-date / delay-seconds */ - curl_off_t retry_after = 0; /* zero for unknown or "now" */ - /* Try it as a decimal number, if it works it is not a date */ - (void)curlx_strtoofft(headp + strlen("Retry-After:"), - NULL, 10, &retry_after); - if(!retry_after) { - time_t date = Curl_getdate_capped(headp + strlen("Retry-After:")); - if(-1 != date) - /* convert date to number of seconds into the future */ - retry_after = date - time(NULL); - } - data->info.retry_after = retry_after; /* store it */ - } - else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) { - /* Content-Range: bytes [num]- - Content-Range: bytes: [num]- - Content-Range: [num]- - Content-Range: [asterisk]/[total] - - The second format was added since Sun's webserver - JavaWebServer/1.1.1 obviously sends the header this way! - The third added since some servers use that! - The fourth means the requested range was unsatisfied. - */ - - char *ptr = headp + strlen("Content-Range:"); - - /* Move forward until first digit or asterisk */ - while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') - ptr++; - - /* if it truly stopped on a digit */ - if(ISDIGIT(*ptr)) { - if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) { - if(data->state.resume_from == k->offset) - /* we asked for a resume and we got it */ - k->content_range = TRUE; - } - } - else if(k->httpcode < 300) - data->state.resume_from = 0; /* get everything */ - } -#if !defined(CURL_DISABLE_COOKIES) - else if(data->cookies && data->state.cookie_engine && - checkprefix("Set-Cookie:", headp)) { - /* If there is a custom-set Host: name, use it here, or else use real peer - host name. */ - const char *host = data->state.aptr.cookiehost? - data->state.aptr.cookiehost:conn->host.name; - const bool secure_context = - conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || - strcasecompare("localhost", host) || - !strcmp(host, "127.0.0.1") || - !strcmp(host, "::1") ? TRUE : FALSE; - - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, - CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_add(data, data->cookies, TRUE, FALSE, - headp + strlen("Set-Cookie:"), host, - data->state.up.path, secure_context); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } #endif - else if(!k->http_bodyless && checkprefix("Last-Modified:", headp) && - (data->set.timecondition || data->set.get_filetime) ) { - k->timeofdoc = Curl_getdate_capped(headp + strlen("Last-Modified:")); - if(data->set.get_filetime) - data->info.filetime = k->timeofdoc; - } - else if((checkprefix("WWW-Authenticate:", headp) && - (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", headp) && - (407 == k->httpcode))) { + break; + case 'c': + case 'C': + /* Check for Content-Length: header lines to get size */ + v = (!k->http_bodyless && !data->set.ignorecl)? + HD_VAL(hd, hdlen, "Content-Length:") : NULL; + if(v) { + curl_off_t contentlength; + CURLofft offt = curlx_strtoofft(v, NULL, 10, &contentlength); - bool proxy = (k->httpcode == 407) ? TRUE : FALSE; - char *auth = Curl_copy_header_value(headp); - if(!auth) - return CURLE_OUT_OF_MEMORY; - - result = Curl_http_input_auth(data, proxy, auth); - - free(auth); - - if(result) - return result; - } -#ifdef USE_SPNEGO - else if(checkprefix("Persistent-Auth:", headp)) { - struct negotiatedata *negdata = &conn->negotiate; - struct auth *authp = &data->state.authhost; - if(authp->picked == CURLAUTH_NEGOTIATE) { - char *persistentauth = Curl_copy_header_value(headp); - if(!persistentauth) + if(offt == CURL_OFFT_OK) { + k->size = contentlength; + k->maxdownload = k->size; + } + else if(offt == CURL_OFFT_FLOW) { + /* out of range */ + if(data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + streamclose(conn, "overflow content-length"); + infof(data, "Overflow Content-Length: value"); + } + else { + /* negative or just rubbish - bad HTTP */ + failf(data, "Invalid Content-Length: value"); + return CURLE_WEIRD_SERVER_REPLY; + } + return CURLE_OK; + } + v = (!k->http_bodyless && data->set.str[STRING_ENCODING])? + 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 + */ + return Curl_build_unencoding_stack(data, v, FALSE); + } + /* check for Content-Type: header lines to get the MIME-type */ + v = HD_VAL(hd, hdlen, "Content-Type:"); + if(v) { + char *contenttype = Curl_copy_header_value(hd); + if(!contenttype) return CURLE_OUT_OF_MEMORY; - negdata->noauthpersist = checkprefix("false", persistentauth)? - TRUE:FALSE; - negdata->havenoauthpersist = TRUE; - infof(data, "Negotiate: noauthpersist -> %d, header part: %s", - negdata->noauthpersist, persistentauth); - free(persistentauth); + if(!*contenttype) + /* ignore empty data */ + free(contenttype); + else { + Curl_safefree(data->info.contenttype); + data->info.contenttype = contenttype; + } + return CURLE_OK; } - } -#endif - else if((k->httpcode >= 300 && k->httpcode < 400) && - checkprefix("Location:", headp) && - !data->req.location) { - /* this is the URL that the server advises us to use instead */ - char *location = Curl_copy_header_value(headp); - if(!location) - return CURLE_OUT_OF_MEMORY; - if(!*location) - /* ignore empty data */ - free(location); - else { - data->req.location = location; + if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) { + /* + * [RFC 2616, section 8.1.2.1] + * "Connection: close" is HTTP/1.1 language and means that + * the connection will close when this request has been + * served. + */ + streamclose(conn, "Connection: close used"); + return CURLE_OK; + } + if((conn->httpversion == 10) && + HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) { + /* + * An HTTP/1.0 reply with the 'Connection: keep-alive' line + * tells us the connection will be kept alive for our + * pleasure. Default action for 1.0 is to close. + * + * [RFC2068, section 19.7.1] */ + connkeep(conn, "Connection keep-alive"); + infof(data, "HTTP/1.0 connection set to keep alive"); + return CURLE_OK; + } + v = !k->http_bodyless? HD_VAL(hd, hdlen, "Content-Range:") : NULL; + if(v) { + /* Content-Range: bytes [num]- + Content-Range: bytes: [num]- + Content-Range: [num]- + Content-Range: [asterisk]/[total] - if(data->set.http_follow_location) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->req.location); /* clone */ - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; + The second format was added since Sun's webserver + JavaWebServer/1.1.1 obviously sends the header this way! + The third added since some servers use that! + The fourth means the requested range was unsatisfied. + */ - /* some cases of POST and PUT etc needs to rewind the data - stream at this point */ - result = http_perhapsrewind(data, conn); - if(result) - return result; + const char *ptr = v; - /* mark the next request as a followed location: */ - data->state.this_is_a_follow = TRUE; + /* Move forward until first digit or asterisk */ + while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') + ptr++; + + /* if it truly stopped on a digit */ + if(ISDIGIT(*ptr)) { + if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) { + if(data->state.resume_from == k->offset) + /* we asked for a resume and we got it */ + k->content_range = TRUE; + } + } + else if(k->httpcode < 300) + data->state.resume_from = 0; /* get everything */ + } + break; + case 'l': + case 'L': + v = (!k->http_bodyless && + (data->set.timecondition || data->set.get_filetime))? + HD_VAL(hd, hdlen, "Last-Modified:") : NULL; + if(v) { + k->timeofdoc = Curl_getdate_capped(v); + if(data->set.get_filetime) + data->info.filetime = k->timeofdoc; + return CURLE_OK; + } + if((k->httpcode >= 300 && k->httpcode < 400) && + HD_IS(hd, hdlen, "Location:") && + !data->req.location) { + /* this is the URL that the server advises us to use instead */ + char *location = Curl_copy_header_value(hd); + if(!location) + return CURLE_OUT_OF_MEMORY; + if(!*location) + /* ignore empty data */ + free(location); + else { + data->req.location = location; + + if(data->set.http_follow_location) { + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->req.location); /* clone */ + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + + /* some cases of POST and PUT etc needs to rewind the data + stream at this point */ + result = http_perhapsrewind(data, conn); + if(result) + return result; + + /* mark the next request as a followed location: */ + data->state.this_is_a_follow = TRUE; + } } } - } + break; + case 'p': + case 'P': +#ifndef CURL_DISABLE_PROXY + v = HD_VAL(hd, hdlen, "Proxy-Connection:"); + if(v) { + if((conn->httpversion == 10) && conn->bits.httpproxy && + HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { + /* + * When an HTTP/1.0 reply comes when using a proxy, the + * 'Proxy-Connection: keep-alive' line tells us the + * connection will be kept alive for our pleasure. + * Default action for 1.0 is to close. + */ + connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */ + infof(data, "HTTP/1.0 proxy connection set to keep alive"); + } + else if((conn->httpversion == 11) && conn->bits.httpproxy && + HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { + /* + * We get an HTTP/1.1 response from a proxy and it says it'll + * close down after this transfer. + */ + connclose(conn, "Proxy-Connection: asked to close after done"); + infof(data, "HTTP/1.1 proxy connection set close"); + } + return CURLE_OK; + } +#endif + if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) { + char *auth = Curl_copy_header_value(hd); + if(!auth) + return CURLE_OUT_OF_MEMORY; + result = Curl_http_input_auth(data, TRUE, auth); + free(auth); + return result; + } +#ifdef USE_SPNEGO + if(HD_IS(hd, hdlen, "Persistent-Auth:")) { + struct negotiatedata *negdata = &conn->negotiate; + struct auth *authp = &data->state.authhost; + if(authp->picked == CURLAUTH_NEGOTIATE) { + char *persistentauth = Curl_copy_header_value(hd); + if(!persistentauth) + return CURLE_OUT_OF_MEMORY; + negdata->noauthpersist = checkprefix("false", persistentauth)? + TRUE:FALSE; + negdata->havenoauthpersist = TRUE; + infof(data, "Negotiate: noauthpersist -> %d, header part: %s", + negdata->noauthpersist, persistentauth); + free(persistentauth); + } + } +#endif + break; + case 'r': + case 'R': + v = HD_VAL(hd, hdlen, "Retry-After:"); + if(v) { + /* Retry-After = HTTP-date / delay-seconds */ + curl_off_t retry_after = 0; /* zero for unknown or "now" */ + /* Try it as a decimal number, if it works it is not a date */ + (void)curlx_strtoofft(v, NULL, 10, &retry_after); + if(!retry_after) { + time_t date = Curl_getdate_capped(v); + if(-1 != date) + /* convert date to number of seconds into the future */ + retry_after = date - time(NULL); + } + data->info.retry_after = retry_after; /* store it */ + return CURLE_OK; + } + break; + case 's': + case 'S': +#if !defined(CURL_DISABLE_COOKIES) + v = (data->cookies && data->state.cookie_engine)? + HD_VAL(hd, hdlen, "Set-Cookie:") : NULL; + if(v) { + /* If there is a custom-set Host: name, use it here, or else use + * real peer host name. */ + const char *host = data->state.aptr.cookiehost? + data->state.aptr.cookiehost:conn->host.name; + const bool secure_context = + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || + strcasecompare("localhost", host) || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "::1") ? TRUE : FALSE; + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, + CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, + data->state.up.path, secure_context); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + return CURLE_OK; + } +#endif #ifndef CURL_DISABLE_HSTS - /* If enabled, the header is incoming and this is over HTTPS */ - else if(data->hsts && checkprefix("Strict-Transport-Security:", headp) && - ((conn->handler->flags & PROTOPT_SSL) || + /* If enabled, the header is incoming and this is over HTTPS */ + v = (data->hsts && + ((conn->handler->flags & PROTOPT_SSL) || #ifdef CURLDEBUG /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") #else 0 #endif - )) { - CURLcode check = - Curl_hsts_parse(data->hsts, conn->host.name, - headp + strlen("Strict-Transport-Security:")); - if(check) - infof(data, "Illegal STS header skipped"); + ) + )? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; + if(v) { + CURLcode check = + Curl_hsts_parse(data->hsts, conn->host.name, v); + if(check) + infof(data, "Illegal STS header skipped"); #ifdef DEBUGBUILD - else - infof(data, "Parsed STS header fine (%zu entries)", - data->hsts->list.size); + else + infof(data, "Parsed STS header fine (%zu entries)", + data->hsts->list.size); #endif - } + } #endif -#ifndef CURL_DISABLE_ALTSVC - /* If enabled, the header is incoming and this is over HTTPS */ - else if(data->asi && checkprefix("Alt-Svc:", headp) && - ((conn->handler->flags & PROTOPT_SSL) || -#ifdef CURLDEBUG - /* allow debug builds to circumvent the HTTPS restriction */ - getenv("CURL_ALTSVC_HTTP") -#else - 0 -#endif - )) { - /* the ALPN of the current request */ - enum alpnid id = (conn->httpversion == 30)? ALPN_h3 : - (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; - result = Curl_altsvc_parse(data, data->asi, - headp + strlen("Alt-Svc:"), - id, conn->host.name, - curlx_uitous((unsigned int)conn->remote_port)); - if(result) + break; + case 't': + case 'T': + /* RFC 9112, ch. 6.1 + * "Transfer-Encoding MAY be sent in a response to a HEAD request or + * in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a + * GET request, neither of which includes a message body, to indicate + * that the origin server would have applied a transfer coding to the + * message body if the request had been an unconditional GET." + * + * Read: in these cases the 'Transfer-Encoding' does not apply + * to any data following the response headers. Do not add any decoders. + */ + v = (!k->http_bodyless && + (data->state.httpreq != HTTPREQ_HEAD) && + (k->httpcode != 304))? + HD_VAL(hd, hdlen, "Transfer-Encoding:") : NULL; + if(v) { + /* One or more encodings. We check for chunked and/or a compression + algorithm. */ + result = Curl_build_unencoding_stack(data, v, TRUE); + if(result) + return result; + if(!k->chunk && data->set.http_transfer_encoding) { + /* if this isn't chunked, only close can signal the end of this + * transfer as Content-Length is said not to be trusted for + * transfer-encoding! */ + connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); + k->ignore_cl = TRUE; + } + return CURLE_OK; + } + break; + case 'w': + case 'W': + if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) { + char *auth = Curl_copy_header_value(hd); + if(!auth) + return CURLE_OUT_OF_MEMORY; + result = Curl_http_input_auth(data, FALSE, auth); + free(auth); return result; + } + break; } -#endif - else if(conn->handler->protocol & CURLPROTO_RTSP) { - result = Curl_rtsp_parseheader(data, headp); + + if(conn->handler->protocol & CURLPROTO_RTSP) { + result = Curl_rtsp_parseheader(data, hd); if(result) return result; } @@ -3762,18 +3194,41 @@ * Called after the first HTTP response line (the status line) has been * received and parsed. */ - CURLcode Curl_http_statusline(struct Curl_easy *data, struct connectdata *conn) { struct SingleRequest *k = &data->req; - data->info.httpcode = k->httpcode; - data->info.httpversion = conn->httpversion; - if(!data->state.httpversion || - data->state.httpversion > conn->httpversion) + switch(k->httpversion) { + case 10: + case 11: +#ifdef USE_HTTP2 + case 20: +#endif +#ifdef USE_HTTP3 + case 30: +#endif + /* no major version switch mid-connection */ + if(conn->httpversion && + (k->httpversion/10 != conn->httpversion/10)) { + failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", + conn->httpversion/10, k->httpversion/10); + return CURLE_UNSUPPORTED_PROTOCOL; + } + break; + default: + failf(data, "Unsupported HTTP version (%u.%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 = (unsigned char)k->httpversion; + + if(!data->state.httpversion || data->state.httpversion > k->httpversion) /* store the lowest server version we encounter */ - data->state.httpversion = conn->httpversion; + data->state.httpversion = (unsigned char)k->httpversion; /* * This code executes as part of processing the header. As a @@ -3790,26 +3245,20 @@ k->ignorebody = TRUE; /* Avoid appending error msg to good data. */ } - if(conn->httpversion == 10) { + if(k->httpversion == 10) { /* Default action for HTTP/1.0 must be to close, unless we get one of those fancy headers that tell us the server keeps it open for us! */ infof(data, "HTTP 1.0, assume close after body"); connclose(conn, "HTTP/1.0 close after body"); } - else if(conn->httpversion == 20 || + else if(k->httpversion == 20 || (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) { DEBUGF(infof(data, "HTTP/2 found, allow multiplexing")); /* HTTP/2 cannot avoid multiplexing since it is a core functionality of the protocol */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; } - else if(conn->httpversion >= 11 && - !conn->bits.close) { - /* If HTTP version is >= 1.1 and connection is persistent */ - DEBUGF(infof(data, - "HTTP 1.1 or later with persistent connection")); - } k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; switch(k->httpcode) { @@ -3860,12 +3309,11 @@ return CURLE_OK; } -static CURLcode verify_header(struct Curl_easy *data) +static CURLcode verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen) { struct SingleRequest *k = &data->req; - const char *header = Curl_dyn_ptr(&data->state.headerb); - size_t hlen = Curl_dyn_len(&data->state.headerb); - char *ptr = memchr(header, 0x00, hlen); + char *ptr = memchr(hd, 0x00, hdlen); if(ptr) { /* this is bad, bail out */ failf(data, "Nul byte in header"); @@ -3874,11 +3322,11 @@ if(k->headerline < 2) /* the first "header" is the status-line and it has no colon */ return CURLE_OK; - if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2) + if(((hd[0] == ' ') || (hd[0] == '\t')) && k->headerline > 2) /* line folding, can't happen on line 2 */ ; else { - ptr = memchr(header, ':', hlen); + ptr = memchr(hd, ':', hdlen); if(!ptr) { /* this is bad, bail out */ failf(data, "Header without colon"); @@ -3916,29 +3364,477 @@ } -/* - * Read any HTTP header lines from the server and pass them to the client app. - */ -static CURLcode http_rw_headers(struct Curl_easy *data, - const char *buf, size_t blen, - size_t *pconsumed) +static CURLcode http_on_response(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed) { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - char *headp; + + (void)buf; /* not used without HTTP2 enabled */ + *pconsumed = 0; + + if(k->upgr101 == UPGR101_RECEIVED) { + /* supposedly upgraded to http2 now */ + if(conn->httpversion != 20) + infof(data, "Lying server, not serving HTTP/2"); + } + if(conn->httpversion < 20) { + conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; + } + + if(k->httpcode < 100) { + failf(data, "Unsupported response code in HTTP response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + 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. + */ + Curl_http_exp100_got100(data); + break; + case 101: + /* Switching Protocols only allowed from HTTP/1.1 */ + if(conn->httpversion != 11) { + /* invalid for other HTTP versions */ + failf(data, "unexpected 101 response code"); + return CURLE_WEIRD_SERVER_REPLY; + } + if(k->upgr101 == UPGR101_H2) { + /* Switching to HTTP/2, where we will get more responses */ + infof(data, "Received 101, Switching to HTTP/2"); + k->upgr101 = UPGR101_RECEIVED; + /* We expect more response from HTTP/2 later */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + /* Any remaining `buf` bytes are already HTTP/2 and passed to + * be processed. */ + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); + if(result) + return result; + *pconsumed += blen; + } +#ifdef USE_WEBSOCKETS + else if(k->upgr101 == UPGR101_WS) { + /* verify the response. Any passed `buf` bytes are already in + * WebSockets format and taken in by the protocol handler. */ + result = Curl_ws_accept(data, buf, blen); + if(result) + return result; + *pconsumed += blen; /* ws accept handled the data */ + k->header = FALSE; /* we will not get more responses */ + if(data->set.connect_only) + k->keepon &= ~KEEP_RECV; /* read no more content */ + } +#endif + else { + /* We silently accept this as the final response. + * TODO: this looks, uhm, wrong. 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; + 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; + } + return result; + } + + /* k->httpcode >= 200, final response */ + k->header = FALSE; + + if(k->upgr101 == UPGR101_H2) { + /* A requested upgrade was denied, poke the multi handle to possibly + allow a pending pipewait to continue */ + Curl_multi_connchanged(data->multi); + } + + if((k->size == -1) && !k->chunk && !conn->bits.close && + (conn->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"); + } + + /* At this point we have some idea about the fate of the connection. + If we are closing the connection it may result auth failure. */ +#if defined(USE_NTLM) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || + ((data->req.httpcode == 407) && + (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); + data->state.authproblem = TRUE; + } +#endif +#if defined(USE_SPNEGO) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->http_negotiate_state == GSS_AUTHRECV)) || + ((data->req.httpcode == 407) && + (conn->proxy_negotiate_state == GSS_AUTHRECV)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); + data->state.authproblem = TRUE; + } + if((conn->http_negotiate_state == GSS_AUTHDONE) && + (data->req.httpcode != 401)) { + conn->http_negotiate_state = GSS_AUTHSUCC; + } + if((conn->proxy_negotiate_state == GSS_AUTHDONE) && + (data->req.httpcode != 407)) { + conn->proxy_negotiate_state = GSS_AUTHSUCC; + } +#endif + +#ifdef USE_WEBSOCKETS + /* All >=200 HTTP status codes are errors when wanting websockets */ + if(data->req.upgr101 == UPGR101_WS) { + failf(data, "Refused WebSockets upgrade: %d", k->httpcode); + return CURLE_HTTP_RETURNED_ERROR; + } +#endif + + /* 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); + return CURLE_HTTP_RETURNED_ERROR; + } + + /* 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) + return result; + + 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've 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've read the entire response. + */ + if(!Curl_req_done_sending(data)) { + if((k->httpcode == 417) && Curl_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; + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->state.url); + Curl_req_abort_sending(data); + } + 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"); + k->keepon |= KEEP_SEND; + } + + } + + /* This is the last response that we will got for the current request. + * Check on the body size and determine if the response is complete. + */ + result = Curl_http_size(data); + if(result) + return result; + + /* If we requested a "no body", this is a good time to get + * out and return home. + */ + 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'd + 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(0 == k->maxdownload + && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) + && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) + k->download_done = TRUE; + + /* final response without error, prepare to receive the body */ + return Curl_http_firstwrite(data); +} + +static CURLcode http_rw_hd(struct Curl_easy *data, + const char *hd, size_t hdlen, + const char *buf_remain, size_t blen, + size_t *pconsumed) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + int writetype; + + *pconsumed = 0; + if((0x0a == *hd) || (0x0d == *hd)) { + /* Empty header line means end of headers! */ + size_t consumed; + + /* now, only output this if the header AND body are requested: + */ + Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen); + + writetype = CLIENTWRITE_HEADER | + ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); + + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + data->req.deductheadercount = + (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0; + + /* analyze the response to find out what to do. */ + /* Caveat: we clear anything in the header brigade, because a + * response might switch HTTP version which may call use recursively. + * Not nice, but that is currently the way of things. */ + Curl_dyn_reset(&data->state.headerb); + result = http_on_response(data, buf_remain, blen, &consumed); + if(result) + return result; + *pconsumed += consumed; + return CURLE_OK; + } + + /* + * Checks for special headers coming up. + */ + + writetype = CLIENTWRITE_HEADER; + if(!k->headerline++) { + /* This is the first header, it MUST be the error code line + or else we consider this to be the body right away! */ + bool fine_statusline = FALSE; + + k->httpversion = 0; /* Don't know yet */ + if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { + /* + * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 + * + * The response code is always a three-digit number in HTTP as the spec + * says. We allow any three-digit number here, but we cannot make + * guarantees on future behaviors since it isn't within the protocol. + */ + const char *p = hd; + + while(*p && ISBLANK(*p)) + p++; + if(!strncmp(p, "HTTP/", 5)) { + p += 5; + switch(*p) { + case '1': + p++; + if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { + if(ISBLANK(p[2])) { + k->httpversion = 10 + (p[1] - '0'); + p += 3; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(ISSPACE(*p)) + fine_statusline = TRUE; + } + } + } + if(!fine_statusline) { + failf(data, "Unsupported HTTP/1 subversion in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + break; + case '2': + case '3': + if(!ISBLANK(p[1])) + break; + k->httpversion = (*p - '0') * 10; + p += 2; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(!ISSPACE(*p)) + break; + fine_statusline = TRUE; + } + break; + default: /* unsupported */ + failf(data, "Unsupported HTTP version in response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + } + + if(!fine_statusline) { + /* If user has set option HTTP200ALIASES, + compare header line against list of aliases + */ + statusline check = checkhttpprefix(data, hd, hdlen); + if(check == STATUS_DONE) { + fine_statusline = TRUE; + k->httpcode = 200; + k->httpversion = 10; + } + } + } + else if(data->conn->handler->protocol & CURLPROTO_RTSP) { + const char *p = hd; + while(*p && ISBLANK(*p)) + p++; + if(!strncmp(p, "RTSP/", 5)) { + p += 5; + if(ISDIGIT(*p)) { + p++; + if((p[0] == '.') && ISDIGIT(p[1])) { + if(ISBLANK(p[2])) { + p += 3; + if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { + k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + + (p[2] - '0'); + p += 3; + if(ISSPACE(*p)) { + fine_statusline = TRUE; + k->httpversion = 11; /* RTSP acts like HTTP 1.1 */ + } + } + } + } + } + if(!fine_statusline) + return CURLE_WEIRD_SERVER_REPLY; + } + } + + if(fine_statusline) { + result = Curl_http_statusline(data, data->conn); + if(result) + return result; + writetype |= CLIENTWRITE_STATUS; + } + else { + k->header = FALSE; /* this is not a header line */ + return CURLE_WEIRD_SERVER_REPLY; + } + } + + result = verify_header(data, hd, hdlen); + if(result) + return result; + + result = Curl_http_header(data, hd, hdlen); + if(result) + return result; + + /* + * Taken in one (more) header. Write it to the client. + */ + Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen); + + if(k->httpcode/100 == 1) + writetype |= CLIENTWRITE_1XX; + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + return CURLE_OK; +} + +/* + * Read any HTTP header lines from the server and pass them to the client app. + */ +static CURLcode http_parse_headers(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; char *end_ptr; bool leftover_body = FALSE; /* header line within buffer loop */ *pconsumed = 0; - do { - size_t line_length; - int writetype; + while(blen && k->header) { + size_t consumed; - /* data is in network encoding so use 0x0a instead of '\n' */ - end_ptr = memchr(buf, 0x0a, blen); - + end_ptr = memchr(buf, '\n', blen); if(!end_ptr) { /* Not a complete header line within buffer, append the data to the end of the headerbuff. */ @@ -3958,6 +3854,10 @@ /* this is not the beginning of a protocol first header line */ k->header = FALSE; streamclose(conn, "bad HTTP: No end-of-message indicator"); + if(conn->httpversion >= 10) { + failf(data, "Invalid status line"); + return CURLE_WEIRD_SERVER_REPLY; + } if(!data->set.http09_allowed) { failf(data, "Received HTTP/0.9 when not allowed"); return CURLE_UNSUPPORTED_PROTOCOL; @@ -3970,14 +3870,13 @@ } /* decrease the size of the remaining (supposed) header line */ - line_length = (end_ptr - buf) + 1; - result = Curl_dyn_addn(&data->state.headerb, buf, line_length); + consumed = (end_ptr - buf) + 1; + result = Curl_dyn_addn(&data->state.headerb, buf, consumed); if(result) return result; - - blen -= line_length; - buf += line_length; - *pconsumed += line_length; + blen -= consumed; + buf += consumed; + *pconsumed += consumed; /**** * We now have a FULL header line in 'headerb'. @@ -3991,6 +3890,10 @@ if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); /* this is not the beginning of a protocol first header line */ + if(conn->httpversion >= 10) { + failf(data, "Invalid status line"); + return CURLE_WEIRD_SERVER_REPLY; + } if(!data->set.http09_allowed) { failf(data, "Received HTTP/0.9 when not allowed"); return CURLE_UNSUPPORTED_PROTOCOL; @@ -4001,496 +3904,21 @@ } } - /* headers are in network encoding so use 0x0a and 0x0d instead of '\n' - and '\r' */ - headp = Curl_dyn_ptr(&data->state.headerb); - if((0x0a == *headp) || (0x0d == *headp)) { - size_t headerlen; - bool switch_to_h2 = FALSE; - /* Zero-length header line means end of headers! */ - - if('\r' == *headp) - headp++; /* pass the \r byte */ - if('\n' == *headp) - headp++; /* pass the \n byte */ - - if(100 <= k->httpcode && 199 >= k->httpcode) { - /* "A user agent MAY ignore unexpected 1xx status responses." */ - 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. - * However, we'll get more headers now so we must get - * back into the header-parsing state! - */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - - /* if we did wait for this do enable write now! */ - if(k->exp100 > EXP100_SEND_DATA) { - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - } - break; - case 101: - if(conn->httpversion == 11) { - /* Switching Protocols only allowed from HTTP/1.1 */ - if(k->upgr101 == UPGR101_H2) { - /* Switching to HTTP/2 */ - infof(data, "Received 101, Switching to HTTP/2"); - k->upgr101 = UPGR101_RECEIVED; - - /* we'll get more headers (HTTP/2 response) */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - switch_to_h2 = TRUE; - } -#ifdef USE_WEBSOCKETS - else if(k->upgr101 == UPGR101_WS) { - /* verify the response */ - result = Curl_ws_accept(data, buf, blen); - if(result) - return result; - k->header = FALSE; /* no more header to parse! */ - *pconsumed += blen; /* ws accept handled the data */ - blen = 0; - if(data->set.connect_only) - k->keepon &= ~KEEP_RECV; /* read no more content */ - } -#endif - else { - /* Not switching to another protocol */ - k->header = FALSE; /* no more header to parse! */ - } - } - else { - /* invalid for other HTTP versions */ - failf(data, "unexpected 101 response code"); - return CURLE_WEIRD_SERVER_REPLY; - } - break; - default: - /* the status code 1xx indicates a provisional response, so - we'll get another set of headers */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - break; - } - } - else { - if(k->upgr101 == UPGR101_H2) { - /* A requested upgrade was denied, poke the multi handle to possibly - allow a pending pipewait to continue */ - Curl_multi_connchanged(data->multi); - } - k->header = FALSE; /* no more header to parse! */ - - if((k->size == -1) && !k->chunk && !conn->bits.close && - (conn->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"); - } - } - - if(!k->header) { - result = Curl_http_size(data); - if(result) - return result; - } - - /* At this point we have some idea about the fate of the connection. - If we are closing the connection it may result auth failure. */ -#if defined(USE_NTLM) - if(conn->bits.close && - (((data->req.httpcode == 401) && - (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || - ((data->req.httpcode == 407) && - (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { - infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); - data->state.authproblem = TRUE; - } -#endif -#if defined(USE_SPNEGO) - if(conn->bits.close && - (((data->req.httpcode == 401) && - (conn->http_negotiate_state == GSS_AUTHRECV)) || - ((data->req.httpcode == 407) && - (conn->proxy_negotiate_state == GSS_AUTHRECV)))) { - infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); - data->state.authproblem = TRUE; - } - if((conn->http_negotiate_state == GSS_AUTHDONE) && - (data->req.httpcode != 401)) { - conn->http_negotiate_state = GSS_AUTHSUCC; - } - if((conn->proxy_negotiate_state == GSS_AUTHDONE) && - (data->req.httpcode != 407)) { - conn->proxy_negotiate_state = GSS_AUTHSUCC; - } -#endif - - /* now, only output this if the header AND body are requested: - */ - writetype = CLIENTWRITE_HEADER | - ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); - - headerlen = Curl_dyn_len(&data->state.headerb); - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), - headerlen); - if(result) - return result; - - result = Curl_bump_headersize(data, headerlen, FALSE); - if(result) - return result; - - /* - * When all the headers have been parsed, see if we should give - * up and return an error. - */ - if(http_should_fail(data)) { - failf(data, "The requested URL returned error: %d", - k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; - } - -#ifdef USE_WEBSOCKETS - /* All non-101 HTTP status codes are bad when wanting to upgrade to - websockets */ - if(data->req.upgr101 == UPGR101_WS) { - failf(data, "Refused WebSockets upgrade: %d", k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; - } -#endif - - - data->req.deductheadercount = - (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0; - - /* 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) - return result; - - if(k->httpcode >= 300) { - if((!conn->bits.authneg) && !conn->bits.close && - !data->state.rewindbeforesend) { - /* - * 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've considered the big picture and we - * avoid this check. - * - * rewindbeforesend indicates that something has told libcurl to - * continue sending even if it gets discarded - */ - - 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've read the entire response. - */ - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - if(!k->upload_done) { - if((k->httpcode == 417) && data->state.expect100header) { - /* 417 Expectation Failed - try again without the Expect - header */ - if(!k->writebytecount && - k->exp100 == EXP100_AWAITING_CONTINUE) { - infof(data, "Got HTTP failure 417 while waiting for a 100"); - } - else { - infof(data, "Got HTTP failure 417 while sending data"); - streamclose(conn, - "Stop sending data before everything sent"); - result = http_perhapsrewind(data, conn); - if(result) - return result; - } - data->state.disableexpect = TRUE; - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->state.url); - Curl_done_sending(data, k); - } - else if(data->set.http_keep_sending_on_error) { - infof(data, "HTTP error before end of send, keep sending"); - if(k->exp100 > EXP100_SEND_DATA) { - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - } - } - else { - infof(data, "HTTP error before end of send, stop sending"); - streamclose(conn, "Stop sending data before everything sent"); - result = Curl_done_sending(data, k); - if(result) - return result; - k->upload_done = TRUE; - if(data->state.expect100header) - k->exp100 = EXP100_FAILED; - } - } - break; - - default: /* default label present to avoid compiler warnings */ - break; - } - } - - if(data->state.rewindbeforesend && - (conn->writesockfd != CURL_SOCKET_BAD)) { - /* We rewind before next send, continue sending now */ - infof(data, "Keep sending data to get tossed away"); - k->keepon |= KEEP_SEND; - } - } - - if(!k->header) { - /* - * really end-of-headers. - * - * If we requested a "no body", this is a good time to get - * out and return home. - */ - 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'd - 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(0 == k->maxdownload - && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) - && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) - k->download_done = TRUE; - - Curl_debug(data, CURLINFO_HEADER_IN, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - goto out; /* exit header line loop */ - } - - /* We continue reading headers, reset the line-based header */ - Curl_dyn_reset(&data->state.headerb); - if(switch_to_h2) { - /* Having handled the headers, we can do the HTTP/2 switch. - * Any remaining `buf` bytes are already HTTP/2 and passed to - * be processed. */ - result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); - if(result) - return result; - *pconsumed += blen; - blen = 0; - } - - continue; - } - - /* - * Checks for special headers coming up. - */ - - writetype = CLIENTWRITE_HEADER; - if(!k->headerline++) { - /* This is the first header, it MUST be the error code line - or else we consider this to be the body right away! */ - bool fine_statusline = FALSE; - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - /* - * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 - * - * The response code is always a three-digit number in HTTP as the spec - * says. We allow any three-digit number here, but we cannot make - * guarantees on future behaviors since it isn't within the protocol. - */ - int httpversion = 0; - char *p = headp; - - while(*p && ISBLANK(*p)) - p++; - if(!strncmp(p, "HTTP/", 5)) { - p += 5; - switch(*p) { - case '1': - p++; - if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) { - if(ISBLANK(p[2])) { - httpversion = 10 + (p[1] - '0'); - p += 3; - if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + - (p[2] - '0'); - p += 3; - if(ISSPACE(*p)) - fine_statusline = TRUE; - } - } - } - if(!fine_statusline) { - failf(data, "Unsupported HTTP/1 subversion in response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - break; - case '2': - case '3': - if(!ISBLANK(p[1])) - break; - httpversion = (*p - '0') * 10; - p += 2; - if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + - (p[2] - '0'); - p += 3; - if(!ISSPACE(*p)) - break; - fine_statusline = TRUE; - } - break; - default: /* unsupported */ - failf(data, "Unsupported HTTP version in response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - } - - if(fine_statusline) { - if(k->httpcode < 100) { - failf(data, "Unsupported response code in HTTP response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - switch(httpversion) { - case 10: - case 11: -#ifdef USE_HTTP2 - case 20: -#endif -#ifdef ENABLE_QUIC - case 30: -#endif - conn->httpversion = (unsigned char)httpversion; - break; - default: - failf(data, "Unsupported HTTP version (%u.%d) in response", - httpversion/10, httpversion%10); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - if(k->upgr101 == UPGR101_RECEIVED) { - /* supposedly upgraded to http2 now */ - if(conn->httpversion != 20) - infof(data, "Lying server, not serving HTTP/2"); - } - if(conn->httpversion < 20) { - conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; - } - } - else { - /* If user has set option HTTP200ALIASES, - compare header line against list of aliases - */ - statusline check = - checkhttpprefix(data, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(check == STATUS_DONE) { - fine_statusline = TRUE; - k->httpcode = 200; - conn->httpversion = 10; - } - } - } - else if(conn->handler->protocol & CURLPROTO_RTSP) { - char *p = headp; - while(*p && ISBLANK(*p)) - p++; - if(!strncmp(p, "RTSP/", 5)) { - p += 5; - if(ISDIGIT(*p)) { - p++; - if((p[0] == '.') && ISDIGIT(p[1])) { - if(ISBLANK(p[2])) { - p += 3; - if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) { - k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 + - (p[2] - '0'); - p += 3; - if(ISSPACE(*p)) { - fine_statusline = TRUE; - conn->httpversion = 11; /* RTSP acts like HTTP 1.1 */ - } - } - } - } - } - if(!fine_statusline) - return CURLE_WEIRD_SERVER_REPLY; - } - } - - if(fine_statusline) { - result = Curl_http_statusline(data, conn); - if(result) - return result; - writetype |= CLIENTWRITE_STATUS; - } - else { - k->header = FALSE; /* this is not a header line */ - break; - } - } - - result = verify_header(data); - if(result) - return result; - - result = Curl_http_header(data, conn, headp); - if(result) - return result; - - /* - * End of header-checks. Write them to the client. - */ - if(k->httpcode/100 == 1) - writetype |= CLIENTWRITE_1XX; - - Curl_debug(data, CURLINFO_HEADER_IN, headp, - Curl_dyn_len(&data->state.headerb)); - - result = Curl_client_write(data, writetype, headp, - Curl_dyn_len(&data->state.headerb)); - if(result) - return result; - - result = Curl_bump_headersize(data, Curl_dyn_len(&data->state.headerb), - FALSE); - if(result) - return result; - + result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb), + Curl_dyn_len(&data->state.headerb), + buf, blen, &consumed); + /* We are done with this line. We reset because response + * processing might switch to HTTP/2 and that might call us + * directly again. */ Curl_dyn_reset(&data->state.headerb); + if(consumed) { + blen -= consumed; + buf += consumed; + *pconsumed += consumed; + } + if(result) + return result; } - while(blen); /* We might have reached the end of the header part here, but there might be a non-header part left in the end of the read @@ -4502,16 +3930,30 @@ return CURLE_OK; } +CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, + const char *hd, size_t hdlen, + bool is_eos) +{ + CURLcode result; + size_t consumed; + char tmp = 0; + + result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed); + if(!result && is_eos) { + result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS), + &tmp, 0); + } + return result; +} + /* * HTTP protocol `write_resp` implementation. Will parse headers * when not done yet and otherwise return without consuming data. */ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, const char *buf, size_t blen, - size_t *pconsumed, - bool *done) + size_t *pconsumed) { - *done = FALSE; if(!data->req.header) { *pconsumed = 0; return CURLE_OK; @@ -4519,11 +3961,8 @@ else { CURLcode result; - result = http_rw_headers(data, buf, blen, pconsumed); + result = http_parse_headers(data, buf, blen, pconsumed); if(!result && !data->req.header) { - /* we have successfully finished parsing the HEADERs */ - result = Curl_http_firstwrite(data, data->conn, done); - if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) { /* leftover from parsing something that turned out not * to be a header, only happens if we allow for @@ -4540,23 +3979,21 @@ CURLcode Curl_http_write_resp(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos, - bool *done) + bool is_eos) { CURLcode result; size_t consumed; int flags; - *done = FALSE; - result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done); - if(result || *done) + result = Curl_http_write_resp_hds(data, buf, blen, &consumed); + if(result || data->req.done) goto out; DEBUGASSERT(consumed <= blen); blen -= consumed; buf += consumed; /* either all was consumed in header parsing, or we have data left - * and are done with heders, e.g. it is BODY data */ + * and are done with headers, e.g. it is BODY data */ DEBUGASSERT(!blen || !data->req.header); if(!data->req.header && (blen || is_eos)) { /* BODY data after header been parsed, write and consume */ @@ -4938,4 +4375,142 @@ } } +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; + data->req.keepon |= KEEP_SEND; + data->req.keepon &= ~KEEP_SEND_TIMED; + 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: + /* We are now waiting for a reply from the server or + * a timeout on our side */ + DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE")); + ctx->state = EXP100_AWAITING_CONTINUE; + ctx->start = Curl_now(); + Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); + data->req.keepon &= ~KEEP_SEND; + data->req.keepon |= KEEP_SEND_TIMED; + *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 = Curl_timediff(Curl_now(), ctx->start); + if(ms < data->set.expect_100_timeout) { + DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); + data->req.keepon &= ~KEEP_SEND; + data->req.keepon |= KEEP_SEND_TIMED; + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } + /* we've 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; + data->req.keepon &= ~KEEP_SEND_TIMED; + 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_rewind, + Curl_creader_def_unpause, + 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; +} + +void Curl_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); +} + +bool Curl_http_exp100_is_selected(struct Curl_easy *data) +{ + struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); + return r? TRUE : FALSE; +} + #endif /* CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index ad2697c..b0c4f5f 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h
@@ -44,7 +44,7 @@ #ifndef CURL_DISABLE_HTTP -#if defined(ENABLE_QUIC) +#if defined(USE_HTTP3) #include <stdint.h> #endif @@ -74,12 +74,6 @@ const char *thisheader, const size_t thislen); struct HTTP; /* see below */ -CURLcode Curl_buffer_send(struct dynbuf *in, - struct Curl_easy *data, - struct HTTP *http, - curl_off_t *bytes_written, - curl_off_t included_body_bytes, - int socketindex); CURLcode Curl_add_timecondition(struct Curl_easy *data, #ifndef USE_HYPER @@ -100,10 +94,6 @@ bool is_connect, struct dynhds *hds); -CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, - struct dynbuf *buf, - struct Curl_easy *handle); - void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, const char **method, Curl_HttpReq *); CURLcode Curl_http_useragent(struct Curl_easy *data); @@ -112,14 +102,14 @@ struct dynbuf *req); CURLcode Curl_http_statusline(struct Curl_easy *data, struct connectdata *conn); -CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, - char *headp); +CURLcode Curl_http_header(struct Curl_easy *data, + const char *hd, size_t hdlen); CURLcode Curl_transferencode(struct Curl_easy *data); -CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, - Curl_HttpReq httpreq, - const char **teep); -CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *r, Curl_HttpReq httpreq); +CURLcode Curl_http_req_set_reader(struct Curl_easy *data, + Curl_HttpReq httpreq, + const char **tep); +CURLcode Curl_http_req_complete(struct Curl_easy *data, + struct dynbuf *r, Curl_HttpReq httpreq); bool Curl_use_http_1_1plus(const struct Curl_easy *data, const struct connectdata *conn); #ifndef CURL_DISABLE_COOKIES @@ -129,14 +119,9 @@ #else #define Curl_http_cookies(a,b,c) CURLE_OK #endif -CURLcode Curl_http_resume(struct Curl_easy *data, - struct connectdata *conn, - Curl_HttpReq httpreq); CURLcode Curl_http_range(struct Curl_easy *data, Curl_HttpReq httpreq); -CURLcode Curl_http_firstwrite(struct Curl_easy *data, - struct connectdata *conn, - bool *done); +CURLcode Curl_http_firstwrite(struct Curl_easy *data); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http_setup_conn(struct Curl_easy *data, @@ -148,8 +133,10 @@ curl_socket_t *socks); CURLcode Curl_http_write_resp(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos, - bool *done); + bool is_eos); +CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, + const char *hd, size_t hdlen, + bool is_eos); /* These functions are in http.c */ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, @@ -192,43 +179,24 @@ version. This count includes CONNECT response headers. */ #define MAX_HTTP_RESP_HEADER_SIZE (300*1024) +bool Curl_http_exp100_is_selected(struct Curl_easy *data); +void Curl_http_exp100_got100(struct Curl_easy *data); + #endif /* CURL_DISABLE_HTTP */ /**************************************************************************** * HTTP unique setup ***************************************************************************/ struct HTTP { - curl_off_t postsize; /* off_t to handle large file sizes */ - const char *postdata; - struct back { - curl_read_callback fread_func; /* backup storage for fread pointer */ - void *fread_in; /* backup storage for fread_in pointer */ - const char *postdata; - curl_off_t postsize; - struct Curl_easy *data; - } backup; - - enum { - HTTPSEND_NADA, /* init */ - HTTPSEND_REQUEST, /* sending a request */ - HTTPSEND_BODY /* sending body */ - } sending; - -#ifndef CURL_DISABLE_HTTP - void *h2_ctx; /* HTTP/2 implementation context */ - void *h3_ctx; /* HTTP/3 implementation context */ - struct dynbuf send_buffer; /* used if the request couldn't be sent in one - chunk, points to an allocated send_buffer - struct */ -#endif + /* TODO: no longer used, we should remove it from SingleRequest */ + char unused; }; CURLcode Curl_http_size(struct Curl_easy *data); CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, const char *buf, size_t blen, - size_t *pconsumed, - bool *done); + size_t *pconsumed); /** * Curl_http_output_auth() setups the authentication headers for the
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index c3157d1..f0f7b56 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c
@@ -29,6 +29,7 @@ #include <nghttp2/nghttp2.h> #include "urldata.h" #include "bufq.h" +#include "hash.h" #include "http1.h" #include "http2.h" #include "http.h" @@ -121,15 +122,17 @@ struct cf_h2_ctx { nghttp2_session *h2; - uint32_t max_concurrent_streams; /* The easy handle used in the current filter call, cleared at return */ struct cf_call_data call_data; struct bufq inbufq; /* network input */ struct bufq outbufq; /* network output */ struct bufc_pool stream_bufcp; /* spares for stream buffers */ + struct dynbuf scratch; /* scratch buffer for temp use */ + struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */ size_t drain_total; /* sum of all stream's UrlState drain */ + uint32_t max_concurrent_streams; int32_t goaway_error; int32_t last_stream_id; BIT(conn_closed); @@ -153,6 +156,9 @@ Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_dyn_free(&ctx->scratch); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); memset(ctx, 0, sizeof(*ctx)); ctx->call_data = save; } @@ -169,11 +175,9 @@ struct Curl_easy *data); /** - * All about the H3 internals of a stream + * All about the H2 internals of a stream */ -struct stream_ctx { - /*********** for HTTP/2 we store stream-local data here *************/ - int32_t id; /* HTTP/2 protocol identifier for stream */ +struct h2_stream_ctx { struct bufq recvbuf; /* response buffer */ struct bufq sendbuf; /* request buffer */ struct h1_req_parser h1; /* parsing the request */ @@ -181,6 +185,7 @@ size_t resp_hds_len; /* amount of response header bytes in recvbuf */ size_t upload_blocked_len; curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t nrcvd_data; /* number of DATA bytes received */ char **push_headers; /* allocated array */ size_t push_headers_used; /* number of entries filled in */ @@ -188,29 +193,77 @@ int status_code; /* HTTP response status code */ uint32_t error; /* stream error code */ + CURLcode xfer_result; /* Result of writing out response */ uint32_t local_window_size; /* the local recv window size */ - bool resp_hds_complete; /* we have a complete, final response */ - bool closed; /* TRUE on stream close */ - bool reset; /* TRUE on stream reset */ - bool close_handled; /* TRUE if stream closure is handled by libcurl */ - bool bodystarted; - bool send_closed; /* transfer is done sending, we might have still - buffered data in stream->sendbuf to upload. */ + int32_t id; /* HTTP/2 protocol identifier for stream */ + BIT(resp_hds_complete); /* we have a complete, final response */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ + BIT(bodystarted); + BIT(send_closed); /* transfer is done sending, we might have still + buffered data in stream->sendbuf to upload. */ }; -#define H2_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h2_ctx \ - : NULL)) -#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx -#define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \ - H2_STREAM_CTX(d)->id : -2) +#define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) +{ + struct h2_stream_ctx *stream; + + (void)ctx; + stream = calloc(1, sizeof(*stream)); + if(!stream) + return NULL; + + stream->id = -1; + Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, + H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); + stream->resp_hds_len = 0; + stream->bodystarted = FALSE; + stream->status_code = -1; + stream->closed = FALSE; + stream->close_handled = FALSE; + stream->error = NGHTTP2_NO_ERROR; + stream->local_window_size = H2_STREAM_WINDOW_SIZE; + stream->upload_left = 0; + stream->nrcvd_data = 0; + return stream; +} + +static void free_push_headers(struct h2_stream_ctx *stream) +{ + size_t i; + for(i = 0; i<stream->push_headers_used; i++) + 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); + free(stream); +} + +static void h2_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h2_stream_ctx_free((struct h2_stream_ctx *)stream); +} /* * Mark this transfer to get "drained". */ static void drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data, - struct stream_ctx *stream) + struct h2_stream_ctx *stream) { unsigned char bits; @@ -229,10 +282,10 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data, - struct stream_ctx **pstream) + struct h2_stream_ctx **pstream) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream; + struct h2_stream_ctx *stream; (void)cf; DEBUGASSERT(data); @@ -240,45 +293,31 @@ failf(data, "initialization failure, transfer not http initialized"); return CURLE_FAILED_INIT; } - stream = H2_STREAM_CTX(data); + stream = H2_STREAM_CTX(ctx, data); if(stream) { *pstream = stream; return CURLE_OK; } - stream = calloc(1, sizeof(*stream)); + stream = h2_stream_ctx_create(ctx); if(!stream) return CURLE_OUT_OF_MEMORY; - stream->id = -1; - Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, - H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); - Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, - H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); - stream->resp_hds_len = 0; - stream->bodystarted = FALSE; - stream->status_code = -1; - stream->closed = FALSE; - stream->close_handled = FALSE; - stream->error = NGHTTP2_NO_ERROR; - stream->local_window_size = H2_STREAM_WINDOW_SIZE; - stream->upload_left = 0; + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h2_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } - H2_STREAM_LCTX(data) = stream; *pstream = stream; return CURLE_OK; } -static void http2_data_done(struct Curl_cfilter *cf, - struct Curl_easy *data, bool premature) +static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); DEBUGASSERT(ctx); - (void)premature; if(!stream) return; @@ -298,36 +337,12 @@ stream->id, NGHTTP2_STREAM_CLOSED); flush_egress = TRUE; } - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - /* Anything in the recvbuf is still being counted - * in stream and connection window flow control. Need - * to free that space or the connection window might get - * exhausted eventually. */ - nghttp2_session_consume(ctx->h2, stream->id, - Curl_bufq_len(&stream->recvbuf)); - /* give WINDOW_UPATE a chance to be sent, but ignore any error */ - flush_egress = TRUE; - } if(flush_egress) nghttp2_session_send(ctx->h2); } - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - Curl_h1_req_parse_free(&stream->h1); - Curl_dynhds_free(&stream->resp_trailers); - if(stream->push_headers) { - /* if they weren't used and then freed before */ - for(; stream->push_headers_used > 0; --stream->push_headers_used) { - free(stream->push_headers[stream->push_headers_used - 1]); - } - free(stream->push_headers); - stream->push_headers = NULL; - } - - free(stream); - H2_STREAM_LCTX(data) = NULL; + Curl_hash_offt_remove(&ctx->streams, data->id); } static int h2_client_new(struct Curl_cfilter *cf, @@ -411,7 +426,7 @@ bool via_h1_upgrade) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream; + struct h2_stream_ctx *stream; CURLcode result = CURLE_OUT_OF_MEMORY; int rc; nghttp2_session_callbacks *cbs = NULL; @@ -420,6 +435,8 @@ Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); + Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free); ctx->last_stream_id = 2147483647; rc = nghttp2_session_callbacks_new(&cbs); @@ -718,6 +735,7 @@ the struct are hidden from the user. */ struct curl_pushheaders { struct Curl_easy *data; + struct h2_stream_ctx *stream; const nghttp2_push_promise *frame; }; @@ -731,9 +749,8 @@ if(!h || !GOOD_EASY_HANDLE(h->data)) return NULL; else { - struct stream_ctx *stream = H2_STREAM_CTX(h->data); - if(stream && num < stream->push_headers_used) - return stream->push_headers[num]; + if(h->stream && num < h->stream->push_headers_used) + return h->stream->push_headers[num]; } return NULL; } @@ -743,7 +760,7 @@ */ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) { - struct stream_ctx *stream; + struct h2_stream_ctx *stream; size_t len; size_t i; /* Verify that we got a good easy handle in the push header struct, @@ -756,7 +773,7 @@ !strcmp(header, ":") || strchr(header + 1, ':')) return NULL; - stream = H2_STREAM_CTX(h->data); + stream = h->stream; if(!stream) return NULL; @@ -783,7 +800,7 @@ (void)Curl_close(&second); } else { - struct stream_ctx *second_stream; + struct h2_stream_ctx *second_stream; second->req.p.http = http; http2_data_setup(cf, second, &second_stream); @@ -816,7 +833,7 @@ v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY); if(v) { - uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER); + uc = Curl_url_set_authority(u, v); if(uc) { rc = 2; goto fail; @@ -850,9 +867,8 @@ static void discard_newhandle(struct Curl_cfilter *cf, struct Curl_easy *newhandle) { - if(!newhandle->req.p.http) { - http2_data_done(cf, newhandle, TRUE); - newhandle->req.p.http = NULL; + if(newhandle->req.p.http) { + http2_data_done(cf, newhandle); } (void)Curl_close(&newhandle); } @@ -867,12 +883,11 @@ CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", frame->promised_stream_id); if(data->multi->push_cb) { - struct stream_ctx *stream; - struct stream_ctx *newstream; + struct h2_stream_ctx *stream; + struct h2_stream_ctx *newstream; struct curl_pushheaders heads; CURLMcode rc; CURLcode result; - size_t i; /* clone the parent */ struct Curl_easy *newhandle = h2_duphandle(cf, data); if(!newhandle) { @@ -881,12 +896,10 @@ goto fail; } - heads.data = data; - heads.frame = frame; /* ask the application */ CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application"); - stream = H2_STREAM_CTX(data); + stream = H2_STREAM_CTX(ctx, data); if(!stream) { failf(data, "Internal NULL stream"); discard_newhandle(cf, newhandle); @@ -894,6 +907,10 @@ goto fail; } + heads.data = data; + heads.stream = stream; + heads.frame = frame; + rv = set_transfer_url(newhandle, &heads); if(rv) { discard_newhandle(cf, newhandle); @@ -917,11 +934,7 @@ Curl_set_in_callback(data, false); /* free the headers again */ - for(i = 0; i<stream->push_headers_used; i++) - free(stream->push_headers[i]); - free(stream->push_headers); - stream->push_headers = NULL; - stream->push_headers_used = 0; + free_push_headers(stream); if(rv) { DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); @@ -963,22 +976,39 @@ return rv; } -static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf, +static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, struct Curl_easy *data, - const char *buf, size_t blen) + struct h2_stream_ctx *stream, + const char *buf, size_t blen, bool eos) { - struct stream_ctx *stream = H2_STREAM_CTX(data); - ssize_t nwritten; - CURLcode result; - (void)cf; - nwritten = Curl_bufq_write(&stream->recvbuf, - (const unsigned char *)buf, blen, &result); - if(nwritten < 0) - return result; - stream->resp_hds_len += (size_t)nwritten; - DEBUGASSERT((size_t)nwritten == blen); - return CURLE_OK; + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); + if(stream->xfer_result) + CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers", + stream->id, stream->xfer_result, blen); + } +} + +static void h2_xfer_write_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + 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); + /* If the transfer write is errored, we do not want any more data */ + if(stream->xfer_result) { + struct cf_h2_ctx *ctx = cf->ctx; + CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, " + "RST-ing stream", + stream->id, stream->xfer_result, blen); + nghttp2_submit_rst_stream(ctx->h2, 0, stream->id, + NGHTTP2_ERR_CALLBACK_FAILURE); + } } static CURLcode on_stream_frame(struct Curl_cfilter *cf, @@ -986,10 +1016,8 @@ const nghttp2_frame *frame) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); int32_t stream_id = frame->hd.stream_id; - CURLcode result; - size_t rbuflen; int rv; if(!stream) { @@ -999,9 +1027,8 @@ switch(frame->hd.type) { case NGHTTP2_DATA: - rbuflen = Curl_bufq_len(&stream->recvbuf); - CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d", - stream_id, rbuflen, + CURL_TRC_CF(data, cf, "[%d] DATA, window=%d/%d", + stream_id, nghttp2_session_get_stream_effective_recv_data_length( ctx->h2, stream->id), nghttp2_session_get_stream_effective_local_window_size( @@ -1018,20 +1045,6 @@ if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { drain_stream(cf, data, stream); } - else if(rbuflen > stream->local_window_size) { - int32_t wsize = nghttp2_session_get_stream_local_window_size( - ctx->h2, stream->id); - if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) { - /* H2 flow control is not absolute, as the server might not have the - * same view, yet. When we receive more than we want, we enforce - * the local window size again to make nghttp2 send WINDOW_UPATEs - * accordingly. */ - nghttp2_session_set_local_window_size(ctx->h2, - NGHTTP2_FLAG_NONE, - stream->id, - stream->local_window_size); - } - } break; case NGHTTP2_HEADERS: if(stream->bodystarted) { @@ -1052,9 +1065,7 @@ stream->status_code = -1; } - result = recvbuf_write_hds(cf, data, STRCONST("\r\n")); - if(result) - return result; + h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -1233,7 +1244,7 @@ * 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)) { - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); if(stream) drain_stream(cf, data, stream); } @@ -1270,10 +1281,9 @@ const uint8_t *mem, size_t len, void *userp) { struct Curl_cfilter *cf = userp; - struct stream_ctx *stream; + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; struct Curl_easy *data_s; - ssize_t nwritten; - CURLcode result; (void)flags; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ @@ -1292,22 +1302,17 @@ return 0; } - stream = H2_STREAM_CTX(data_s); + stream = H2_STREAM_CTX(ctx, data_s); if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; - nwritten = Curl_bufq_write(&stream->recvbuf, mem, len, &result); - if(nwritten < 0) { - if(result != CURLE_AGAIN) - return NGHTTP2_ERR_CALLBACK_FAILURE; + h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE); - nwritten = 0; - } + nghttp2_session_consume(ctx->h2, stream_id, len); + stream->nrcvd_data += (curl_off_t)len; /* if we receive data for another handle, wake that up */ drain_stream(cf, data_s, stream); - - DEBUGASSERT((size_t)nwritten == len); return 0; } @@ -1315,8 +1320,9 @@ uint32_t error_code, void *userp) { struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf); - struct stream_ctx *stream; + struct h2_stream_ctx *stream; int rv; (void)session; @@ -1339,7 +1345,7 @@ (void)nghttp2_session_set_stream_user_data(session, stream_id, 0); return NGHTTP2_ERR_CALLBACK_FAILURE; } - stream = H2_STREAM_CTX(data_s); + stream = H2_STREAM_CTX(ctx, data_s); if(!stream) { CURL_TRC_CF(data_s, cf, "[%d] on_stream_close, GOOD easy but no stream", stream_id); @@ -1374,7 +1380,8 @@ const nghttp2_frame *frame, void *userp) { struct Curl_cfilter *cf = userp; - struct stream_ctx *stream; + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; struct Curl_easy *data_s = NULL; (void)cf; @@ -1387,7 +1394,7 @@ return 0; } - stream = H2_STREAM_CTX(data_s); + stream = H2_STREAM_CTX(ctx, data_s); if(!stream || !stream->bodystarted) { return 0; } @@ -1403,7 +1410,8 @@ void *userp) { struct Curl_cfilter *cf = userp; - struct stream_ctx *stream; + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream; struct Curl_easy *data_s; int32_t stream_id = frame->hd.stream_id; CURLcode result; @@ -1418,7 +1426,7 @@ internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; - stream = H2_STREAM_CTX(data_s); + stream = H2_STREAM_CTX(ctx, data_s); if(!stream) { failf(data_s, "Internal NULL stream"); return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1459,7 +1467,7 @@ stream->push_headers = malloc(stream->push_headers_alloc * sizeof(char *)); if(!stream->push_headers) - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + return NGHTTP2_ERR_CALLBACK_FAILURE; stream->push_headers_used = 0; } else if(stream->push_headers_used == @@ -1468,15 +1476,15 @@ if(stream->push_headers_alloc > 1000) { /* this is beyond crazy many headers, bail out */ failf(data_s, "Too many PUSH_PROMISE headers"); - Curl_safefree(stream->push_headers); - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; } stream->push_headers_alloc *= 2; - headp = Curl_saferealloc(stream->push_headers, - stream->push_headers_alloc * sizeof(char *)); + headp = realloc(stream->push_headers, + stream->push_headers_alloc * sizeof(char *)); if(!headp) { - stream->push_headers = NULL; - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; } stream->push_headers = headp; } @@ -1512,14 +1520,15 @@ result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 ")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - /* the space character after the status code is mandatory */ - result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n")); + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, value, valuelen); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); + if(!result) + h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ @@ -1534,16 +1543,17 @@ /* nghttp2 guarantees that namelen > 0, and :status was already received, and this is not pseudo-header field . */ /* convert to an HTTP1-style header */ - result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, STRCONST(": ")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n")); + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(": ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n")); + if(!result) + h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ @@ -1564,8 +1574,9 @@ void *userp) { struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; struct Curl_easy *data_s; - struct stream_ctx *stream = NULL; + struct h2_stream_ctx *stream = NULL; CURLcode result; ssize_t nread; (void)source; @@ -1580,7 +1591,7 @@ internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; - stream = H2_STREAM_CTX(data_s); + stream = H2_STREAM_CTX(ctx, data_s); if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1667,7 +1678,7 @@ { struct cf_h2_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); if(!ctx || !ctx->h2 || !stream) goto out; @@ -1691,7 +1702,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, struct Curl_easy *data, - struct stream_ctx *stream, + struct h2_stream_ctx *stream, CURLcode *err) { ssize_t rv = 0; @@ -1705,6 +1716,15 @@ return -1; } 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; + *err = CURLE_OK; + goto out; + } failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", stream->id, nghttp2_http2_strerror(stream->error), stream->error); @@ -1713,7 +1733,7 @@ } else if(stream->reset) { failf(data, "HTTP/2 stream %u was reset", stream->id); - *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2; return -1; } @@ -1783,11 +1803,12 @@ * struct. */ -static void h2_pri_spec(struct Curl_easy *data, +static void h2_pri_spec(struct cf_h2_ctx *ctx, + struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { struct Curl_data_priority *prio = &data->set.priority; - struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent); + struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent); int32_t depstream_id = depstream? depstream->id:0; nghttp2_priority_spec_init(pri_spec, depstream_id, sweight_wanted(data), @@ -1805,7 +1826,7 @@ struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); int rv = 0; if(stream && stream->id > 0 && @@ -1815,7 +1836,7 @@ /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; - h2_pri_spec(data, &pri_spec); + h2_pri_spec(ctx, data, &pri_spec); CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1838,40 +1859,31 @@ } static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - struct stream_ctx *stream, + struct h2_stream_ctx *stream, char *buf, size_t len, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; ssize_t nread = -1; + (void)buf; *err = CURLE_AGAIN; - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) - goto out; - DEBUGASSERT(nread > 0); + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id); + *err = stream->xfer_result; + nread = -1; } - - if(nread < 0) { - if(stream->closed) { - CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); - nread = http2_handle_stream_close(cf, data, stream, err); - } - else if(stream->reset || - (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx->goaway && ctx->last_stream_id < stream->id)) { - CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); - *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - nread = -1; - } + else if(stream->closed) { + CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); + nread = http2_handle_stream_close(cf, data, stream, err); } - else if(nread == 0) { - *err = CURLE_AGAIN; + else if(stream->reset || + (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || + (ctx->goaway && ctx->last_stream_id < stream->id)) { + CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2; nread = -1; } -out: if(nread < 0 && *err != CURLE_AGAIN) CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d", stream->id, len, nread, *err); @@ -1879,10 +1891,11 @@ } static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + size_t data_max_bytes) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream; + struct h2_stream_ctx *stream; CURLcode result = CURLE_OK; ssize_t nread; @@ -1898,17 +1911,18 @@ * it is time to stop due to connection close or us not processing * all network input */ while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - stream = H2_STREAM_CTX(data); - if(stream && (stream->closed || Curl_bufq_is_full(&stream->recvbuf))) { + 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. */ if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) - break; + drain_stream(cf, data, stream); + break; } - nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); + nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result); if(nread < 0) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 data: %d(%s)", result, @@ -1923,8 +1937,9 @@ break; } else { - CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", - nread); + CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread); + data_max_bytes = (data_max_bytes > (size_t)nread)? + (data_max_bytes - (size_t)nread) : 0; } if(h2_process_pending_input(cf, data, &result)) @@ -1942,7 +1957,7 @@ char *buf, size_t len, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); ssize_t nread = -1; CURLcode result; struct cf_call_data save; @@ -1966,7 +1981,7 @@ goto out; if(nread < 0) { - *err = h2_progress_ingress(cf, data); + *err = h2_progress_ingress(cf, data, len); if(*err) goto out; @@ -2011,9 +2026,8 @@ nread = -1; } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, " - "buffered=%zu, window=%d/%d, connection %d/%d", + "window=%d/%d, connection %d/%d", stream->id, len, nread, *err, - Curl_bufq_len(&stream->recvbuf), nghttp2_session_get_stream_effective_recv_data_length( ctx->h2, stream->id), nghttp2_session_get_stream_effective_local_window_size( @@ -2025,12 +2039,13 @@ return nread; } -static ssize_t h2_submit(struct stream_ctx **pstream, +static ssize_t h2_submit(struct h2_stream_ctx **pstream, struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, + size_t *phdslen, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = NULL; + struct h2_stream_ctx *stream = NULL; struct dynhds h2_headers; nghttp2_nv *nva = NULL; const void *body = NULL; @@ -2040,6 +2055,7 @@ nghttp2_priority_spec pri_spec; ssize_t nwritten; + *phdslen = 0; Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); *err = http2_data_setup(cf, data, &stream); @@ -2051,6 +2067,7 @@ nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; + *phdslen = (size_t)nwritten; if(!stream->h1.done) { /* need more data */ goto out; @@ -2072,7 +2089,7 @@ goto out; } - h2_pri_spec(data, &pri_spec); + h2_pri_spec(ctx, data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); @@ -2169,10 +2186,11 @@ const void *buf, size_t len, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); struct cf_call_data save; int rv; ssize_t nwritten; + size_t hdslen = 0; CURLcode result; int blocked = 0, was_blocked = 0; @@ -2236,11 +2254,12 @@ } } else { - nwritten = h2_submit(&stream, cf, data, buf, len, err); + nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err); if(nwritten < 0) { goto out; } DEBUGASSERT(stream); + DEBUGASSERT(hdslen <= (size_t)nwritten); } /* Call the nghttp2 send loop and flush to write ALL buffered data, @@ -2275,18 +2294,26 @@ * frame buffer or our network out buffer. */ size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, stream->id); - /* Whatever the cause, we need to return CURL_EAGAIN for this call. - * We have unwritten state that needs us being invoked again and EAGAIN - * is the only way to ensure that. */ - stream->upload_blocked_len = nwritten; + /* At the start of a stream, we are called with request headers + * and, possibly, parts of the body. Later, only body data. + * If we cannot send pure body data, we EAGAIN. If there had been + * header, we return that *they* have been written and remember the + * block on the data length only. */ + stream->upload_blocked_len = ((size_t)nwritten) - hdslen; CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " - "blocked_len=%zu", + "hds_len=%zu blocked_len=%zu", stream->id, len, nghttp2_session_get_remote_window_size(ctx->h2), rwin, - nwritten); - *err = CURLE_AGAIN; - nwritten = -1; - goto out; + hdslen, stream->upload_blocked_len); + if(hdslen) { + *err = CURLE_OK; + nwritten = hdslen; + } + else { + *err = CURLE_AGAIN; + nwritten = -1; + goto out; + } } else if(should_close_session(ctx)) { /* nghttp2 thinks this session is done. If the stream has not been @@ -2340,7 +2367,7 @@ sock = Curl_conn_cf_get_socket(cf, data); Curl_pollset_check(data, ps, sock, &want_recv, &want_send); if(want_recv || want_send) { - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); struct cf_call_data save; bool c_exhaust, s_exhaust; @@ -2387,7 +2414,7 @@ goto out; } - result = h2_progress_ingress(cf, data); + result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE); if(result) goto out; @@ -2441,7 +2468,7 @@ { #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); DEBUGASSERT(data); if(ctx && ctx->h2 && stream) { @@ -2510,10 +2537,10 @@ result = http2_data_done_send(cf, data); break; case CF_CTRL_DATA_DETACH: - http2_data_done(cf, data, TRUE); + http2_data_done(cf, data); break; case CF_CTRL_DATA_DONE: - http2_data_done(cf, data, arg1 != 0); + http2_data_done(cf, data); break; default: break; @@ -2526,11 +2553,10 @@ const struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq) - || (stream && !Curl_bufq_is_empty(&stream->sendbuf)) - || (stream && !Curl_bufq_is_empty(&stream->recvbuf)))) + || (stream && !Curl_bufq_is_empty(&stream->sendbuf)))) return TRUE; return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; } @@ -2586,6 +2612,11 @@ *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max; CF_DATA_RESTORE(cf, save); return CURLE_OK; + case CF_QUERY_STREAM_ERROR: { + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + *pres1 = stream? (int)stream->error : 0; + return CURLE_OK; + } default: break; } @@ -2615,7 +2646,8 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - int sockindex) + int sockindex, + bool via_h1_upgrade) { struct Curl_cfilter *cf = NULL; struct cf_h2_ctx *ctx; @@ -2630,8 +2662,9 @@ if(result) goto out; + ctx = NULL; Curl_conn_cf_add(data, conn, sockindex, cf); - result = CURLE_OK; + result = cf_h2_ctx_init(cf, data, via_h1_upgrade); out: if(result) @@ -2641,7 +2674,8 @@ } static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + bool via_h1_upgrade) { struct Curl_cfilter *cf_h2 = NULL; struct cf_h2_ctx *ctx; @@ -2656,8 +2690,9 @@ if(result) goto out; + ctx = NULL; Curl_conn_cf_insert_after(cf, cf_h2); - result = CURLE_OK; + result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade); out: if(result) @@ -2714,11 +2749,7 @@ DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); DEBUGF(infof(data, "switching to HTTP/2")); - result = http2_cfilter_add(&cf, data, conn, sockindex); - if(result) - return result; - - result = cf_h2_ctx_init(cf, data, FALSE); + result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE); if(result) return result; @@ -2741,15 +2772,11 @@ DEBUGASSERT(!Curl_cf_is_http2(cf, data)); - result = http2_cfilter_insert_after(cf, data); + result = http2_cfilter_insert_after(cf, data, FALSE); if(result) return result; cf_h2 = cf->next; - result = cf_h2_ctx_init(cf_h2, data, FALSE); - if(result) - return result; - cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; @@ -2774,17 +2801,13 @@ DEBUGF(infof(data, "upgrading to HTTP/2")); DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); - result = http2_cfilter_add(&cf, data, conn, sockindex); + result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE); if(result) return result; DEBUGASSERT(cf->cft == &Curl_cft_nghttp2); ctx = cf->ctx; - result = cf_h2_ctx_init(cf, data, TRUE); - if(result) - return result; - if(nread > 0) { /* Remaining data from the protocol switch reply is already using * the switched protocol, ie. HTTP/2. We add that to the network @@ -2823,8 +2846,11 @@ CURLE_HTTP2_STREAM error! */ bool Curl_h2_http_1_1_error(struct Curl_easy *data) { - struct stream_ctx *stream = H2_STREAM_CTX(data); - return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED); + if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) { + int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET); + return (err == NGHTTP2_HTTP_1_1_REQUIRED); + } + return FALSE; } #else /* !USE_NGHTTP2 */
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index c938291..98cc033 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -158,10 +158,7 @@ msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, "x-%s-date:%s", provider1, timestamp); - if(Curl_checkheaders(data, STRCONST("Host"))) { - head = NULL; - } - else { + if(!Curl_checkheaders(data, STRCONST("Host"))) { char full_host[FULL_HOST_LEN + 1]; if(data->state.aptr.host) {
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 039c179..48e7e46 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -27,10 +27,13 @@ #ifndef CURL_DISABLE_HTTP #include "urldata.h" /* it includes http_chunks.h */ +#include "curl_printf.h" +#include "curl_trc.h" #include "sendf.h" /* for the client write stuff */ #include "dynbuf.h" #include "content_encoding.h" #include "http.h" +#include "multiif.h" #include "strtoofft.h" #include "warnless.h" @@ -152,9 +155,9 @@ ch->hexbuffer[ch->hexindex++] = *buf; buf++; blen--; + (*pconsumed)++; } else { - char *endptr; if(0 == ch->hexindex) { /* This is illegal data, we received junk where we expected a hexadecimal digit. */ @@ -166,7 +169,7 @@ /* blen and buf are unmodified */ ch->hexbuffer[ch->hexindex] = 0; - if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) { + if(curlx_strtoofft(ch->hexbuffer, NULL, 16, &ch->datasize)) { failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer); ch->state = CHUNK_FAILED; ch->last_code = CHUNKE_ILLEGAL_HEX; @@ -183,12 +186,16 @@ if(0 == ch->datasize) { ch->state = CHUNK_TRAILER; /* now check for trailers */ } - else + else { ch->state = CHUNK_DATA; + CURL_TRC_WRITE(data, "http_chunked, chunk start of %" + CURL_FORMAT_CURL_OFF_T " bytes", ch->datasize); + } } buf++; blen--; + (*pconsumed)++; break; case CHUNK_DATA: @@ -218,6 +225,9 @@ ch->datasize -= piece; /* decrease amount left to expect */ buf += piece; /* move read pointer forward */ blen -= piece; /* decrease space left in this round */ + CURL_TRC_WRITE(data, "http_chunked, write %zu body bytes, %" + CURL_FORMAT_CURL_OFF_T " bytes in chunk remain", + piece, ch->datasize); if(0 == ch->datasize) /* end of data this round, we now expect a trailing CRLF */ @@ -236,6 +246,7 @@ } buf++; blen--; + (*pconsumed)++; break; case CHUNK_TRAILER: @@ -293,6 +304,7 @@ } buf++; blen--; + (*pconsumed)++; break; case CHUNK_TRAILER_CR: @@ -300,6 +312,7 @@ ch->state = CHUNK_TRAILER_POSTCR; buf++; blen--; + (*pconsumed)++; } else { ch->state = CHUNK_FAILED; @@ -320,6 +333,7 @@ /* skip if CR */ buf++; blen--; + (*pconsumed)++; } /* now wait for the final LF */ ch->state = CHUNK_STOP; @@ -328,15 +342,19 @@ case CHUNK_STOP: if(*buf == 0x0a) { blen--; + (*pconsumed)++; /* Record the length of any data left in the end of the buffer even if there's no more chunks to read */ ch->datasize = blen; ch->state = CHUNK_DONE; + CURL_TRC_WRITE(data, "http_chunk, response complete"); return CURLE_OK; } else { ch->state = CHUNK_FAILED; ch->last_code = CHUNKE_BAD_CHUNK; + CURL_TRC_WRITE(data, "http_chunk error, expected 0x0a, seeing 0x%ux", + (unsigned int)*buf); return CURLE_RECV_ERROR; } case CHUNK_DONE: @@ -386,7 +404,7 @@ static CURLcode cw_chunked_init(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct chunked_writer *ctx = (struct chunked_writer *)writer; + struct chunked_writer *ctx = writer->ctx; data->req.chunk = TRUE; /* chunks coming our way. */ Curl_httpchunk_init(data, &ctx->ch, FALSE); @@ -396,7 +414,7 @@ static void cw_chunked_close(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct chunked_writer *ctx = (struct chunked_writer *)writer; + struct chunked_writer *ctx = writer->ctx; Curl_httpchunk_free(data, &ctx->ch); } @@ -404,7 +422,7 @@ struct Curl_cwriter *writer, int type, const char *buf, size_t blen) { - struct chunked_writer *ctx = (struct chunked_writer *)writer; + struct chunked_writer *ctx = writer->ctx; CURLcode result; size_t consumed; @@ -452,4 +470,212 @@ sizeof(struct chunked_writer) }; +/* max length of a HTTP chunk that we want to generate */ +#define CURL_CHUNKED_MINLEN (1024) +#define CURL_CHUNKED_MAXLEN (64 * 1024) + +struct chunked_reader { + struct Curl_creader super; + struct bufq chunkbuf; + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_chunked_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct chunked_reader *ctx = reader->ctx; + (void)data; + Curl_bufq_init2(&ctx->chunkbuf, CURL_CHUNKED_MAXLEN, 2, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void cr_chunked_close(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct chunked_reader *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->chunkbuf); +} + +static CURLcode add_last_chunk(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct chunked_reader *ctx = reader->ctx; + struct curl_slist *trailers = NULL, *tr; + CURLcode result; + size_t n; + int rc; + + if(!data->set.trailer_callback) { + CURL_TRC_READ(data, "http_chunk, added last, empty chunk"); + return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n); + } + + result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n"), &n); + if(result) + goto out; + + Curl_set_in_callback(data, true); + rc = data->set.trailer_callback(&trailers, data->set.trailer_data); + Curl_set_in_callback(data, false); + + if(rc != CURL_TRAILERFUNC_OK) { + failf(data, "operation aborted by trailing headers callback"); + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } + + for(tr = trailers; tr; tr = tr->next) { + /* only add correctly formatted trailers */ + char *ptr = strchr(tr->data, ':'); + if(!ptr || *(ptr + 1) != ' ') { + infof(data, "Malformatted trailing header, skipping trailer"); + continue; + } + + result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data, + strlen(tr->data), &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n); + if(result) + goto out; + } + + result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n); + +out: + curl_slist_free_all(trailers); + CURL_TRC_READ(data, "http_chunk, added last chunk with trailers " + "from client -> %d", result); + return result; +} + +static CURLcode add_chunk(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen) +{ + struct chunked_reader *ctx = reader->ctx; + CURLcode result; + char tmp[CURL_CHUNKED_MINLEN]; + size_t nread; + bool eos; + + DEBUGASSERT(!ctx->read_eos); + blen = CURLMIN(blen, CURL_CHUNKED_MAXLEN); /* respect our buffer pref */ + if(blen < sizeof(tmp)) { + /* small read, make a chunk of decent size */ + buf = tmp; + blen = sizeof(tmp); + } + else { + /* larger read, make a chunk that will fit when read back */ + blen -= (8 + 2 + 2); /* deduct max overhead, 8 hex + 2*crlf */ + } + + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + if(result) + return result; + if(eos) + ctx->read_eos = TRUE; + + if(nread) { + /* actually got bytes, wrap them into the chunkbuf */ + char hd[11] = ""; + int hdlen; + size_t n; + + hdlen = msnprintf(hd, sizeof(hd), "%zx\r\n", nread); + if(hdlen <= 0) + return CURLE_READ_ERROR; + /* On a soft-limited bufq, we do not need to check that all was written */ + result = Curl_bufq_cwrite(&ctx->chunkbuf, hd, hdlen, &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n); + 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); + if(result) + return result; + } + + if(ctx->read_eos) + return add_last_chunk(data, reader); + return CURLE_OK; +} + +static CURLcode cr_chunked_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct chunked_reader *ctx = reader->ctx; + CURLcode result = CURLE_READ_ERROR; + + *pnread = 0; + *peos = ctx->eos; + + if(!ctx->eos) { + if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) { + /* Still getting data form the next reader, buffer is empty */ + result = add_chunk(data, reader, buf, blen); + if(result) + return result; + } + + if(!Curl_bufq_is_empty(&ctx->chunkbuf)) { + result = Curl_bufq_cread(&ctx->chunkbuf, buf, blen, pnread); + if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) { + /* no more data, read all, done. */ + ctx->eos = TRUE; + *peos = TRUE; + } + return result; + } + } + /* We may get here, because we are done or because callbacks paused */ + DEBUGASSERT(ctx->eos || !ctx->read_eos); + return CURLE_OK; +} + +static curl_off_t cr_chunked_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return -1; +} + +/* HTTP chunked Transfer-Encoding encoder */ +const struct Curl_crtype Curl_httpchunk_encoder = { + "chunked", + cr_chunked_init, + cr_chunked_read, + cr_chunked_close, + Curl_creader_def_needs_rewind, + cr_chunked_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_done, + sizeof(struct chunked_reader) +}; + +CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &Curl_httpchunk_encoder, + CURL_CR_TRANSFER_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + #endif /* CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index 07f2984..d3ecc36 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h
@@ -133,6 +133,13 @@ extern const struct Curl_cwtype Curl_httpchunk_unencoder; +extern const struct Curl_crtype Curl_httpchunk_encoder; + +/** + * Add a transfer-encoding "chunked" reader to the transfers reader stack + */ +CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data); + #endif /* !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_HTTP_CHUNKS_H */
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c index 153e3d4..4cbe2df 100644 --- a/Utilities/cmcurl/lib/http_negotiate.c +++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -120,16 +120,29 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, struct connectdata *conn, bool proxy) { - struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg : - &conn->negotiate; - struct auth *authp = proxy ? &data->state.authproxy : &data->state.authhost; - curlnegotiate *state = proxy ? &conn->proxy_negotiate_state : - &conn->http_negotiate_state; + struct negotiatedata *neg_ctx; + struct auth *authp; + curlnegotiate *state; char *base64 = NULL; size_t len = 0; char *userp; CURLcode result; + if(proxy) { +#ifndef CURL_DISABLE_PROXY + neg_ctx = &conn->proxyneg; + authp = &data->state.authproxy; + state = &conn->proxy_negotiate_state; +#else + return CURLE_NOT_BUILT_IN; +#endif + } + else { + neg_ctx = &conn->negotiate; + authp = &data->state.authhost; + state = &conn->http_negotiate_state; + } + authp->done = FALSE; if(*state == GSS_AUTHRECV) { @@ -171,8 +184,10 @@ base64); if(proxy) { +#ifndef CURL_DISABLE_PROXY Curl_safefree(data->state.aptr.proxyuserpwd); data->state.aptr.proxyuserpwd = userp; +#endif } else { Curl_safefree(data->state.aptr.userpwd);
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index b845ddf..3dccb5c 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -40,7 +40,6 @@ #include "strcase.h" #include "http_ntlm.h" #include "curl_ntlm_core.h" -#include "curl_ntlm_wb.h" #include "curl_base64.h" #include "vauth/vauth.h" #include "url.h" @@ -266,10 +265,6 @@ { Curl_auth_cleanup_ntlm(&conn->ntlm); Curl_auth_cleanup_ntlm(&conn->proxyntlm); - -#if defined(NTLM_WB_ENABLED) - Curl_http_auth_cleanup_ntlm_wb(conn); -#endif } #endif /* !CURL_DISABLE_HTTP && USE_NTLM */
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index 113c43a..7c035d4 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -293,7 +293,7 @@ struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 0, http_proxy_cf_destroy, http_proxy_cf_connect,
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index 81a177f..c795672 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c
@@ -50,6 +50,63 @@ #include "curl_memory.h" #include "memdebug.h" +/* for macOS and iOS targets */ +#if defined(USE_APPLE_IDN) +#include <unicode/uidna.h> + +static CURLcode mac_idn_to_ascii(const char *in, char **out) +{ + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); + if(U_FAILURE(err)) { + return CURLE_OUT_OF_MEMORY; + } + else { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[256] = {0}; + (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer, + sizeof(buffer), &info, &err); + uidna_close(idna); + if(U_FAILURE(err)) { + return CURLE_URL_MALFORMAT; + } + else { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } +} + +static CURLcode mac_ascii_to_idn(const char *in, char **out) +{ + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); + if(U_FAILURE(err)) { + return CURLE_OUT_OF_MEMORY; + } + else { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[256] = {0}; + (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer, + sizeof(buffer), &info, &err); + uidna_close(idna); + if(U_FAILURE(err)) { + return CURLE_URL_MALFORMAT; + } + else { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } +} +#endif + #ifdef USE_WIN32_IDN /* using Windows kernel32 and normaliz libraries. */ @@ -181,6 +238,8 @@ result = CURLE_NOT_BUILT_IN; #elif defined(USE_WIN32_IDN) result = win32_idn_to_ascii(input, &decoded); +#elif defined(USE_APPLE_IDN) + result = mac_idn_to_ascii(input, &decoded); #endif if(!result) *output = decoded; @@ -198,6 +257,10 @@ CURLcode result = win32_ascii_to_idn(puny, &enc); if(result) return result; +#elif defined(USE_APPLE_IDN) + CURLcode result = mac_ascii_to_idn(puny, &enc); + if(result) + return result; #endif *output = enc; return CURLE_OK; @@ -246,11 +309,7 @@ */ void Curl_free_idnconverted_hostname(struct hostname *host) { - if(host->encalloc) { - /* must be freed with idn2_free() if allocated by libidn */ - Curl_idn_free(host->encalloc); - host->encalloc = NULL; - } + Curl_safefree(host->encalloc); } #endif /* USE_IDN */ @@ -267,20 +326,11 @@ /* Check name for non-ASCII and convert hostname if we can */ if(!Curl_is_ASCII_name(host->name)) { char *decoded; - CURLcode result = idn_decode(host->name, &decoded); - if(!result) { - if(!*decoded) { - /* zero length is a bad host name */ - Curl_idn_free(decoded); - return CURLE_URL_MALFORMAT; - } - /* successful */ - host->encalloc = decoded; - /* change the name pointer to point to the encoded hostname */ - host->name = host->encalloc; - } - else + CURLcode result = Curl_idn_decode(host->name, &decoded); + if(result) return result; + /* successful */ + host->name = host->encalloc = decoded; } #endif return CURLE_OK;
diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h index 74bbcaf..2bdce89 100644 --- a/Utilities/cmcurl/lib/idn.h +++ b/Utilities/cmcurl/lib/idn.h
@@ -26,16 +26,11 @@ bool Curl_is_ASCII_name(const char *hostname); CURLcode Curl_idnconvert_hostname(struct hostname *host); -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) #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); -#ifdef USE_LIBIDN2 -#define Curl_idn_free(x) idn2_free(x) -#else -#define Curl_idn_free(x) free(x) -#endif #else #define Curl_free_idnconverted_hostname(x)
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index 5249f6c..42e1450 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c
@@ -62,7 +62,7 @@ /* ------------------------------------------------------------------ */ -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 /* Return the scope of the given address. */ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) { @@ -97,17 +97,17 @@ #if defined(HAVE_GETIFADDRS) if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int remote_scope, unsigned int local_scope_id, #endif const char *interf, - char *buf, int buf_size) + char *buf, size_t buf_size) { struct ifaddrs *iface, *head; if2ip_result_t res = IF2IP_NOT_FOUND; -#if defined(ENABLE_IPV6) && \ +#if defined(USE_IPV6) && \ !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) (void) local_scope_id; #endif @@ -121,7 +121,7 @@ const char *ip; char scope[12] = ""; char ipstr[64]; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(af == AF_INET6) { #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID unsigned int scopeid = 0; @@ -182,12 +182,12 @@ #elif defined(HAVE_IOCTL_SIOCGIFADDR) if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int remote_scope, unsigned int local_scope_id, #endif const char *interf, - char *buf, int buf_size) + char *buf, size_t buf_size) { struct ifreq req; struct in_addr in; @@ -196,7 +196,7 @@ size_t len; const char *r; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 (void)remote_scope; (void)local_scope_id; #endif @@ -237,15 +237,15 @@ #else if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int remote_scope, unsigned int local_scope_id, #endif const char *interf, - char *buf, int buf_size) + char *buf, size_t buf_size) { (void) af; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 (void) remote_scope; (void) local_scope_id; #endif
diff --git a/Utilities/cmcurl/lib/if2ip.h b/Utilities/cmcurl/lib/if2ip.h index 1f97350..f4b2f4c 100644 --- a/Utilities/cmcurl/lib/if2ip.h +++ b/Utilities/cmcurl/lib/if2ip.h
@@ -32,7 +32,7 @@ #define IPV6_SCOPE_UNIQUELOCAL 3 /* Unique local */ #define IPV6_SCOPE_NODELOCAL 4 /* Loopback. */ -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int Curl_ipv6_scope(const struct sockaddr *sa); #else #define Curl_ipv6_scope(x) 0 @@ -45,12 +45,12 @@ } if2ip_result_t; if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int remote_scope, unsigned int local_scope_id, #endif const char *interf, - char *buf, int buf_size); + char *buf, size_t buf_size); #ifdef __INTERIX
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index f9211d9..f6e98bd 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c
@@ -117,7 +117,7 @@ */ const struct Curl_handler Curl_handler_imap = { - "IMAP", /* scheme */ + "imap", /* scheme */ imap_setup_connection, /* setup_connection */ imap_do, /* do_it */ imap_done, /* done */ @@ -131,6 +131,7 @@ ZERO_NULL, /* perform_getsock */ imap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_IMAP, /* defport */ @@ -146,7 +147,7 @@ */ const struct Curl_handler Curl_handler_imaps = { - "IMAPS", /* scheme */ + "imaps", /* scheme */ imap_setup_connection, /* setup_connection */ imap_do, /* do_it */ imap_done, /* done */ @@ -160,6 +161,7 @@ ZERO_NULL, /* perform_getsock */ imap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_IMAPS, /* defport */ @@ -770,6 +772,7 @@ return CURLE_URL_MALFORMAT; } +#ifndef CURL_DISABLE_MIME /* Prepare the mime data if some. */ if(data->set.mimepost.kind != MIMEKIND_NONE) { /* Use the whole structure as data. */ @@ -785,18 +788,18 @@ result = Curl_mime_add_header(&data->set.mimepost.curlheaders, "Mime-Version: 1.0"); - /* Make sure we will read the entire mime structure. */ if(!result) - result = Curl_mime_rewind(&data->set.mimepost); - + result = Curl_creader_set_mime(data, &data->set.mimepost); if(result) return result; - - data->state.infilesize = Curl_mime_size(&data->set.mimepost); - - /* Read from mime structure. */ - data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) &data->set.mimepost; + data->state.infilesize = Curl_creader_client_length(data); + } + else +#endif + { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + return result; } /* Check we know the size of the upload */ @@ -1211,14 +1214,14 @@ if(data->req.bytecount == size) /* The entire data is already transferred! */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); else { /* IMAP download */ data->req.maxdownload = size; /* force a recv/send check of this connection, as the data might've been read off the socket already */ data->state.select_bits = CURL_CSELECT_IN; - Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, size, FALSE, -1); } } else { @@ -1266,7 +1269,7 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); /* IMAP upload */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); /* End of DO phase */ imap_state(data, IMAP_STOP); @@ -1297,7 +1300,6 @@ struct connectdata *conn) { CURLcode result = CURLE_OK; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; int imapcode; struct imap_conn *imapc = &conn->proto.imapc; struct pingpong *pp = &imapc->pp; @@ -1314,7 +1316,7 @@ do { /* Read the response from the server */ - result = Curl_pp_readresp(data, sock, pp, &imapcode, &nread); + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &imapcode, &nread); if(result) return result; @@ -1513,10 +1515,10 @@ } else if(!data->set.connect_only && !imap->custom && (imap->uid || imap->mindex || data->state.upload || - data->set.mimepost.kind != MIMEKIND_NONE)) { + IS_MIME_POST(data))) { /* Handle responses after FETCH or APPEND transfer has finished */ - if(!data->state.upload && data->set.mimepost.kind == MIMEKIND_NONE) + if(!data->state.upload && !IS_MIME_POST(data)) imap_state(data, IMAP_FETCH_FINAL); else { /* End the APPEND command first by sending an empty line */ @@ -1582,7 +1584,7 @@ selected = TRUE; /* Start the first command in the DO phase */ - if(data->state.upload || data->set.mimepost.kind != MIMEKIND_NONE) + if(data->state.upload || IS_MIME_POST(data)) /* APPEND can be executed directly */ result = imap_perform_append(data); else if(imap->custom && (selected || !imap->mailbox)) @@ -1692,7 +1694,7 @@ if(imap->transfer != PPTRANSFER_BODY) /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c index c9cee0c..4520b87 100644 --- a/Utilities/cmcurl/lib/inet_ntop.c +++ b/Utilities/cmcurl/lib/inet_ntop.c
@@ -42,11 +42,11 @@ #define INT16SZ 2 /* - * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make * sure we have _some_ value for AF_INET6 without polluting our fake value * everywhere. */ -#if !defined(ENABLE_IPV6) && !defined(AF_INET6) +#if !defined(USE_IPV6) && !defined(AF_INET6) #define AF_INET6 (AF_INET + 1) #endif
diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c index 176cc95..9cfcec1 100644 --- a/Utilities/cmcurl/lib/inet_pton.c +++ b/Utilities/cmcurl/lib/inet_pton.c
@@ -39,11 +39,11 @@ #define INT16SZ 2 /* - * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make * sure we have _some_ value for AF_INET6 without polluting our fake value * everywhere. */ -#if !defined(ENABLE_IPV6) && !defined(AF_INET6) +#if !defined(USE_IPV6) && !defined(AF_INET6) #define AF_INET6 (AF_INET + 1) #endif
diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index 4db19fb..d355665 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c
@@ -52,6 +52,7 @@ #include "ftp.h" #include "curl_gssapi.h" #include "sendf.h" +#include "transfer.h" #include "curl_krb5.h" #include "warnless.h" #include "strcase.h" @@ -65,7 +66,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, const char *cmd) { - ssize_t bytes_written; + size_t bytes_written; #define SBUF_SIZE 1024 char s[SBUF_SIZE]; size_t write_len; @@ -90,8 +91,7 @@ #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len, - &bytes_written); + result = Curl_xfer_send(data, sptr, write_len, &bytes_written); #ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; @@ -100,9 +100,9 @@ if(result) break; - Curl_debug(data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written); + Curl_debug(data, CURLINFO_HEADER_OUT, sptr, bytes_written); - if(bytes_written != (ssize_t)write_len) { + if(bytes_written != write_len) { write_len -= bytes_written; sptr += bytes_written; } @@ -470,7 +470,7 @@ ssize_t nread = 0; while(len > 0) { - nread = Curl_conn_recv(data, sockindex, to_p, len, &result); + result = Curl_conn_recv(data, sockindex, to_p, len, &nread); if(nread > 0) { len -= nread; to_p += nread; @@ -494,11 +494,11 @@ { const char *to_p = to; CURLcode result; - ssize_t written; + size_t written; while(len > 0) { - written = Curl_conn_send(data, sockindex, to_p, len, &result); - if(written > 0) { + result = Curl_conn_send(data, sockindex, to_p, len, &written); + if(!result && written > 0) { len -= written; to_p += written; } @@ -524,24 +524,33 @@ return result; if(len) { - /* only realloc if there was a length */ len = ntohl(len); if(len > CURL_MAX_INPUT_LENGTH) - len = 0; - else - buf->data = Curl_saferealloc(buf->data, len); - } - if(!len || !buf->data) - return CURLE_OUT_OF_MEMORY; + return CURLE_TOO_LARGE; - result = socket_read(data, sockindex, buf->data, len); - if(result) - return result; - nread = conn->mech->decode(conn->app_data, buf->data, len, - conn->data_prot, conn); + Curl_dyn_reset(&buf->buf); + } + else + return CURLE_RECV_ERROR; + + do { + char buffer[1024]; + nread = CURLMIN(len, (int)sizeof(buffer)); + result = socket_read(data, sockindex, buffer, nread); + if(result) + return result; + result = Curl_dyn_addn(&buf->buf, buffer, nread); + if(result) + return result; + len -= nread; + } while(len); + /* this decodes the dynbuf *in place* */ + nread = conn->mech->decode(conn->app_data, + Curl_dyn_ptr(&buf->buf), + len, conn->data_prot, conn); if(nread < 0) return CURLE_RECV_ERROR; - buf->size = (size_t)nread; + Curl_dyn_setlen(&buf->buf, nread); buf->index = 0; return CURLE_OK; } @@ -549,9 +558,10 @@ static size_t buffer_read(struct krb5buffer *buf, void *data, size_t len) { - if(buf->size - buf->index < len) - len = buf->size - buf->index; - memcpy(data, (char *)buf->data + buf->index, len); + size_t size = Curl_dyn_len(&buf->buf); + if(size - buf->index < len) + len = size - buf->index; + memcpy(data, Curl_dyn_ptr(&buf->buf) + buf->index, len); buf->index += len; return len; } @@ -567,8 +577,11 @@ *err = CURLE_OK; /* Handle clear text response. */ - if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return Curl_conn_recv(data, sockindex, buffer, len, err); + if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) { + ssize_t nread; + *err = Curl_conn_recv(data, sockindex, buffer, len, &nread); + return nread; + } if(conn->in_buffer.eof_flag) { conn->in_buffer.eof_flag = 0; @@ -583,7 +596,7 @@ while(len > 0) { if(read_data(data, sockindex, &conn->in_buffer)) return -1; - if(conn->in_buffer.size == 0) { + if(Curl_dyn_len(&conn->in_buffer.buf) == 0) { if(bytes_read > 0) conn->in_buffer.eof_flag = 1; return bytes_read; @@ -832,6 +845,7 @@ mech->name); return CURLE_FAILED_INIT; } + Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH); } infof(data, "Trying mechanism %s...", mech->name); @@ -896,15 +910,10 @@ { if(conn->mech && conn->mech->end) conn->mech->end(conn->app_data); - free(conn->app_data); - conn->app_data = NULL; - if(conn->in_buffer.data) { - free(conn->in_buffer.data); - conn->in_buffer.data = NULL; - conn->in_buffer.size = 0; - conn->in_buffer.index = 0; - conn->in_buffer.eof_flag = 0; - } + Curl_safefree(conn->app_data); + Curl_dyn_free(&conn->in_buffer.buf); + conn->in_buffer.index = 0; + conn->in_buffer.eof_flag = 0; conn->sec_complete = 0; conn->data_prot = PROT_CLEAR; conn->mech = NULL;
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 4c04647..678b4d5 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c
@@ -164,7 +164,7 @@ */ const struct Curl_handler Curl_handler_ldap = { - "LDAP", /* scheme */ + "ldap", /* scheme */ ZERO_NULL, /* setup_connection */ ldap_do, /* do_it */ ZERO_NULL, /* done */ @@ -178,6 +178,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_LDAP, /* defport */ @@ -192,7 +193,7 @@ */ const struct Curl_handler Curl_handler_ldaps = { - "LDAPS", /* scheme */ + "ldaps", /* scheme */ ZERO_NULL, /* setup_connection */ ldap_do, /* do_it */ ZERO_NULL, /* done */ @@ -206,6 +207,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_LDAPS, /* defport */ @@ -371,7 +373,7 @@ #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP /* Win32 LDAP SDK doesn't support insecure mode without CA! */ - server = ldap_sslinit(host, conn->port, 1); + server = ldap_sslinit(host, conn->primary.remote_port, 1); ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); #else int ldap_option; @@ -417,10 +419,10 @@ result = CURLE_SSL_CERTPROBLEM; goto quit; } - server = ldapssl_init(host, conn->port, 1); + server = ldapssl_init(host, conn->primary.remote_port, 1); if(!server) { failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, conn->port); + conn->host.dispname, conn->primary.remote_port); result = CURLE_COULDNT_CONNECT; goto quit; } @@ -458,10 +460,10 @@ result = CURLE_SSL_CERTPROBLEM; goto quit; } - server = ldap_init(host, conn->port); + server = ldap_init(host, conn->primary.remote_port); if(!server) { failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, conn->port); + conn->host.dispname, conn->primary.remote_port); result = CURLE_COULDNT_CONNECT; goto quit; } @@ -483,6 +485,8 @@ } */ #else + (void)ldap_option; + (void)ldap_ca; /* we should probably never come up to here since configure should check in first place if we can support LDAP SSL/TLS */ failf(data, "LDAP local: SSL/TLS not supported with this version " @@ -499,10 +503,10 @@ goto quit; } else { - server = ldap_init(host, conn->port); + server = ldap_init(host, conn->primary.remote_port); if(!server) { failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, conn->port); + conn->host.dispname, conn->primary.remote_port); result = CURLE_COULDNT_CONNECT; goto quit; } @@ -544,7 +548,8 @@ goto quit; } - for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg); + num = 0; + for(entryIterator = ldap_first_entry(server, ldapmsg); entryIterator; entryIterator = ldap_next_entry(server, entryIterator), num++) { BerElement *ber = NULL; @@ -749,7 +754,7 @@ FREE_ON_WINLDAP(host); /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); connclose(conn, "LDAP connection always disable reuse"); return result;
diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index 5b6b033..716f0cd 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c
@@ -89,6 +89,22 @@ } /* + * Curl_llist_append() + * + * Adds a new list element to the end of the list. + * + * The 'ne' argument should be a pointer into the object to store. + * + * @unittest: 1300 + */ +void +Curl_llist_append(struct Curl_llist *list, const void *p, + struct Curl_llist_element *ne) +{ + Curl_llist_insert_next(list, list->tail, p, ne); +} + +/* * @unittest: 1300 */ void
diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h index 320580e..d75582f 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h
@@ -45,6 +45,8 @@ void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor); void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *, const void *, struct Curl_llist_element *node); +void Curl_llist_append(struct Curl_llist *, + const void *, struct Curl_llist_element *node); void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *, void *); size_t Curl_llist_count(struct Curl_llist *);
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index 067c211..db00281 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c
@@ -28,6 +28,7 @@ #include <string.h> +#include "strdup.h" #include "curl_md4.h" #include "warnless.h" @@ -54,7 +55,8 @@ #else #include <mbedtls/config.h> #endif -#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) +#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \ + (MBEDTLS_VERSION_NUMBER < 0x03000000) #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS #endif #endif /* USE_MBEDTLS */
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index d712331..a2356c4 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c
@@ -74,6 +74,7 @@ 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); +static curl_off_t mime_size(curl_mimepart *part); static const struct mime_encoder encoders[] = { {"binary", encoder_nop_read, encoder_nop_size}, @@ -1412,36 +1413,35 @@ char *base; struct_stat sbuf; - if(stat(filename, &sbuf) || access(filename, R_OK)) + if(stat(filename, &sbuf)) result = CURLE_READ_ERROR; - - part->data = strdup(filename); - if(!part->data) - result = CURLE_OUT_OF_MEMORY; - - part->datasize = -1; - if(!result && S_ISREG(sbuf.st_mode)) { - part->datasize = filesize(filename, sbuf); - part->seekfunc = mime_file_seek; - } - - part->readfunc = mime_file_read; - part->freefunc = mime_file_free; - part->kind = MIMEKIND_FILE; - - /* As a side effect, set the filename to the current file's base name. - It is possible to withdraw this by explicitly calling - curl_mime_filename() with a NULL filename argument after the current - call. */ - base = strippath(filename); - if(!base) - result = CURLE_OUT_OF_MEMORY; else { - CURLcode res = curl_mime_filename(part, base); + part->data = strdup(filename); + if(!part->data) + result = CURLE_OUT_OF_MEMORY; + else { + part->datasize = -1; + if(S_ISREG(sbuf.st_mode)) { + part->datasize = filesize(filename, sbuf); + part->seekfunc = mime_file_seek; + } - if(res) - result = res; - free(base); + part->readfunc = mime_file_read; + part->freefunc = mime_file_free; + part->kind = MIMEKIND_FILE; + + /* As a side effect, set the filename to the current file's base name. + It is possible to withdraw this by explicitly calling + curl_mime_filename() with a NULL filename argument after the current + call. */ + base = strippath(filename); + if(!base) + result = CURLE_OUT_OF_MEMORY; + else { + result = curl_mime_filename(part, base); + free(base); + } + } } } return result; @@ -1602,7 +1602,7 @@ } /* Rewind mime stream. */ -CURLcode Curl_mime_rewind(curl_mimepart *part) +static CURLcode mime_rewind(curl_mimepart *part) { return mime_part_rewind(part) == CURL_SEEKFUNC_OK? CURLE_OK: CURLE_SEND_FAIL_REWIND; @@ -1634,7 +1634,7 @@ size = boundarysize; /* Final boundary - CRLF after headers. */ for(part = mime->firstpart; part; part = part->nextpart) { - curl_off_t sz = Curl_mime_size(part); + curl_off_t sz = mime_size(part); if(sz < 0) size = sz; @@ -1647,7 +1647,7 @@ } /* Get/compute mime size. */ -curl_off_t Curl_mime_size(curl_mimepart *part) +static curl_off_t mime_size(curl_mimepart *part) { curl_off_t size; @@ -1896,7 +1896,7 @@ } /* Recursively reset paused status in the given part. */ -void Curl_mime_unpause(curl_mimepart *part) +static void mime_unpause(curl_mimepart *part) { if(part) { if(part->lastreadstatus == CURL_READFUNC_PAUSE) @@ -1908,12 +1908,228 @@ curl_mimepart *subpart; for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) - Curl_mime_unpause(subpart); + mime_unpause(subpart); } } } } +struct cr_mime_ctx { + struct Curl_creader super; + curl_mimepart *part; + curl_off_t total_len; + curl_off_t read_len; + CURLcode error_result; + BIT(seen_eos); + BIT(errored); +}; + +static CURLcode cr_mime_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + ctx->total_len = -1; + ctx->read_len = 0; + return CURLE_OK; +} + +/* Real client reader to installed client callbacks. */ +static CURLcode cr_mime_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_mime_ctx *ctx = reader->ctx; + size_t nread; + + /* Once we have errored, we will return the same error forever */ + if(ctx->errored) { + *pnread = 0; + *peos = FALSE; + return ctx->error_result; + } + if(ctx->seen_eos) { + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + /* respect length limitations */ + if(ctx->total_len >= 0) { + curl_off_t remain = ctx->total_len - ctx->read_len; + if(remain <= 0) + blen = 0; + else if(remain < (curl_off_t)blen) + blen = (size_t)remain; + } + nread = 0; + if(blen) { + nread = Curl_mime_read(buf, 1, blen, ctx->part); + } + + switch(nread) { + case 0: + if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { + failf(data, "client mime read EOF fail, " + "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T + " of needed bytes read", ctx->read_len, ctx->total_len); + return CURLE_READ_ERROR; + } + *pnread = 0; + *peos = TRUE; + ctx->seen_eos = TRUE; + break; + + case CURL_READFUNC_ABORT: + failf(data, "operation aborted by callback"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_ABORTED_BY_CALLBACK; + return CURLE_ABORTED_BY_CALLBACK; + + case CURL_READFUNC_PAUSE: + /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ + data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ + *pnread = 0; + *peos = FALSE; + break; /* nothing was read */ + + default: + if(nread > blen) { + /* the read function returned a too large value */ + failf(data, "read function returned funny value"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_READ_ERROR; + return CURLE_READ_ERROR; + } + ctx->read_len += nread; + if(ctx->total_len >= 0) + ctx->seen_eos = (ctx->read_len >= ctx->total_len); + *pnread = nread; + *peos = ctx->seen_eos; + break; + } + DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T + ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d", + blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos)); + return CURLE_OK; +} + +static bool cr_mime_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return ctx->read_len > 0; +} + +static curl_off_t cr_mime_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return ctx->total_len; +} + +static CURLcode cr_mime_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + struct cr_mime_ctx *ctx = reader->ctx; + + if(offset > 0) { + curl_off_t passed = 0; + + do { + char scratch[4*1024]; + size_t readthisamountnow = + (offset - passed > (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : + curlx_sotouz(offset - passed); + size_t nread; + + nread = Curl_mime_read(scratch, 1, readthisamountnow, ctx->part); + passed += (curl_off_t)nread; + if((nread == 0) || (nread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T + " bytes from the mime post", passed); + return CURLE_READ_ERROR; + } + } while(passed < offset); + + /* now, decrease the size of the read */ + if(ctx->total_len > 0) { + ctx->total_len -= offset; + + if(ctx->total_len <= 0) { + failf(data, "Mime post already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we've passed, proceed as normal */ + } + return CURLE_OK; +} + +static CURLcode cr_mime_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + CURLcode result = mime_rewind(ctx->part); + if(result) + failf(data, "Cannot rewind mime/post data"); + return result; +} + +static CURLcode cr_mime_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + mime_unpause(ctx->part); + return CURLE_OK; +} + +static const struct Curl_crtype cr_mime = { + "cr-mime", + cr_mime_init, + cr_mime_read, + Curl_creader_def_close, + cr_mime_needs_rewind, + cr_mime_total_length, + cr_mime_resume_from, + cr_mime_rewind, + cr_mime_unpause, + Curl_creader_def_done, + sizeof(struct cr_mime_ctx) +}; + +CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part) +{ + struct Curl_creader *r; + struct cr_mime_ctx *ctx; + CURLcode result; + + result = Curl_creader_create(&r, data, &cr_mime, CURL_CR_CLIENT); + if(result) + return result; + ctx = r->ctx; + ctx->part = part; + /* Make sure we will read the entire mime structure. */ + result = mime_rewind(ctx->part); + if(result) { + Curl_creader_free(data, r); + return result; + } + ctx->total_len = mime_size(ctx->part); + + return Curl_creader_set(data, r); +} #else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h index a64f41d..954b3cc 100644 --- a/Utilities/cmcurl/lib/mime.h +++ b/Utilities/cmcurl/lib/mime.h
@@ -151,12 +151,15 @@ const char *contenttype, const char *disposition, enum mimestrategy strategy); -curl_off_t Curl_mime_size(struct curl_mimepart *part); size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream); -CURLcode Curl_mime_rewind(struct curl_mimepart *part); const char *Curl_mime_contenttype(const char *filename); -void Curl_mime_unpause(struct curl_mimepart *part); + +/** + * Install a client reader as upload source that reads the given + * mime part. + */ +CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part); #else /* if disabled */ @@ -165,10 +168,8 @@ #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_size(x) (curl_off_t) -1 #define Curl_mime_read NULL -#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN) -#define Curl_mime_unpause(x) +#define Curl_creader_set_mime(x,y) ((void)x, CURLE_NOT_BUILT_IN) #endif
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index 63f7f24..1829abc 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c
@@ -49,16 +49,6 @@ #endif /* - * Non-ANSI integer extensions - */ - -#if (defined(_WIN32_WCE)) || \ - (defined(__MINGW32__)) || \ - (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)) -# define MP_HAVE_INT_EXTENSIONS -#endif - -/* * Max integer data types that mprintf.c is capable */ @@ -87,7 +77,7 @@ #define OUTCHAR(x) \ do { \ - if(!stream(x, userp)) \ + if(!stream((unsigned char)x, userp)) \ done++; \ else \ return done; /* return on failure */ \ @@ -253,7 +243,7 @@ struct va_input *iptr; bool loopit = TRUE; fmt++; - outlen = fmt - start - 1; + outlen = (size_t)(fmt - start - 1); if(*fmt == '%') { /* this means a %% that should be output only as %. Create an output segment. */ @@ -271,7 +261,8 @@ continue; /* while */ } - flags = width = precision = 0; + flags = 0; + width = precision = 0; if(use_dollar != DOLLAR_NOPE) { param = dollarstring(fmt, &fmt); @@ -301,7 +292,7 @@ break; case '-': flags |= FLAGS_LEFT; - flags &= ~FLAGS_PAD_NIL; + flags &= ~(unsigned int)FLAGS_PAD_NIL; break; case '#': flags |= FLAGS_ALT; @@ -349,8 +340,9 @@ case 'h': flags |= FLAGS_SHORT; break; -#if defined(MP_HAVE_INT_EXTENSIONS) +#if defined(_WIN32) || defined(_WIN32_WCE) case 'I': + /* Non-ANSI integer extensions I32 I64 */ if((fmt[0] == '3') && (fmt[1] == '2')) { flags |= FLAGS_LONG; fmt += 2; @@ -367,7 +359,7 @@ #endif } break; -#endif +#endif /* _WIN32 || _WIN32_WCE */ case 'l': if(flags & FLAGS_LONG) flags |= FLAGS_LONGLONG; @@ -558,7 +550,7 @@ optr = &out[ocount++]; if(ocount > MAX_SEGMENTS) return PFMT_MANYSEGS; - optr->input = param; + optr->input = (unsigned int)param; optr->flags = flags; optr->width = width; optr->precision = precision; @@ -571,7 +563,7 @@ } /* is there a trailing piece */ - outlen = fmt - start; + outlen = (size_t)(fmt - start); if(outlen) { optr = &out[ocount++]; if(ocount > MAX_SEGMENTS) @@ -651,7 +643,7 @@ * On success, the input array describes the type of all arguments and their * values. * - * The function then iterates over the output sengments and outputs them one + * The function then iterates over the output segments and outputs them one * by one until done. Using the appropriate input arguments (if any). * * All output is sent to the 'stream()' callback, one byte at a time. @@ -697,7 +689,7 @@ mp_intmax_t signed_num; /* Used to convert negative in positive. */ char *w; size_t outlen = optr->outlen; - int flags = optr->flags; + unsigned int flags = optr->flags; if(outlen) { char *str = optr->start; @@ -719,7 +711,7 @@ else width = -width; flags |= FLAGS_LEFT; - flags &= ~FLAGS_PAD_NIL; + flags &= ~(unsigned int)FLAGS_PAD_NIL; } } else @@ -876,7 +868,7 @@ str = nilstr; len = sizeof(nilstr) - 1; /* Disable quotes around (nil) */ - flags &= (~FLAGS_ALT); + flags &= ~(unsigned int)FLAGS_ALT; } else { str = ""; @@ -895,13 +887,13 @@ if(flags & FLAGS_ALT) OUTCHAR('"'); - if(!(flags&FLAGS_LEFT)) + if(!(flags & FLAGS_LEFT)) while(width-- > 0) OUTCHAR(' '); for(; len && *str; len--) OUTCHAR(*str++); - if(flags&FLAGS_LEFT) + if(flags & FLAGS_LEFT) while(width-- > 0) OUTCHAR(' '); @@ -961,12 +953,13 @@ *fptr = 0; if(width >= 0) { + size_t dlen; if(width >= (int)sizeof(work)) width = sizeof(work)-1; /* RECURSIVE USAGE */ - len = curl_msnprintf(fptr, left, "%d", width); - fptr += len; - left -= len; + dlen = (size_t)curl_msnprintf(fptr, left, "%d", width); + fptr += dlen; + left -= dlen; } if(prec >= 0) { /* for each digit in the integer part, we can have one less @@ -974,7 +967,7 @@ size_t maxprec = sizeof(work) - 2; double val = iptr->val.dnum; if(width > 0 && prec <= width) - maxprec -= width; + maxprec -= (size_t)width; while(val >= 10.0) { val /= 10; maxprec--; @@ -1048,7 +1041,7 @@ struct nsprintf *infop = f; if(infop->length < infop->max) { /* only do this if we haven't reached max length yet */ - *infop->buffer++ = outc; /* store */ + *infop->buffer++ = (char)outc; /* store */ infop->length++; /* we are now one byte larger */ return 0; /* fputc() returns like this on success */ } @@ -1148,7 +1141,7 @@ static int storebuffer(unsigned char outc, void *f) { char **buffer = f; - **buffer = outc; + **buffer = (char)outc; (*buffer)++; return 0; } @@ -1169,9 +1162,7 @@ int out = outc; FILE *s = f; int rc = fputc(out, s); - if(rc == out) - return 0; - return 1; + return rc == EOF; } int curl_mprintf(const char *format, ...)
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 5a9d6d0..4ca24eb 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c
@@ -75,7 +75,7 @@ */ const struct Curl_handler Curl_handler_mqtt = { - "MQTT", /* scheme */ + "mqtt", /* scheme */ mqtt_setup_conn, /* setup_connection */ mqtt_do, /* do_it */ mqtt_done, /* done */ @@ -89,6 +89,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_MQTT, /* defport */ @@ -119,12 +120,12 @@ { CURLcode result = CURLE_OK; struct MQTT *mq = data->req.p.mqtt; - ssize_t n; - result = Curl_nwrite(data, FIRSTSOCKET, buf, len, &n); + size_t n; + result = Curl_xfer_send(data, buf, len, &n); if(result) return result; Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); - if(len != (size_t)n) { + if(len != n) { size_t nsend = len - n; char *sendleftovers = Curl_memdup(&buf[n], nsend); if(!sendleftovers) @@ -366,8 +367,7 @@ ssize_t nread; DEBUGASSERT(nbytes - rlen < sizeof(readbuf)); - result = Curl_read(data, data->conn->sock[FIRSTSOCKET], - (char *)readbuf, nbytes - rlen, &nread); + result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread); if(result) return result; DEBUGASSERT(nread >= 0); @@ -622,7 +622,6 @@ { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; ssize_t nread; size_t remlen; struct mqtt_conn *mqtt = &conn->proto.mqtt; @@ -679,7 +678,7 @@ size_t rest = mq->npacket; if(rest > sizeof(buffer)) rest = sizeof(buffer); - result = Curl_read(data, sockfd, buffer, rest, &nread); + result = Curl_xfer_recv(data, buffer, rest, &nread); if(result) { if(CURLE_AGAIN == result) { infof(data, "EEEE AAAAGAIN"); @@ -744,7 +743,6 @@ struct mqtt_conn *mqtt = &conn->proto.mqtt; struct MQTT *mq = data->req.p.mqtt; ssize_t nread; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; unsigned char byte; *done = FALSE; @@ -762,7 +760,7 @@ switch(mqtt->state) { case MQTT_FIRST: /* Read the initial byte only */ - result = Curl_read(data, sockfd, (char *)&mq->firstbyte, 1, &nread); + result = Curl_xfer_recv(data, (char *)&mq->firstbyte, 1, &nread); if(result) break; else if(!nread) { @@ -778,13 +776,13 @@ FALLTHROUGH(); case MQTT_REMAINING_LENGTH: do { - result = Curl_read(data, sockfd, (char *)&byte, 1, &nread); - if(!nread) + result = Curl_xfer_recv(data, (char *)&byte, 1, &nread); + if(result || !nread) break; Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1); mq->pkt_hd[mq->npacket++] = byte; } while((byte & 0x80) && (mq->npacket < 4)); - if(nread && (byte & 0x80)) + if(!result && nread && (byte & 0x80)) /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 + 127 * 128^3 bytes. server tried to send more */ result = CURLE_WEIRD_SERVER_REPLY;
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 0926b0d..6bbdfe2 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c
@@ -86,6 +86,8 @@ ((x) && (x)->magic == CURL_MULTI_HANDLE) #endif +static void move_pending_to_connect(struct Curl_multi *multi, + struct Curl_easy *data); static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data); static CURLMcode add_next_timeout(struct curltime now, @@ -94,11 +96,13 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); +static void multi_xfer_bufs_free(struct Curl_multi *multi); #ifdef DEBUGBUILD static const char * const multi_statename[]={ "INIT", "PENDING", + "SETUP", "CONNECT", "RESOLVING", "CONNECTING", @@ -148,6 +152,7 @@ static const init_multistate_func finit[MSTATE_LAST] = { NULL, /* INIT */ NULL, /* PENDING */ + NULL, /* SETUP */ Curl_init_CONNECT, /* CONNECT */ NULL, /* RESOLVING */ NULL, /* CONNECTING */ @@ -189,6 +194,10 @@ /* changing to COMPLETED means there's one less easy handle 'alive' */ DEBUGASSERT(data->multi->num_alive > 0); data->multi->num_alive--; + if(!data->multi->num_alive) { + /* free the transfer buffer when we have no more active transfers */ + multi_xfer_bufs_free(data->multi); + } } /* if this state has an init-function, run it */ @@ -355,7 +364,7 @@ * per call." * */ -static void sh_init(struct Curl_hash *hash, int hashsize) +static void sh_init(struct Curl_hash *hash, size_t hashsize) { Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, sh_freeentry); @@ -369,13 +378,12 @@ */ static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg) { - Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg, - &msg->list); + Curl_llist_append(&multi->msglist, msg, &msg->list); } -struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ - int chashsize, /* connection hash */ - int dnssize) /* dns hash */ +struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ + size_t chashsize, /* connection hash */ + size_t dnssize) /* dns hash */ { struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); @@ -525,6 +533,13 @@ multi->dead = FALSE; } + if(data->multi_easy) { + /* if this easy handle was previously used for curl_easy_perform(), there + is a private multi handle here that we can kill */ + curl_multi_cleanup(data->multi_easy); + data->multi_easy = NULL; + } + /* Initialize timeout list for this handle */ Curl_llist_init(&data->state.timeoutlist, NULL); @@ -537,6 +552,8 @@ if(data->set.errorbuffer) data->set.errorbuffer[0] = 0; + data->state.os_errno = 0; + /* make the Curl_easy refer back to this multi handle - before Curl_expire() is called. */ data->multi = multi; @@ -640,7 +657,7 @@ after an error was detected */ bool premature) { - CURLcode result; + CURLcode result, r2; struct connectdata *conn = data->conn; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -691,14 +708,18 @@ result = CURLE_ABORTED_BY_CALLBACK; } + /* Make sure that transfer client writes are really done now. */ + r2 = Curl_xfer_write_done(data, premature); + if(r2 && !result) + result = r2; + /* Inform connection filters that this transfer is done */ Curl_conn_ev_data_done(data, premature); process_pending_handles(data->multi); /* connection / multiplex */ - Curl_safefree(data->state.ulbuf); - - Curl_client_cleanup(data); + if(!result) + result = Curl_req_done(&data->req, data, premature); CONNCACHE_LOCK(data); Curl_detach_connection(data); @@ -784,7 +805,6 @@ data->state.lastconnect_id = -1; } - Curl_safefree(data->state.buffer); return result; } @@ -987,8 +1007,7 @@ DEBUGASSERT(!data->conn); DEBUGASSERT(conn); data->conn = conn; - Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, - &data->conn_queue); + Curl_llist_append(&conn->easyq, data, &data->conn_queue); if(conn->handler && conn->handler->attach) conn->handler->attach(data, conn); Curl_conn_ev_data_attach(conn, data); @@ -998,7 +1017,7 @@ { struct connectdata *conn = data->conn; (void)socks; - /* Not using `conn->sockfd` as `Curl_setup_transfer()` initializes + /* Not using `conn->sockfd` as `Curl_xfer_setup()` initializes * that *after* the connect. */ if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) { /* Default is to wait to something from the server */ @@ -1097,6 +1116,7 @@ switch(data->mstate) { case MSTATE_INIT: case MSTATE_PENDING: + case MSTATE_SETUP: case MSTATE_CONNECT: /* nothing to poll for yet */ break; @@ -1193,6 +1213,68 @@ return CURLM_OK; } +CURLMcode curl_multi_waitfds(struct Curl_multi *multi, + struct curl_waitfd *ufds, + unsigned int size, + unsigned int *fd_count) +{ + struct Curl_easy *data; + unsigned int nfds = 0; + struct easy_pollset ps; + unsigned int i; + CURLMcode result = CURLM_OK; + struct curl_waitfd *ufd; + unsigned int j; + + if(!ufds) + return CURLM_BAD_FUNCTION_ARGUMENT; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + if(multi->in_callback) + return CURLM_RECURSIVE_API_CALL; + + memset(&ps, 0, sizeof(ps)); + for(data = multi->easyp; data; data = data->next) { + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + if(nfds < size) { + curl_socket_t fd = ps.sockets[i]; + int fd_idx = -1; + + /* Simple linear search to skip an already added descriptor */ + for(j = 0; j < nfds; j++) { + if(ufds[j].fd == fd) { + fd_idx = (int)j; + break; + } + } + + if(fd_idx < 0) { + ufd = &ufds[nfds++]; + ufd->fd = ps.sockets[i]; + ufd->events = 0; + } + else + ufd = &ufds[fd_idx]; + + if(ps.actions[i] & CURL_POLL_IN) + ufd->events |= CURL_WAIT_POLLIN; + if(ps.actions[i] & CURL_POLL_OUT) + ufd->events |= CURL_WAIT_POLLOUT; + } + else + return CURLM_OUT_OF_MEMORY; + } + } + + if(fd_count) + *fd_count = nfds; + return result; +} + #ifdef USE_WINSOCK /* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't * be reset this way because an empty datagram would be sent. #9203 @@ -1209,6 +1291,29 @@ } #endif +static CURLMcode ufds_increase(struct pollfd **pfds, unsigned int *pfds_len, + unsigned int inc, bool *is_malloced) +{ + struct pollfd *new_fds, *old_fds = *pfds; + unsigned int new_len = *pfds_len + inc; + + new_fds = calloc(new_len, sizeof(struct pollfd)); + if(!new_fds) { + if(*is_malloced) + free(old_fds); + *pfds = NULL; + *pfds_len = 0; + return CURLM_OUT_OF_MEMORY; + } + memcpy(new_fds, old_fds, (*pfds_len) * sizeof(struct pollfd)); + if(*is_malloced) + free(old_fds); + *pfds = new_fds; + *pfds_len = new_len; + *is_malloced = TRUE; + return CURLM_OK; +} + #define NUM_POLLS_ON_STACK 10 static CURLMcode multi_wait(struct Curl_multi *multi, @@ -1222,12 +1327,12 @@ struct Curl_easy *data; struct easy_pollset ps; size_t i; - unsigned int nfds = 0; - unsigned int curlfds; long timeout_internal; int retcode = 0; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct pollfd *ufds = &a_few_on_stack[0]; + unsigned int ufds_len = NUM_POLLS_ON_STACK; + unsigned int nfds = 0, curl_nfds = 0; /* how many ufds are in use */ bool ufds_malloc = FALSE; #ifdef USE_WINSOCK WSANETWORKEVENTS wsa_events; @@ -1246,13 +1351,6 @@ if(timeout_ms < 0) return CURLM_BAD_FUNCTION_ARGUMENT; - /* Count up how many fds we have from the multi handle */ - memset(&ps, 0, sizeof(ps)); - for(data = multi->easyp; data; data = data->next) { - multi_getsock(data, &ps); - nfds += ps.num; - } - /* If the internally desired timeout is actually shorter than requested from the outside, then use the shorter time! But only if the internal timer is actually larger than -1! */ @@ -1260,70 +1358,60 @@ if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) timeout_ms = (int)timeout_internal; - curlfds = nfds; /* number of internal file descriptors */ - nfds += extra_nfds; /* add the externally provided ones */ + memset(ufds, 0, ufds_len * sizeof(struct pollfd)); + memset(&ps, 0, sizeof(ps)); -#ifdef ENABLE_WAKEUP + /* Add the curl handles to our pollfds first */ + for(data = multi->easyp; data; data = data->next) { + multi_getsock(data, &ps); + + for(i = 0; i < ps.num; i++) { + short events = 0; #ifdef USE_WINSOCK - if(use_wakeup) { -#else - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + long mask = 0; #endif - ++nfds; - } -#endif - - if(nfds > NUM_POLLS_ON_STACK) { - /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes - big, so at 2^29 sockets this value might wrap. When a process gets - the capability to actually handle over 500 million sockets this - calculation needs a integer overflow check. */ - ufds = malloc(nfds * sizeof(struct pollfd)); - if(!ufds) - return CURLM_OUT_OF_MEMORY; - ufds_malloc = TRUE; - } - nfds = 0; - - /* only do the second loop if we found descriptors in the first stage run - above */ - - if(curlfds) { - /* Add the curl handles to our pollfds first */ - for(data = multi->easyp; data; data = data->next) { - multi_getsock(data, &ps); - - for(i = 0; i < ps.num; i++) { - struct pollfd *ufd = &ufds[nfds++]; + if(ps.actions[i] & CURL_POLL_IN) { #ifdef USE_WINSOCK - long mask = 0; + mask |= FD_READ|FD_ACCEPT|FD_CLOSE; #endif - ufd->fd = ps.sockets[i]; - ufd->events = 0; - if(ps.actions[i] & CURL_POLL_IN) { + events |= POLLIN; + } + if(ps.actions[i] & CURL_POLL_OUT) { #ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; + mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; + reset_socket_fdwrite(ps.sockets[i]); #endif - ufd->events |= POLLIN; + events |= POLLOUT; + } + if(events) { + if(nfds && ps.sockets[i] == ufds[nfds-1].fd) { + ufds[nfds-1].events |= events; } - if(ps.actions[i] & CURL_POLL_OUT) { -#ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(ps.sockets[i]); -#endif - ufd->events |= POLLOUT; + else { + if(nfds >= ufds_len) { + if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc)) + return CURLM_OUT_OF_MEMORY; + } + DEBUGASSERT(nfds < ufds_len); + ufds[nfds].fd = ps.sockets[i]; + ufds[nfds].events = events; + ++nfds; } + } #ifdef USE_WINSOCK + if(mask) { if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) { if(ufds_malloc) free(ufds); return CURLM_INTERNAL_ERROR; } -#endif } +#endif } } + curl_nfds = nfds; /* what curl internally used in ufds */ + /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { #ifdef USE_WINSOCK @@ -1342,6 +1430,11 @@ return CURLM_INTERNAL_ERROR; } #endif + if(nfds >= ufds_len) { + if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc)) + return CURLM_OUT_OF_MEMORY; + } + DEBUGASSERT(nfds < ufds_len); ufds[nfds].fd = extra_fds[i].fd; ufds[nfds].events = 0; if(extra_fds[i].events & CURL_WAIT_POLLIN) @@ -1356,6 +1449,11 @@ #ifdef ENABLE_WAKEUP #ifndef USE_WINSOCK if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { + if(nfds >= ufds_len) { + if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc)) + return CURLM_OUT_OF_MEMORY; + } + DEBUGASSERT(nfds < ufds_len); ufds[nfds].fd = multi->wakeup_pair[0]; ufds[nfds].events = POLLIN; ++nfds; @@ -1395,7 +1493,7 @@ struct, the bit values of the actual underlying poll() implementation may not be the same as the ones in the public libcurl API! */ for(i = 0; i < extra_nfds; i++) { - unsigned r = ufds[curlfds + i].revents; + unsigned r = ufds[curl_nfds + i].revents; unsigned short mask = 0; #ifdef USE_WINSOCK curl_socket_t s = extra_fds[i].fd; @@ -1428,7 +1526,7 @@ #ifdef USE_WINSOCK /* Count up all our own sockets that had activity, and remove them from the event. */ - if(curlfds) { + if(curl_nfds) { for(data = multi->easyp; data; data = data->next) { multi_getsock(data, &ps); @@ -1449,7 +1547,7 @@ #else #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(ufds[curlfds + extra_nfds].revents & POLLIN) { + if(ufds[curl_nfds + extra_nfds].revents & POLLIN) { char buf[64]; ssize_t nread; while(1) { @@ -1669,47 +1767,47 @@ CURLcode *result, bool connect_timeout) { - timediff_t timeout_ms; - timeout_ms = Curl_timeleft(data, now, connect_timeout); - + timediff_t timeout_ms = Curl_timeleft(data, now, connect_timeout); if(timeout_ms < 0) { /* Handle timed out */ + struct curltime since; + if(connect_timeout) + since = data->progress.t_startsingle; + else + since = data->progress.t_startop; if(data->mstate == MSTATE_RESOLVING) failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds", - Curl_timediff(*now, data->progress.t_startsingle)); + " milliseconds", Curl_timediff(*now, since)); else if(data->mstate == MSTATE_CONNECTING) failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds", - Curl_timediff(*now, data->progress.t_startsingle)); + " milliseconds", Curl_timediff(*now, since)); else { struct SingleRequest *k = &data->req; if(k->size != -1) { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(*now, data->progress.t_startsingle), - k->bytecount, k->size); + Curl_timediff(*now, since), k->bytecount, k->size); } else { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds with %" CURL_FORMAT_CURL_OFF_T - " bytes received", - Curl_timediff(*now, data->progress.t_startsingle), - k->bytecount); + " bytes received", Curl_timediff(*now, since), k->bytecount); } } - - /* Force connection closed if the connection has indeed been used */ - if(data->mstate > MSTATE_DO) { - streamclose(data->conn, "Disconnected with pending data"); - *stream_error = TRUE; - } *result = CURLE_OPERATION_TIMEDOUT; - (void)multi_done(data, *result, TRUE); + if(data->conn) { + /* Force connection closed if the connection has indeed been used */ + if(data->mstate > MSTATE_DO) { + streamclose(data->conn, "Disconnect due to timeout"); + *stream_error = TRUE; + } + (void)multi_done(data, *result, TRUE); + } + return TRUE; } - return (timeout_ms < 0); + return FALSE; } /* @@ -1801,105 +1899,6 @@ return result; /* pass back status */ } -/* - * readrewind() rewinds the read stream. This is typically used for HTTP - * POST/PUT with multi-pass authentication when a sending was denied and a - * resend is necessary. - */ -static CURLcode readrewind(struct Curl_easy *data) -{ - curl_mimepart *mimepart = &data->set.mimepost; - DEBUGASSERT(data->conn); - - data->state.rewindbeforesend = FALSE; /* we rewind now */ - - /* explicitly switch off sending data on this connection now since we are - about to restart a new transfer and thus we want to avoid inadvertently - sending more data on the existing connection until the next transfer - starts */ - data->req.keepon &= ~KEEP_SEND; - - /* We have sent away data. If not using CURLOPT_POSTFIELDS or - CURLOPT_HTTPPOST, call app to rewind - */ -#ifndef CURL_DISABLE_HTTP - if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { - if(data->state.mimepost) - mimepart = data->state.mimepost; - } -#endif - if(data->set.postfields || - (data->state.httpreq == HTTPREQ_GET) || - (data->state.httpreq == HTTPREQ_HEAD)) - ; /* no need to rewind */ - else if(data->state.httpreq == HTTPREQ_POST_MIME || - data->state.httpreq == HTTPREQ_POST_FORM) { - CURLcode result = Curl_mime_rewind(mimepart); - if(result) { - failf(data, "Cannot rewind mime/post data"); - return result; - } - } - else { - if(data->set.seek_func) { - int err; - - Curl_set_in_callback(data, true); - err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); - Curl_set_in_callback(data, false); - if(err) { - failf(data, "seek callback returned error %d", (int)err); - return CURLE_SEND_FAIL_REWIND; - } - } - else if(data->set.ioctl_func) { - curlioerr err; - - Curl_set_in_callback(data, true); - err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, - data->set.ioctl_client); - Curl_set_in_callback(data, false); - infof(data, "the ioctl callback returned %d", (int)err); - - if(err) { - failf(data, "ioctl callback returned error %d", (int)err); - return CURLE_SEND_FAIL_REWIND; - } - } - else { - /* If no CURLOPT_READFUNCTION is used, we know that we operate on a - given FILE * stream and we can actually attempt to rewind that - ourselves with fseek() */ - if(data->state.fread_func == (curl_read_callback)fread) { - if(-1 != fseek(data->state.in, 0, SEEK_SET)) - /* successful rewind */ - return CURLE_OK; - } - - /* no callback set or failure above, makes us fail at once */ - failf(data, "necessary data rewind wasn't possible"); - return CURLE_SEND_FAIL_REWIND; - } - } - return CURLE_OK; -} - -/* - * Curl_preconnect() is called immediately before a connect starts. When a - * redirect is followed, this is then called multiple times during a single - * transfer. - */ -CURLcode Curl_preconnect(struct Curl_easy *data) -{ - if(!data->state.buffer) { - data->state.buffer = malloc(data->set.buffer_size + 1); - if(!data->state.buffer) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - static void set_in_callback(struct Curl_multi *multi, bool value) { multi->in_callback = value; @@ -1914,7 +1913,6 @@ bool async; bool protocol_connected = FALSE; bool dophase_done = FALSE; - bool done = FALSE; CURLMcode rc; CURLcode result = CURLE_OK; timediff_t recv_timeout_ms; @@ -1954,51 +1952,44 @@ return CURLM_INTERNAL_ERROR; } - if(data->conn && - (data->mstate >= MSTATE_CONNECT) && - (data->mstate < MSTATE_COMPLETED)) { - /* Check for overall operation timeout here but defer handling the - * connection timeout to later, to allow for a connection to be set up - * in the window since we last checked timeout. This prevents us - * tearing down a completed connection in the case where we were slow - * to check the timeout (e.g. process descheduled during this loop). - * We set connect_timeout=FALSE to do this. */ - - /* we need to wait for the connect state as only then is the start time - stored, but we must not check already completed handles */ - if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) { - /* Skip the statemachine and go directly to error handling section. */ - goto statemachine_end; - } - } + /* Wait for the connect state as only then is the start time stored, but + we must not check already completed handles */ + if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) && + multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) + /* Skip the statemachine and go directly to error handling section. */ + goto statemachine_end; switch(data->mstate) { case MSTATE_INIT: - /* init this transfer. */ + /* Transitional state. init this transfer. A handle never comes + back to this state. */ result = Curl_pretransfer(data); - - if(!result) { - /* after init, go CONNECT */ - multistate(data, MSTATE_CONNECT); - *nowp = Curl_pgrsTime(data, TIMER_STARTOP); - rc = CURLM_CALL_MULTI_PERFORM; - } - break; - - case MSTATE_CONNECT: - /* Connect. We want to get a connection identifier filled in. */ - /* init this transfer. */ - result = Curl_preconnect(data); if(result) break; + /* after init, go SETUP */ + multistate(data, MSTATE_SETUP); + (void)Curl_pgrsTime(data, TIMER_STARTOP); + FALLTHROUGH(); + + case MSTATE_SETUP: + /* Transitional state. Setup things for a new transfer. The handle + can come back to this state on a redirect. */ *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE); if(data->set.timeout) Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); - if(data->set.connecttimeout) + /* Since a connection might go to pending and back to CONNECT several + times before it actually takes off, we need to set the timeout once + in SETUP before we enter CONNECT the first time. */ Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + multistate(data, MSTATE_CONNECT); + FALLTHROUGH(); + + case MSTATE_CONNECT: + /* Connect. We want to get a connection identifier filled in. This state + can be entered from SETUP and from PENDING. */ result = Curl_connect(data, &async, &connected); if(CURLE_NO_CONNECTION_AVAILABLE == result) { /* There was no connection available. We will go to the pending @@ -2006,18 +1997,14 @@ multistate(data, MSTATE_PENDING); /* add this handle to the list of connect-pending handles */ - Curl_llist_insert_next(&multi->pending, multi->pending.tail, data, - &data->connect_queue); + Curl_llist_append(&multi->pending, data, &data->connect_queue); /* unlink from the main list */ unlink_easy(multi, data); result = CURLE_OK; break; } - else if(data->state.previouslypending) { - /* this transfer comes from the pending queue so try move another */ - infof(data, "Transfer was pending, now try another"); + else process_pending_handles(data->multi); - } if(!result) { *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE); @@ -2058,7 +2045,7 @@ hostname = conn->host.name; /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, hostname, (int)conn->port); + dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port); if(dns) { #ifdef CURLRES_ASYNCH @@ -2153,9 +2140,6 @@ break; case MSTATE_PROTOCONNECT: - if(data->state.rewindbeforesend) - result = readrewind(data); - if(!result && data->conn->bits.reuse) { /* ftp seems to hang when protoconnect on reused connection * since we handle PROTOCONNECT in general inside the filers, it @@ -2207,10 +2191,10 @@ /* call the prerequest callback function */ Curl_set_in_callback(data, true); prereq_rc = data->set.fprereq(data->set.prereq_userp, - data->info.conn_primary_ip, - data->info.conn_local_ip, - data->info.conn_primary_port, - data->info.conn_local_port); + data->info.primary.remote_ip, + data->info.primary.local_ip, + data->info.primary.remote_port, + data->info.primary.local_port); Curl_set_in_callback(data, false); if(prereq_rc != CURL_PREREQFUNC_OK) { failf(data, "operation aborted by pre-request callback"); @@ -2301,7 +2285,7 @@ follow = FOLLOW_RETRY; drc = Curl_follow(data, newurl, follow); if(!drc) { - multistate(data, MSTATE_CONNECT); + multistate(data, MSTATE_SETUP); rc = CURLM_CALL_MULTI_PERFORM; result = CURLE_OK; } @@ -2450,7 +2434,6 @@ { char *newurl = NULL; bool retry = FALSE; - DEBUGASSERT(data->state.buffer); /* check if over send speed */ send_timeout_ms = 0; if(data->set.max_send_speed) @@ -2480,9 +2463,9 @@ } /* read/write data if it is ready to do so */ - result = Curl_readwrite(data, &done); + result = Curl_readwrite(data); - if(done || (result == CURLE_RECV_ERROR)) { + if(data->req.done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race * condition and the server closed the reused connection exactly when * we wanted to use it, so figure out if that is indeed the case. @@ -2497,7 +2480,7 @@ /* if we are to retry, set the result to OK and consider the request as done */ result = CURLE_OK; - done = TRUE; + data->req.done = TRUE; } } else if((CURLE_HTTP2_STREAM == result) && @@ -2517,7 +2500,7 @@ as done */ retry = TRUE; result = CURLE_OK; - done = TRUE; + data->req.done = TRUE; } else result = ret; @@ -2539,7 +2522,7 @@ Curl_posttransfer(data); multi_done(data, result, TRUE); } - else if(done) { + else if(data->req.done && !Curl_cwriter_is_paused(data)) { /* call this even if the readwrite function returned error */ Curl_posttransfer(data); @@ -2562,10 +2545,9 @@ /* multi_done() might return CURLE_GOT_NOTHING */ result = Curl_follow(data, newurl, follow); if(!result) { - multistate(data, MSTATE_CONNECT); + multistate(data, MSTATE_SETUP); rc = CURLM_CALL_MULTI_PERFORM; } - free(newurl); } else { /* after the transfer is done, go DONE */ @@ -2577,7 +2559,6 @@ newurl = data->req.location; data->req.location = NULL; result = Curl_follow(data, newurl, FOLLOW_FAKE); - free(newurl); if(result) { stream_error = TRUE; result = multi_done(data, result, TRUE); @@ -2596,6 +2577,7 @@ transfers */ Curl_expire(data, 0, EXPIRE_RUN_NOW); } + free(newurl); break; } @@ -2646,8 +2628,7 @@ return CURLM_INTERNAL_ERROR; } - if(data->conn && - data->mstate >= MSTATE_CONNECT && + if(data->mstate >= MSTATE_CONNECT && data->mstate < MSTATE_DO && rc != CURLM_CALL_MULTI_PERFORM && !multi_ischanged(multi, false)) { @@ -2657,7 +2638,7 @@ * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before * declaring the connection timed out as we may almost have a completed * connection. */ - multi_handle_timeout(data, nowp, &stream_error, &result, TRUE); + multi_handle_timeout(data, nowp, &stream_error, &result, FALSE); } statemachine_end: @@ -2734,8 +2715,7 @@ multistate(data, MSTATE_MSGSENT); /* add this handle to the list of msgsent handles */ - Curl_llist_insert_next(&multi->msgsent, multi->msgsent.tail, data, - &data->connect_queue); + Curl_llist_append(&multi->msgsent, data, &data->connect_queue); /* unlink from the main list */ unlink_easy(multi, data); return CURLM_OK; @@ -2797,10 +2777,20 @@ */ do { multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) + if(t) { /* the removed may have another timeout in queue */ + data = t->payload; + if(data->mstate == MSTATE_PENDING) { + bool stream_unused; + CURLcode result_unused; + if(multi_handle_timeout(data, &now, &stream_unused, &result_unused, + FALSE)) { + infof(data, "PENDING handle timeout"); + move_pending_to_connect(multi, data); + } + } (void)add_next_timeout(now, multi, t->payload); - + } } while(t); *running_handles = multi->num_alive; @@ -2883,6 +2873,7 @@ Curl_free_multi_ssl_backend_data(multi->ssl_backend_data); #endif + multi_xfer_bufs_free(multi); free(multi); return CURLM_OK; @@ -3242,7 +3233,7 @@ if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) /* set socket event bitmask if they're not locked */ - data->state.select_bits = (unsigned char)ev_bitmask; + data->state.select_bits |= (unsigned char)ev_bitmask; Curl_expire(data, 0, EXPIRE_RUN_NOW); } @@ -3753,47 +3744,55 @@ process_pending_handles(data->multi); } -/* process_pending_handles() moves all handles from PENDING - back into the main list and change state to CONNECT */ +static void move_pending_to_connect(struct Curl_multi *multi, + struct Curl_easy *data) +{ + DEBUGASSERT(data->mstate == MSTATE_PENDING); + + /* put it back into the main list */ + link_easy(multi, data); + + multistate(data, MSTATE_CONNECT); + + /* Remove this node from the pending list */ + Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); + + /* Make sure that the handle will be processed soonish. */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); +} + +/* process_pending_handles() moves a handle from PENDING back into the main + list and change state to CONNECT. + + We do not move all transfers because that can be a significant amount. + Since this is tried every now and then doing too many too often becomes a + performance problem. + + When there is a change for connection limits like max host connections etc, + this likely only allows one new transfer. When there is a pipewait change, + it can potentially allow hundreds of new transfers. + + We could consider an improvement where we store the queue reason and allow + more pipewait rechecks than others. +*/ static void process_pending_handles(struct Curl_multi *multi) { struct Curl_llist_element *e = multi->pending.head; if(e) { struct Curl_easy *data = e->ptr; - - DEBUGASSERT(data->mstate == MSTATE_PENDING); - - /* put it back into the main list */ - link_easy(multi, data); - - multistate(data, MSTATE_CONNECT); - - /* Remove this node from the list */ - Curl_llist_remove(&multi->pending, e, NULL); - - /* Make sure that the handle will be processed soonish. */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - /* mark this as having been in the pending queue */ - data->state.previouslypending = TRUE; + move_pending_to_connect(multi, data); } } void Curl_set_in_callback(struct Curl_easy *data, bool value) { - /* might get called when there is no data pointer! */ - if(data) { - if(data->multi_easy) - data->multi_easy->in_callback = value; - else if(data->multi) - data->multi->in_callback = value; - } + if(data && data->multi) + data->multi->in_callback = value; } -bool Curl_is_in_callback(struct Curl_easy *easy) +bool Curl_is_in_callback(struct Curl_easy *data) { - return ((easy->multi && easy->multi->in_callback) || - (easy->multi_easy && easy->multi_easy->in_callback)); + return (data && data->multi && data->multi->in_callback); } unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) @@ -3819,3 +3818,120 @@ } return a; } + +CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + *pbuf = NULL; + *pbuflen = 0; + if(!data->multi) { + failf(data, "transfer has no multi handle"); + return CURLE_FAILED_INIT; + } + if(!data->set.buffer_size) { + failf(data, "transfer buffer size is 0"); + return CURLE_FAILED_INIT; + } + if(data->multi->xfer_buf_borrowed) { + failf(data, "attempt to borrow xfer_buf when already borrowed"); + return CURLE_AGAIN; + } + + if(data->multi->xfer_buf && + data->set.buffer_size > data->multi->xfer_buf_len) { + /* not large enough, get a new one */ + free(data->multi->xfer_buf); + data->multi->xfer_buf = NULL; + data->multi->xfer_buf_len = 0; + } + + if(!data->multi->xfer_buf) { + data->multi->xfer_buf = malloc((size_t)data->set.buffer_size); + if(!data->multi->xfer_buf) { + failf(data, "could not allocate xfer_buf of %zu bytes", + (size_t)data->set.buffer_size); + return CURLE_OUT_OF_MEMORY; + } + data->multi->xfer_buf_len = data->set.buffer_size; + } + + data->multi->xfer_buf_borrowed = TRUE; + *pbuf = data->multi->xfer_buf; + *pbuflen = data->multi->xfer_buf_len; + return CURLE_OK; +} + +void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf) +{ + (void)buf; + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + DEBUGASSERT(!buf || data->multi->xfer_buf == buf); + data->multi->xfer_buf_borrowed = FALSE; +} + +CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + *pbuf = NULL; + *pbuflen = 0; + if(!data->multi) { + failf(data, "transfer has no multi handle"); + return CURLE_FAILED_INIT; + } + if(!data->set.upload_buffer_size) { + failf(data, "transfer upload buffer size is 0"); + return CURLE_FAILED_INIT; + } + if(data->multi->xfer_ulbuf_borrowed) { + failf(data, "attempt to borrow xfer_ulbuf when already borrowed"); + return CURLE_AGAIN; + } + + if(data->multi->xfer_ulbuf && + data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) { + /* not large enough, get a new one */ + free(data->multi->xfer_ulbuf); + data->multi->xfer_ulbuf = NULL; + data->multi->xfer_ulbuf_len = 0; + } + + if(!data->multi->xfer_ulbuf) { + data->multi->xfer_ulbuf = malloc((size_t)data->set.upload_buffer_size); + if(!data->multi->xfer_ulbuf) { + failf(data, "could not allocate xfer_ulbuf of %zu bytes", + (size_t)data->set.upload_buffer_size); + return CURLE_OUT_OF_MEMORY; + } + data->multi->xfer_ulbuf_len = data->set.upload_buffer_size; + } + + data->multi->xfer_ulbuf_borrowed = TRUE; + *pbuf = data->multi->xfer_ulbuf; + *pbuflen = data->multi->xfer_ulbuf_len; + return CURLE_OK; +} + +void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf) +{ + (void)buf; + DEBUGASSERT(data); + DEBUGASSERT(data->multi); + DEBUGASSERT(!buf || data->multi->xfer_ulbuf == buf); + data->multi->xfer_ulbuf_borrowed = FALSE; +} + +static void multi_xfer_bufs_free(struct Curl_multi *multi) +{ + DEBUGASSERT(multi); + Curl_safefree(multi->xfer_buf); + multi->xfer_buf_len = 0; + multi->xfer_buf_borrowed = FALSE; + Curl_safefree(multi->xfer_ulbuf); + multi->xfer_ulbuf_len = 0; + multi->xfer_ulbuf_borrowed = FALSE; +}
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index e03e382..add9a05 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h
@@ -44,24 +44,25 @@ typedef enum { MSTATE_INIT, /* 0 - start in this state */ MSTATE_PENDING, /* 1 - no connections, waiting for one */ - MSTATE_CONNECT, /* 2 - resolve/connect has been sent off */ - MSTATE_RESOLVING, /* 3 - awaiting the resolve to finalize */ - MSTATE_CONNECTING, /* 4 - awaiting the TCP connect to finalize */ - MSTATE_TUNNELING, /* 5 - awaiting HTTPS proxy SSL initialization to + MSTATE_SETUP, /* 2 - start a new transfer */ + MSTATE_CONNECT, /* 3 - resolve/connect has been sent off */ + MSTATE_RESOLVING, /* 4 - awaiting the resolve to finalize */ + MSTATE_CONNECTING, /* 5 - awaiting the TCP connect to finalize */ + MSTATE_TUNNELING, /* 6 - awaiting HTTPS proxy SSL initialization to complete and/or proxy CONNECT to finalize */ - MSTATE_PROTOCONNECT, /* 6 - initiate protocol connect procedure */ - MSTATE_PROTOCONNECTING, /* 7 - completing the protocol-specific connect + MSTATE_PROTOCONNECT, /* 7 - initiate protocol connect procedure */ + MSTATE_PROTOCONNECTING, /* 8 - completing the protocol-specific connect phase */ - MSTATE_DO, /* 8 - start send off the request (part 1) */ - MSTATE_DOING, /* 9 - sending off the request (part 1) */ - MSTATE_DOING_MORE, /* 10 - send off the request (part 2) */ - MSTATE_DID, /* 11 - done sending off request */ - MSTATE_PERFORMING, /* 12 - transfer data */ - MSTATE_RATELIMITING, /* 13 - wait because limit-rate exceeded */ - MSTATE_DONE, /* 14 - post data transfer operation */ - MSTATE_COMPLETED, /* 15 - operation complete */ - MSTATE_MSGSENT, /* 16 - the operation complete message is sent */ - MSTATE_LAST /* 17 - not a true state, never use this */ + MSTATE_DO, /* 9 - start send off the request (part 1) */ + MSTATE_DOING, /* 10 - sending off the request (part 1) */ + MSTATE_DOING_MORE, /* 11 - send off the request (part 2) */ + MSTATE_DID, /* 12 - done sending off request */ + MSTATE_PERFORMING, /* 13 - transfer data */ + MSTATE_RATELIMITING, /* 14 - wait because limit-rate exceeded */ + MSTATE_DONE, /* 15 - post data transfer operation */ + MSTATE_COMPLETED, /* 16 - operation complete */ + MSTATE_MSGSENT, /* 17 - the operation complete message is sent */ + MSTATE_LAST /* 18 - not a true state, never use this */ } CURLMstate; /* we support N sockets per easy handle. Set the corresponding bit to what @@ -124,6 +125,13 @@ times of all currently set timers */ struct Curl_tree *timetree; + /* buffer used for transfer data, lazy initialized */ + char *xfer_buf; /* the actual buffer */ + size_t xfer_buf_len; /* the allocated length */ + /* buffer used for upload data, lazy initialized */ + char *xfer_ulbuf; /* the actual buffer */ + size_t xfer_ulbuf_len; /* the allocated length */ + #if defined(USE_SSL) struct multi_ssl_backend_data *ssl_backend_data; #endif @@ -151,7 +159,7 @@ WSAEVENT wsa_event; /* winsock event used for waits */ #else #ifdef ENABLE_WAKEUP - curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup + curl_socket_t wakeup_pair[2]; /* pipe()/socketpair() used for wakeup 0 is used for read, 1 is used for write */ #endif #endif @@ -171,6 +179,8 @@ #endif BIT(dead); /* a callback returned error, everything needs to crash and burn */ + BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ + BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ #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 7a344fa..59a3064 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h
@@ -38,15 +38,16 @@ void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); void Curl_set_in_callback(struct Curl_easy *data, bool value); -bool Curl_is_in_callback(struct Curl_easy *easy); +bool Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); void Curl_multi_connchanged(struct Curl_multi *multi); /* Internal version of curl_multi_init() accepts size parameters for the socket, connection and dns hashes */ -struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize, - int dnssize); +struct Curl_multi *Curl_multi_handle(size_t hashsize, + size_t chashsize, + size_t dnssize); /* the write bits start at bit 16 for the *getsock() bitmap */ #define GETSOCK_WRITEBITSTART 16 @@ -94,4 +95,53 @@ /* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi); +/** + * Borrow the transfer buffer from the multi, suitable + * for the given transfer `data`. The buffer may only be used in one + * multi processing of the easy handle. It MUST be returned to the + * multi before it can be borrowed again. + * Pointers into the buffer remain only valid as long as it is borrowed. + * + * @param data the easy handle + * @param pbuf on return, the buffer to use or NULL on error + * @param pbuflen on return, the size of *pbuf or 0 on error + * @return CURLE_OK when buffer is available and is returned. + * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, + * CURLE_FAILED_INIT if the easy handle is without multi. + * CURLE_AGAIN if the buffer is borrowed already. + */ +CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen); +/** + * Release the borrowed buffer. All references into the buffer become + * invalid after this. + * @param buf the buffer pointer borrowed for coding error checks. + */ +void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf); + +/** + * Borrow the upload buffer from the multi, suitable + * for the given transfer `data`. The buffer may only be used in one + * multi processing of the easy handle. It MUST be returned to the + * multi before it can be borrowed again. + * Pointers into the buffer remain only valid as long as it is borrowed. + * + * @param data the easy handle + * @param pbuf on return, the buffer to use or NULL on error + * @param pbuflen on return, the size of *pbuf or 0 on error + * @return CURLE_OK when buffer is available and is returned. + * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, + * CURLE_FAILED_INIT if the easy handle is without multi. + * CURLE_AGAIN if the buffer is borrowed already. + */ +CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, + char **pbuf, size_t *pbuflen); + +/** + * Release the borrowed upload buffer. All references into the buffer become + * invalid after this. + * @param buf the upload buffer pointer borrowed for coding error checks. + */ +void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf); + #endif /* HEADER_CURL_MULTIIF_H */
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index 038c6dc..cd2a284 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c
@@ -53,6 +53,8 @@ #define NETRC_FAILED -1 #define NETRC_SUCCESS 0 +#define MAX_NETRC_LINE 4096 + /* * Returns zero on success. */ @@ -80,13 +82,14 @@ file = fopen(netrcfile, FOPEN_READTEXT); if(file) { bool done = FALSE; - char netrcbuffer[4096]; - int netrcbuffsize = (int)sizeof(netrcbuffer); + struct dynbuf buf; + Curl_dyn_init(&buf, MAX_NETRC_LINE); - while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) { + while(!done && Curl_get_line(&buf, file)) { char *tok; char *tok_end; bool quoted; + char *netrcbuffer = Curl_dyn_ptr(&buf); if(state == MACDEF) { if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) state = NOTHING; @@ -245,6 +248,7 @@ } /* while Curl_get_line() */ out: + Curl_dyn_free(&buf); if(!retcode) { /* success */ if(login_alloc) {
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c index 5241640..62299e2 100644 --- a/Utilities/cmcurl/lib/noproxy.c +++ b/Utilities/cmcurl/lib/noproxy.c
@@ -78,7 +78,7 @@ const char *network, unsigned int bits) { -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 int bytes; int rest; unsigned char address[16];
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index 1e60ff7..0348ef5 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c
@@ -117,7 +117,7 @@ */ const struct Curl_handler Curl_handler_ldap = { - "LDAP", /* scheme */ + "ldap", /* scheme */ oldap_setup_connection, /* setup_connection */ oldap_do, /* do_it */ oldap_done, /* done */ @@ -131,6 +131,7 @@ ZERO_NULL, /* perform_getsock */ oldap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_LDAP, /* defport */ @@ -145,7 +146,7 @@ */ const struct Curl_handler Curl_handler_ldaps = { - "LDAPS", /* scheme */ + "ldaps", /* scheme */ oldap_setup_connection, /* setup_connection */ oldap_do, /* do_it */ oldap_done, /* done */ @@ -159,6 +160,7 @@ ZERO_NULL, /* perform_getsock */ oldap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_LDAPS, /* defport */ @@ -548,9 +550,12 @@ return result; } - hosturl = aprintf("ldap%s://%s:%d", - conn->handler->flags & PROTOPT_SSL? "s": "", - conn->host.name, conn->remote_port); + hosturl = aprintf("%s://%s%s%s:%d", + conn->handler->scheme, + conn->bits.ipv6_ip? "[": "", + conn->host.name, + conn->bits.ipv6_ip? "]": "", + conn->remote_port); if(!hosturl) return CURLE_OUT_OF_MEMORY; @@ -916,7 +921,7 @@ else { lr->msgid = msgid; data->req.p.ldap = lr; - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); *done = TRUE; } }
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index b976ffb..81576c0 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c
@@ -164,7 +164,7 @@ const char *fmt, va_list args) { - ssize_t bytes_written = 0; + size_t bytes_written = 0; size_t write_len; char *s; CURLcode result; @@ -199,8 +199,11 @@ #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_nwrite(data, FIRSTSOCKET, s, write_len, &bytes_written); - if(result) + result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, &bytes_written); + if(result == CURLE_AGAIN) { + bytes_written = 0; + } + else if(result) return result; #ifdef HAVE_GSSAPI data_sec = conn->data_prot; @@ -208,9 +211,9 @@ conn->data_prot = (unsigned char)data_sec; #endif - Curl_debug(data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written); + Curl_debug(data, CURLINFO_HEADER_OUT, s, bytes_written); - if(bytes_written != (ssize_t)write_len) { + if(bytes_written != write_len) { /* the whole chunk was not sent, keep it around and adjust sizes */ pp->sendthis = s; pp->sendsize = write_len; @@ -251,7 +254,7 @@ } static CURLcode pingpong_read(struct Curl_easy *data, - curl_socket_t sockfd, + int sockindex, char *buffer, size_t buflen, ssize_t *nread) @@ -261,7 +264,7 @@ enum protection_level prot = data->conn->data_prot; data->conn->data_prot = PROT_CLEAR; #endif - result = Curl_read(data, sockfd, buffer, buflen, nread); + result = Curl_conn_recv(data, sockindex, buffer, buflen, nread); #ifdef HAVE_GSSAPI DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); data->conn->data_prot = (unsigned char)prot; @@ -275,7 +278,7 @@ * Reads a piece of a server response. */ CURLcode Curl_pp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, + int sockindex, struct pingpong *pp, int *code, /* return the server code if done */ size_t *size) /* size of the response */ @@ -300,7 +303,7 @@ ssize_t gotbytes = 0; char buffer[900]; - result = pingpong_read(data, sockfd, buffer, sizeof(buffer), &gotbytes); + result = pingpong_read(data, sockindex, buffer, sizeof(buffer), &gotbytes); if(result == CURLE_AGAIN) return CURLE_OK; @@ -395,14 +398,20 @@ struct pingpong *pp) { /* we have a piece of a command still left to send */ - ssize_t written; - CURLcode result = Curl_nwrite(data, FIRSTSOCKET, - pp->sendthis + pp->sendsize - pp->sendleft, - pp->sendleft, &written); + size_t written; + CURLcode result; + + result = Curl_conn_send(data, FIRSTSOCKET, + pp->sendthis + pp->sendsize - pp->sendleft, + pp->sendleft, &written); + if(result == CURLE_AGAIN) { + result = CURLE_OK; + written = 0; + } if(result) return result; - if(written != (ssize_t)pp->sendleft) { + if(written != pp->sendleft) { /* only a fraction was sent */ pp->sendleft -= written; } @@ -423,7 +432,7 @@ bool Curl_pp_moredata(struct pingpong *pp) { - return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf)); + return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal); } #endif
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index 006b9c5..28172c7 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h
@@ -132,7 +132,7 @@ * Reads a piece of a server response. */ CURLcode Curl_pp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, + int sockindex, struct pingpong *pp, int *code, /* return the server code if done */ size_t *size); /* size of the response */
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index cf25192..9a30331 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c
@@ -112,7 +112,7 @@ */ const struct Curl_handler Curl_handler_pop3 = { - "POP3", /* scheme */ + "pop3", /* scheme */ pop3_setup_connection, /* setup_connection */ pop3_do, /* do_it */ pop3_done, /* done */ @@ -126,6 +126,7 @@ ZERO_NULL, /* perform_getsock */ pop3_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_POP3, /* defport */ @@ -141,7 +142,7 @@ */ const struct Curl_handler Curl_handler_pop3s = { - "POP3S", /* scheme */ + "pop3s", /* scheme */ pop3_setup_connection, /* setup_connection */ pop3_do, /* do_it */ pop3_done, /* done */ @@ -155,6 +156,7 @@ ZERO_NULL, /* perform_getsock */ pop3_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_POP3S, /* defport */ @@ -934,7 +936,7 @@ if(pop3->transfer == PPTRANSFER_BODY) { /* POP3 download */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); if(pp->overflow) { /* The recv buffer contains data that is actually body content so send @@ -970,7 +972,6 @@ struct connectdata *conn) { CURLcode result = CURLE_OK; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; int pop3code; struct pop3_conn *pop3c = &conn->proto.pop3c; struct pingpong *pp = &pop3c->pp; @@ -987,7 +988,7 @@ do { /* Read the response from the server */ - result = Curl_pp_readresp(data, sock, pp, &pop3code, &nread); + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); if(result) return result; @@ -1451,7 +1452,7 @@ * This function scans the body after the end-of-body and writes everything * until the end is found. */ -CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread) +CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) { /* This code could be made into a special function in the handler struct */ CURLcode result = CURLE_OK;
diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h index 83f0f83..e8e98db 100644 --- a/Utilities/cmcurl/lib/pop3.h +++ b/Utilities/cmcurl/lib/pop3.h
@@ -92,6 +92,7 @@ /* This function scans the body after the end-of-body and writes everything * until the end is found */ -CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread); +CURLcode Curl_pop3_write(struct Curl_easy *data, + const char *str, size_t nread); #endif /* HEADER_CURL_POP3_H */
diff --git a/Utilities/cmcurl/lib/request.c b/Utilities/cmcurl/lib/request.c new file mode 100644 index 0000000..26741fa --- /dev/null +++ b/Utilities/cmcurl/lib/request.c
@@ -0,0 +1,409 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "cfilters.h" +#include "dynbuf.h" +#include "doh.h" +#include "multiif.h" +#include "progress.h" +#include "request.h" +#include "sendf.h" +#include "transfer.h" +#include "url.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +void Curl_req_init(struct SingleRequest *req) +{ + memset(req, 0, sizeof(*req)); +} + +CURLcode Curl_req_soft_reset(struct SingleRequest *req, + struct Curl_easy *data) +{ + CURLcode result; + + req->done = FALSE; + req->upload_done = FALSE; + req->download_done = FALSE; + req->ignorebody = FALSE; + req->bytecount = 0; + req->writebytecount = 0; + req->header = TRUE; /* assume header */ + req->headerline = 0; + req->headerbytecount = 0; + req->allheadercount = 0; + req->deductheadercount = 0; + + result = Curl_client_start(data); + if(result) + return result; + + if(!req->sendbuf_init) { + Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1, + BUFQ_OPT_SOFT_LIMIT); + req->sendbuf_init = TRUE; + } + else { + Curl_bufq_reset(&req->sendbuf); + if(data->set.upload_buffer_size != req->sendbuf.chunk_size) { + Curl_bufq_free(&req->sendbuf); + Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1, + BUFQ_OPT_SOFT_LIMIT); + } + } + + return CURLE_OK; +} + +CURLcode Curl_req_start(struct SingleRequest *req, + struct Curl_easy *data) +{ + req->start = Curl_now(); + return Curl_req_soft_reset(req, data); +} + +static CURLcode req_flush(struct Curl_easy *data); + +CURLcode Curl_req_done(struct SingleRequest *req, + struct Curl_easy *data, bool aborted) +{ + (void)req; + if(!aborted) + (void)req_flush(data); + Curl_client_reset(data); + return CURLE_OK; +} + +void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) +{ + struct curltime t0 = {0, 0}; + + /* This is a bit ugly. `req->p` is a union and we assume we can + * free this safely without leaks. */ + Curl_safefree(req->p.http); + Curl_safefree(req->newurl); + Curl_client_reset(data); + if(req->sendbuf_init) + Curl_bufq_reset(&req->sendbuf); + +#ifndef CURL_DISABLE_DOH + if(req->doh) { + Curl_close(&req->doh->probe[0].easy); + Curl_close(&req->doh->probe[1].easy); + } +#endif + /* Can no longer memset() this struct as we need to keep some state */ + req->size = -1; + req->maxdownload = -1; + req->bytecount = 0; + req->writebytecount = 0; + req->start = t0; + req->headerbytecount = 0; + req->allheadercount = 0; + req->deductheadercount = 0; + req->headerline = 0; + req->offset = 0; + req->httpcode = 0; + req->keepon = 0; + req->upgr101 = UPGR101_INIT; + req->timeofdoc = 0; + req->bodywrites = 0; + req->location = NULL; + req->newurl = NULL; +#ifndef CURL_DISABLE_COOKIES + req->setcookies = 0; +#endif + req->header = FALSE; + req->content_range = FALSE; + req->download_done = FALSE; + req->eos_written = FALSE; + req->eos_read = FALSE; + req->upload_done = FALSE; + req->upload_aborted = FALSE; + req->ignorebody = FALSE; + req->http_bodyless = FALSE; + req->chunk = FALSE; + req->ignore_cl = FALSE; + req->upload_chunky = FALSE; + req->getheader = FALSE; + req->no_body = data->set.opt_no_body; + req->authneg = FALSE; +} + +void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) +{ + /* This is a bit ugly. `req->p` is a union and we assume we can + * free this safely without leaks. */ + Curl_safefree(req->p.http); + Curl_safefree(req->newurl); + if(req->sendbuf_init) + Curl_bufq_free(&req->sendbuf); + Curl_client_cleanup(data); + +#ifndef CURL_DISABLE_DOH + if(req->doh) { + Curl_close(&req->doh->probe[0].easy); + Curl_close(&req->doh->probe[1].easy); + Curl_dyn_free(&req->doh->probe[0].serverdoh); + Curl_dyn_free(&req->doh->probe[1].serverdoh); + curl_slist_free_all(req->doh->headers); + Curl_safefree(req->doh); + } +#endif +} + +static CURLcode xfer_send(struct Curl_easy *data, + const char *buf, size_t blen, + size_t hds_len, size_t *pnwritten) +{ + CURLcode result = CURLE_OK; + + *pnwritten = 0; +#ifdef CURLDEBUG + { + /* Allow debug builds to override this logic to force short initial + sends + */ + char *p = getenv("CURL_SMALLREQSEND"); + if(p) { + size_t altsize = (size_t)strtoul(p, NULL, 10); + if(altsize && altsize < blen) + blen = altsize; + } + } +#endif + /* Make sure this doesn't send more body bytes than what the max send + speed says. The headers do not count to the max speed. */ + if(data->set.max_send_speed) { + size_t body_bytes = blen - hds_len; + if((curl_off_t)body_bytes > data->set.max_send_speed) + blen = hds_len + (size_t)data->set.max_send_speed; + } + + result = Curl_xfer_send(data, buf, blen, pnwritten); + if(!result && *pnwritten) { + if(hds_len) + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf, + CURLMIN(hds_len, *pnwritten)); + if(*pnwritten > hds_len) { + size_t body_len = *pnwritten - hds_len; + Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len); + data->req.writebytecount += body_len; + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + } + } + return result; +} + +static CURLcode req_send_buffer_flush(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + const unsigned char *buf; + size_t blen; + + while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) { + size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen); + result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten); + if(result) + break; + + Curl_bufq_skip(&data->req.sendbuf, nwritten); + if(hds_len) { + data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten); + } + /* leave if we could not send all. Maybe network blocking or + * speed limits on transfer */ + if(nwritten < blen) + break; + } + return result; +} + +static CURLcode req_set_upload_done(struct Curl_easy *data) +{ + DEBUGASSERT(!data->req.upload_done); + data->req.upload_done = TRUE; + data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we're done sending */ + + Curl_creader_done(data, data->req.upload_aborted); + + if(data->req.upload_aborted) { + if(data->req.writebytecount) + infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T + " bytes", data->req.writebytecount); + else + infof(data, "abort upload"); + } + else if(data->req.writebytecount) + infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T + " bytes", data->req.writebytecount); + else if(!data->req.download_done) + infof(data, Curl_creader_total_length(data)? + "We are completely uploaded and fine" : + "Request completely sent off"); + + return Curl_xfer_send_close(data); +} + +static CURLcode req_flush(struct Curl_easy *data) +{ + CURLcode result; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + + if(!Curl_bufq_is_empty(&data->req.sendbuf)) { + result = req_send_buffer_flush(data); + if(result) + return result; + if(!Curl_bufq_is_empty(&data->req.sendbuf)) { + return CURLE_AGAIN; + } + } + + if(!data->req.upload_done && data->req.eos_read && + Curl_bufq_is_empty(&data->req.sendbuf)) { + return req_set_upload_done(data); + } + return CURLE_OK; +} + +static ssize_t add_from_client(void *reader_ctx, + unsigned char *buf, size_t buflen, + CURLcode *err) +{ + struct Curl_easy *data = reader_ctx; + size_t nread; + bool eos; + + *err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos); + if(*err) + return -1; + if(eos) + data->req.eos_read = TRUE; + return (ssize_t)nread; +} + +#ifndef USE_HYPER + +static CURLcode req_send_buffer_add(struct Curl_easy *data, + const char *buf, size_t blen, + size_t hds_len) +{ + CURLcode result = CURLE_OK; + ssize_t n; + n = Curl_bufq_write(&data->req.sendbuf, + (const unsigned char *)buf, blen, &result); + if(n < 0) + return result; + /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */ + DEBUGASSERT((size_t)n == blen); + data->req.sendbuf_hds_len += hds_len; + return CURLE_OK; +} + +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) +{ + CURLcode result; + const char *buf; + size_t blen, nwritten; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + + buf = Curl_dyn_ptr(req); + blen = Curl_dyn_len(req); + if(!Curl_creader_total_length(data)) { + /* Request without body. Try to send directly from the buf given. */ + data->req.eos_read = TRUE; + result = xfer_send(data, buf, blen, blen, &nwritten); + if(result) + return result; + buf += nwritten; + blen -= nwritten; + } + + if(blen) { + /* Either we have a request body, or we could not send the complete + * request in one go. Buffer the remainder and try to add as much + * body bytes as room is left in the buffer. Then flush. */ + result = req_send_buffer_add(data, buf, blen, blen); + if(result) + return result; + + return Curl_req_send_more(data); + } + return CURLE_OK; +} +#endif /* !USE_HYPER */ + +bool Curl_req_want_send(struct Curl_easy *data) +{ + return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf); +} + +bool Curl_req_done_sending(struct Curl_easy *data) +{ + if(data->req.upload_done) { + DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf)); + return TRUE; + } + return FALSE; +} + +CURLcode Curl_req_send_more(struct Curl_easy *data) +{ + CURLcode result; + + /* Fill our send buffer if more from client can be read. */ + if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf)) { + ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0, + add_from_client, data, &result); + if(nread < 0 && result != CURLE_AGAIN) + return result; + } + + result = req_flush(data); + if(result == CURLE_AGAIN) + result = CURLE_OK; + + return result; +} + +CURLcode Curl_req_abort_sending(struct Curl_easy *data) +{ + if(!data->req.upload_done) { + Curl_bufq_reset(&data->req.sendbuf); + data->req.upload_aborted = TRUE; + return req_set_upload_done(data); + } + return CURLE_OK; +}
diff --git a/Utilities/cmcurl/lib/request.h b/Utilities/cmcurl/lib/request.h new file mode 100644 index 0000000..f9be6f2 --- /dev/null +++ b/Utilities/cmcurl/lib/request.h
@@ -0,0 +1,227 @@ +#ifndef HEADER_CURL_REQUEST_H +#define HEADER_CURL_REQUEST_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" + +#include "bufq.h" + +/* forward declarations */ +struct UserDefined; + +enum expect100 { + EXP100_SEND_DATA, /* enough waiting, just 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 */ + EXP100_FAILED /* used on 417 Expectation Failed */ +}; + +enum upgrade101 { + UPGR101_INIT, /* default state */ + UPGR101_WS, /* upgrade to WebSockets requested */ + UPGR101_H2, /* upgrade to HTTP/2 requested */ + UPGR101_RECEIVED, /* 101 response received */ + UPGR101_WORKING /* talking upgraded protocol */ +}; + + +/* + * Request specific data in the easy handle (Curl_easy). Previously, + * these members were on the connectdata struct but since a conn struct may + * now be shared between different Curl_easys, we store connection-specific + * data here. This struct only keeps stuff that's interesting for *this* + * request, as it will be cleared between multiple ones + */ +struct SingleRequest { + curl_off_t size; /* -1 if unknown at this point */ + curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, + -1 means unlimited */ + curl_off_t bytecount; /* total number of bytes read */ + curl_off_t writebytecount; /* number of bytes written */ + + struct curltime start; /* transfer started at this time */ + unsigned int headerbytecount; /* received server headers (not CONNECT + headers) */ + unsigned int allheadercount; /* all received headers (server + CONNECT) */ + unsigned int deductheadercount; /* this amount of bytes doesn't count when + we check if anything has been transferred + at the end of a connection. We use this + counter to make only a 100 reply (without + a following second response code) result + in a CURLE_GOT_NOTHING error code */ + 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 httpversion; /* Version in response (09, 10, 11, etc.) */ + int httpcode; /* error code from the 'HTTP/1.? XXX' or + 'RTSP/1.? XXX' line */ + int keepon; + enum upgrade101 upgr101; /* 101 upgrade state */ + + /* Client Writer stack, handles transfer- and content-encodings, protocol + * checks, pausing by client callbacks. */ + struct Curl_cwriter *writer_stack; + /* Client Reader stack, handles transfer- and content-encodings, protocol + * checks, pausing by client callbacks. */ + struct Curl_creader *reader_stack; + struct bufq sendbuf; /* data which needs to be send to the server */ + size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */ + time_t timeofdoc; + long bodywrites; + char *location; /* This points to an allocated version of the Location: + header data */ + char *newurl; /* Set to the new URL to use when a redirect or a retry is + wanted */ + + /* Allocated protocol-specific data. Each protocol handler makes sure this + points to data it needs. */ + union { + struct FILEPROTO *file; + struct FTP *ftp; + struct HTTP *http; + struct IMAP *imap; + struct ldapreqinfo *ldap; + struct MQTT *mqtt; + struct POP3 *pop3; + struct RTSP *rtsp; + struct smb_request *smb; + struct SMTP *smtp; + struct SSHPROTO *ssh; + struct TELNET *telnet; + } p; +#ifndef CURL_DISABLE_DOH + struct dohdata *doh; /* DoH specific data for this request */ +#endif +#ifndef CURL_DISABLE_COOKIES + unsigned char setcookies; +#endif + BIT(header); /* incoming data has HTTP header */ + BIT(done); /* request is done, e.g. no more send/recv should + * happen. This can be TRUE before `upload_done` or + * `download_done` is TRUE. */ + BIT(content_range); /* set TRUE if Content-Range: was found */ + BIT(download_done); /* set to TRUE when download is complete */ + BIT(eos_written); /* iff EOS has been written to client */ + BIT(eos_read); /* iff EOS has been read from the client */ + BIT(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(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 */ + BIT(chunk); /* if set, this is a chunked transfer-encoding */ + BIT(ignore_cl); /* ignore content-length */ + BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding + on upload */ + BIT(getheader); /* TRUE if header parsing is wanted */ + BIT(no_body); /* the response has no body */ + BIT(authneg); /* TRUE when the auth phase has started, which means + that we are creating a request with an auth header, + but it is not the final request in the auth + negotiation. */ + BIT(sendbuf_init); /* sendbuf is initialized */ +}; + +/** + * Initialize the state of the request for first use. + */ +void Curl_req_init(struct SingleRequest *req); + +/** + * The request is about to start. Record time and do a soft reset. + */ +CURLcode Curl_req_start(struct SingleRequest *req, + struct Curl_easy *data); + +/** + * The request may continue with a follow up. Reset + * members, but keep start time for overall duration calc. + */ +CURLcode Curl_req_soft_reset(struct SingleRequest *req, + struct Curl_easy *data); + +/** + * The request is done. If not aborted, make sure that buffers are + * flushed to the client. + * @param req the request + * @param data the transfer + * @param aborted TRUE iff the request was aborted/errored + */ +CURLcode Curl_req_done(struct SingleRequest *req, + struct Curl_easy *data, bool aborted); + +/** + * Free the state of the request, not usable afterwards. + */ +void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data); + +/** + * Hard reset the state of the request to virgin state base on + * transfer settings. + */ +void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data); + +#ifndef USE_HYPER +/** + * Send request headers. If not all could be sent + * 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 + * @return CURLE_OK (on blocking with *pnwritten == 0) or error. + */ +CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf); + +#endif /* !USE_HYPER */ + +/** + * TRUE iff the request has sent all request headers and data. + */ +bool Curl_req_done_sending(struct Curl_easy *data); + +/* + * Read more from client and flush all buffered request bytes. + * @return CURLE_OK on success or the error on the sending. + * Never returns CURLE_AGAIN. + */ +CURLcode Curl_req_send_more(struct Curl_easy *data); + +/** + * TRUE iff the request wants to send, e.g. has buffered bytes. + */ +bool Curl_req_want_send(struct Curl_easy *data); + +/** + * Stop sending any more request data to the server. + * Will clear the send buffer and mark request sending as done. + */ +CURLcode Curl_req_abort_sending(struct Curl_easy *data); + +#endif /* HEADER_CURL_REQUEST_H */
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 26f4735..1f162da 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c
@@ -70,8 +70,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos, - bool *done); + bool is_eos); static CURLcode rtsp_setup_connection(struct Curl_easy *data, struct connectdata *conn); @@ -94,14 +93,14 @@ static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len); static -CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport); +CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport); /* * RTSP handler interface. */ const struct Curl_handler Curl_handler_rtsp = { - "RTSP", /* scheme */ + "rtsp", /* scheme */ rtsp_setup_connection, /* setup_connection */ rtsp_do, /* do_it */ rtsp_done, /* done */ @@ -115,6 +114,7 @@ ZERO_NULL, /* perform_getsock */ rtsp_disconnect, /* disconnect */ rtsp_rtp_write_resp, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ rtsp_conncheck, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_RTSP, /* defport */ @@ -225,8 +225,6 @@ Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = data->req.p.rtsp; struct dynbuf req_buffer; - curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ - curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */ const char *p_request = NULL; const char *p_session_id = NULL; @@ -241,6 +239,8 @@ const char *p_userpwd = NULL; *done = TRUE; + /* Initialize a dynamic send buffer */ + Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; rtsp->CSeq_recv = 0; @@ -310,9 +310,8 @@ } if(rtspreq == RTSPREQ_RECEIVE) { - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); - - return result; + Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1); + goto out; } p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; @@ -320,7 +319,8 @@ (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", p_request); - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } /* Stream URI. Default to server '*' if not specified */ @@ -347,7 +347,8 @@ else { failf(data, "Refusing to issue an RTSP SETUP without a Transport: header."); - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } p_transport = data->state.aptr.rtsp_transport; @@ -366,9 +367,10 @@ data->state.aptr.accept_encoding = aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!data->state.aptr.accept_encoding) - return CURLE_OUT_OF_MEMORY; - + if(!data->state.aptr.accept_encoding) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } p_accept_encoding = data->state.aptr.accept_encoding; } } @@ -390,9 +392,11 @@ result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, p_stream_uri, FALSE); if(result) - return result; + goto out; +#ifndef CURL_DISABLE_PROXY p_proxyuserpwd = data->state.aptr.proxyuserpwd; +#endif p_userpwd = data->state.aptr.userpwd; /* Referrer */ @@ -424,23 +428,22 @@ */ if(Curl_checkheaders(data, STRCONST("CSeq"))) { failf(data, "CSeq cannot be set as a custom header."); - return CURLE_RTSP_CSEQ_ERROR; + result = CURLE_RTSP_CSEQ_ERROR; + goto out; } if(Curl_checkheaders(data, STRCONST("Session"))) { failf(data, "Session ID cannot be set as a custom header."); - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - /* Initialize a dynamic send buffer */ - Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); - result = Curl_dyn_addf(&req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ "CSeq: %ld\r\n", /* CSeq */ p_request, p_stream_uri, rtsp->CSeq_sent); if(result) - return result; + goto out; /* * Rather than do a normal alloc line, keep the session_id unformatted @@ -449,7 +452,7 @@ if(p_session_id) { result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id); if(result) - return result; + goto out; } /* @@ -481,44 +484,58 @@ Curl_safefree(data->state.aptr.userpwd); if(result) - return result; + goto out; if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) { result = Curl_add_timecondition(data, &req_buffer); if(result) - return result; + goto out; } result = Curl_add_custom_headers(data, FALSE, &req_buffer); if(result) - return result; + goto out; if(rtspreq == RTSPREQ_ANNOUNCE || rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { + curl_off_t req_clen; /* request content length */ if(data->state.upload) { - putsize = data->state.infilesize; + req_clen = data->state.infilesize; data->state.httpreq = HTTPREQ_PUT; - + result = Curl_creader_set_fread(data, req_clen); + if(result) + goto out; } else { - postsize = (data->state.infilesize != -1)? - data->state.infilesize: - (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); - data->state.httpreq = HTTPREQ_POST; + if(data->set.postfields) { + size_t plen = strlen(data->set.postfields); + req_clen = (curl_off_t)plen; + result = Curl_creader_set_buf(data, data->set.postfields, plen); + } + else if(data->state.infilesize >= 0) { + req_clen = data->state.infilesize; + result = Curl_creader_set_fread(data, req_clen); + } + else { + req_clen = 0; + result = Curl_creader_set_null(data); + } + if(result) + goto out; } - if(putsize > 0 || postsize > 0) { + if(req_clen > 0) { /* 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 = Curl_dyn_addf(&req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", - (data->state.upload ? putsize : postsize)); + req_clen); if(result) - return result; + goto out; } if(rtspreq == RTSPREQ_SET_PARAMETER || @@ -528,7 +545,7 @@ STRCONST("Content-Type: " "text/parameters\r\n")); if(result) - return result; + goto out; } } @@ -538,11 +555,9 @@ STRCONST("Content-Type: " "application/sdp\r\n")); if(result) - return result; + goto out; } } - - data->state.expect100header = FALSE; /* RTSP posts are simple/small */ } else if(rtspreq == RTSPREQ_GET_PARAMETER) { /* Check for an empty GET_PARAMETER (heartbeat) request */ @@ -550,31 +565,26 @@ data->req.no_body = TRUE; } } + else { + result = Curl_creader_set_null(data); + if(result) + goto out; + } - /* RTSP never allows chunked transfer */ - data->req.forbidchunk = TRUE; /* Finish the request buffer */ result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n")); if(result) - return result; + goto out; - if(postsize > 0) { - result = Curl_dyn_addn(&req_buffer, data->set.postfields, - (size_t)postsize); - if(result) - return result; - } + Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); /* issue the request */ - result = Curl_buffer_send(&req_buffer, data, data->req.p.http, - &data->info.request_size, 0, FIRSTSOCKET); + result = Curl_req_send(data, &req_buffer); if(result) { failf(data, "Failed sending RTSP request"); - return result; + goto out; } - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1); - /* Increment the CSeq on success */ data->state.rtsp_next_client_CSeq++; @@ -585,7 +595,8 @@ if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; } - +out: + Curl_dyn_free(&req_buffer); return result; } @@ -779,8 +790,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos, - bool *done) + bool is_eos) { struct rtsp_conn *rtspc = &(data->conn->proto.rtspc); CURLcode result = CURLE_OK; @@ -788,7 +798,6 @@ if(!data->req.header) rtspc->in_header = FALSE; - *done = FALSE; if(!blen) { goto out; } @@ -796,7 +805,7 @@ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)", blen, rtspc->in_header, is_eos)); - /* If header parsing is not onging, extract RTP messages */ + /* If header parsing is not ongoing, extract RTP messages */ if(!rtspc->in_header) { result = rtsp_filter_rtp(data, buf, blen, &consumed); if(result) @@ -812,7 +821,7 @@ /* we want to parse headers, do so */ if(data->req.header && blen) { rtspc->in_header = TRUE; - result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done); + result = Curl_http_write_resp_hds(data, buf, blen, &consumed); if(result) goto out; @@ -838,13 +847,14 @@ } if(rtspc->state != RTP_PARSE_SKIP) - *done = FALSE; + data->req.done = FALSE; /* 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=%" CURL_FORMAT_CURL_OFF_T ")", - blen, rtspc->in_header, *done, rtspc->state, data->req.size)); + blen, rtspc->in_header, data->req.done, rtspc->state, + data->req.size)); if(!result && (is_eos || blen)) { result = Curl_client_write(data, CLIENTWRITE_BODY| (is_eos? CLIENTWRITE_EOS:0), @@ -904,12 +914,12 @@ return CURLE_OK; } -CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) +CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) { if(checkprefix("CSeq:", header)) { long CSeq = 0; char *endp; - char *p = &header[5]; + const char *p = &header[5]; while(ISBLANK(*p)) p++; CSeq = strtol(p, &endp, 10); @@ -924,8 +934,7 @@ } } else if(checkprefix("Session:", header)) { - char *start; - char *end; + const char *start, *end; size_t idlen; /* Find the first non-space letter */ @@ -980,14 +989,13 @@ } static -CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport) +CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport) { /* If we receive multiple Transport response-headers, the linterleaved channels of each response header is recorded and used together for subsequent data validity checks.*/ /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */ - char *start; - char *end; + const char *start, *end; start = transport; while(start && *start) { while(*start && ISBLANK(*start) ) @@ -996,7 +1004,7 @@ if(checkprefix("interleaved=", start)) { long chan1, chan2, chan; char *endp; - char *p = start + 12; + const char *p = start + 12; chan1 = strtol(p, &endp, 10); if(p != endp && chan1 >= 0 && chan1 <= 255) { unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 237b80f..b1ffa5c 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h
@@ -31,7 +31,7 @@ extern const struct Curl_handler Curl_handler_rtsp; -CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); +CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header); #else /* disabled */
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index db3189a..68a8bf3 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c
@@ -41,6 +41,7 @@ #include "cfilters.h" #include "connect.h" #include "content_encoding.h" +#include "cw-out.h" #include "vtls/vtls.h" #include "vssh/ssh.h" #include "easyif.h" @@ -49,8 +50,8 @@ #include "select.h" #include "strdup.h" #include "http2.h" -#include "headers.h" #include "progress.h" +#include "warnless.h" #include "ws.h" /* The last 3 #include files should be in this order */ @@ -59,342 +60,18 @@ #include "memdebug.h" -static CURLcode do_init_stack(struct Curl_easy *data); - -#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP) -/* - * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF - * (\n), with special processing for CRLF sequences that are split between two - * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new - * size of the data is returned. - */ -static size_t convert_lineends(struct Curl_easy *data, - char *startPtr, size_t size) -{ - char *inPtr, *outPtr; - - /* sanity check */ - if(!startPtr || (size < 1)) { - return size; - } - - if(data->state.prev_block_had_trailing_cr) { - /* The previous block of incoming data - had a trailing CR, which was turned into a LF. */ - if(*startPtr == '\n') { - /* This block of incoming data starts with the - previous block's LF so get rid of it */ - memmove(startPtr, startPtr + 1, size-1); - size--; - /* and it wasn't a bare CR but a CRLF conversion instead */ - data->state.crlf_conversions++; - } - data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */ - } - - /* find 1st CR, if any */ - inPtr = outPtr = memchr(startPtr, '\r', size); - if(inPtr) { - /* at least one CR, now look for CRLF */ - while(inPtr < (startPtr + size-1)) { - /* note that it's size-1, so we'll never look past the last byte */ - if(memcmp(inPtr, "\r\n", 2) == 0) { - /* CRLF found, bump past the CR and copy the NL */ - inPtr++; - *outPtr = *inPtr; - /* keep track of how many CRLFs we converted */ - data->state.crlf_conversions++; - } - else { - if(*inPtr == '\r') { - /* lone CR, move LF instead */ - *outPtr = '\n'; - } - else { - /* not a CRLF nor a CR, just copy whatever it is */ - *outPtr = *inPtr; - } - } - outPtr++; - inPtr++; - } /* end of while loop */ - - if(inPtr < startPtr + size) { - /* handle last byte */ - if(*inPtr == '\r') { - /* deal with a CR at the end of the buffer */ - *outPtr = '\n'; /* copy a NL instead */ - /* note that a CRLF might be split across two blocks */ - data->state.prev_block_had_trailing_cr = TRUE; - } - else { - /* copy last byte */ - *outPtr = *inPtr; - } - outPtr++; - } - if(outPtr < startPtr + size) - /* tidy up by null terminating the now shorter data */ - *outPtr = '\0'; - - return (outPtr - startPtr); - } - return size; -} -#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ - -/* - * Curl_nwrite() is an internal write function that sends data to the - * server. Works with a socket index for the connection. - * - * If the write would block (CURLE_AGAIN), it returns CURLE_OK and - * (*nwritten == 0). Otherwise we return regular CURLcode value. - */ -CURLcode Curl_nwrite(struct Curl_easy *data, - int sockindex, - const void *buf, - size_t blen, - ssize_t *pnwritten) -{ - ssize_t nwritten; - CURLcode result = CURLE_OK; - struct connectdata *conn; - - DEBUGASSERT(sockindex >= 0 && sockindex < 2); - DEBUGASSERT(pnwritten); - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; -#ifdef CURLDEBUG - { - /* Allow debug builds to override this logic to force short sends - */ - char *p = getenv("CURL_SMALLSENDS"); - if(p) { - size_t altsize = (size_t)strtoul(p, NULL, 10); - if(altsize) - blen = CURLMIN(blen, altsize); - } - } -#endif - nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result); - if(result == CURLE_AGAIN) { - nwritten = 0; - result = CURLE_OK; - } - else if(result) { - nwritten = -1; /* make sure */ - } - else { - DEBUGASSERT(nwritten >= 0); - } - - *pnwritten = nwritten; - return result; -} - -/* - * Curl_write() is an internal write function that sends data to the - * server. Works with plain sockets, SCP, SSL or kerberos. - * - * If the write would block (CURLE_AGAIN), we return CURLE_OK and - * (*written == 0). Otherwise we return regular CURLcode value. - */ -CURLcode Curl_write(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) -{ - struct connectdata *conn; - int num; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); - return Curl_nwrite(data, num, mem, len, written); -} - -static CURLcode pausewrite(struct Curl_easy *data, - int type, /* what type of data */ - bool paused_body, - const char *ptr, - size_t len) -{ - /* signalled to pause sending on this connection, but since we have data - we want to send we need to dup it to save a copy for when the sending - is again enabled */ - struct SingleRequest *k = &data->req; - struct UrlState *s = &data->state; - unsigned int i; - bool newtype = TRUE; - - Curl_conn_ev_data_pause(data, TRUE); - - if(s->tempcount) { - for(i = 0; i< s->tempcount; i++) { - if(s->tempwrite[i].type == type && - !!s->tempwrite[i].paused_body == !!paused_body) { - /* data for this type exists */ - newtype = FALSE; - break; - } - } - DEBUGASSERT(i < 3); - if(i >= 3) - /* There are more types to store than what fits: very bad */ - return CURLE_OUT_OF_MEMORY; - } - else - i = 0; - - if(newtype) { - /* store this information in the state struct for later use */ - Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER); - s->tempwrite[i].type = type; - s->tempwrite[i].paused_body = paused_body; - s->tempcount++; - } - - if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len)) - return CURLE_OUT_OF_MEMORY; - - /* mark the connection as RECV paused */ - k->keepon |= KEEP_RECV_PAUSE; - - return CURLE_OK; -} - - -/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via - * client write callback(s) and takes care of pause requests from the - * callbacks. - */ -static CURLcode chop_write(struct Curl_easy *data, - int type, - bool skip_body_write, - char *optr, - size_t olen) -{ - struct connectdata *conn = data->conn; - curl_write_callback writeheader = NULL; - curl_write_callback writebody = NULL; - char *ptr = optr; - size_t len = olen; - void *writebody_ptr = data->set.out; - - if(!len) - return CURLE_OK; - - /* If reading is paused, append this data to the already held data for this - type. */ - if(data->req.keepon & KEEP_RECV_PAUSE) - return pausewrite(data, type, !skip_body_write, ptr, len); - - /* Determine the callback(s) to use. */ - if(!skip_body_write && - ((type & CLIENTWRITE_BODY) || - ((type & CLIENTWRITE_HEADER) && data->set.include_header))) { - writebody = data->set.fwrite_func; - } - if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) && - (data->set.fwrite_header || data->set.writeheader)) { - /* - * Write headers to the same callback or to the especially setup - * header callback function (added after version 7.7.1). - */ - writeheader = - data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func; - } - - /* Chop data, write chunks. */ - while(len) { - size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE; - - if(writebody) { - size_t wrote; - Curl_set_in_callback(data, true); - wrote = writebody(ptr, 1, chunklen, writebody_ptr); - Curl_set_in_callback(data, false); - - if(CURL_WRITEFUNC_PAUSE == wrote) { - if(conn->handler->flags & PROTOPT_NONETWORK) { - /* Protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it can't pause since the - transfer isn't done using the "normal" procedure. */ - failf(data, "Write callback asked for PAUSE when not supported"); - return CURLE_WRITE_ERROR; - } - return pausewrite(data, type, TRUE, ptr, len); - } - if(wrote != chunklen) { - failf(data, "Failure writing output to destination"); - return CURLE_WRITE_ERROR; - } - } - - ptr += chunklen; - len -= chunklen; - } - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) - /* HTTP header, but not status-line */ - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) { - unsigned char htype = (unsigned char) - (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : - (type & CLIENTWRITE_1XX ? CURLH_1XX : - (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : - CURLH_HEADER))); - CURLcode result = Curl_headers_push(data, optr, htype); - if(result) - return result; - } -#endif - - if(writeheader) { - size_t wrote; - - Curl_set_in_callback(data, true); - wrote = writeheader(optr, 1, olen, data->set.writeheader); - Curl_set_in_callback(data, false); - - if(CURL_WRITEFUNC_PAUSE == wrote) - return pausewrite(data, type, FALSE, optr, olen); - if(wrote != olen) { - failf(data, "Failed writing header"); - return CURLE_WRITE_ERROR; - } - } - - return CURLE_OK; -} - +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. - - If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the - local character encoding. This is a problem and should be changed in - the future to leave the original data alone. */ CURLcode Curl_client_write(struct Curl_easy *data, - int type, char *buf, size_t blen) + int type, const char *buf, size_t blen) { CURLcode result; -#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV) - /* FTP data may need conversion. */ - if((type & CLIENTWRITE_BODY) && - (data->conn->handler->protocol & PROTO_FAMILY_FTP) && - data->conn->proto.ftpc.transfertype == 'A') { - /* convert end-of-line markers */ - blen = convert_lineends(data, buf, blen); - } -#endif /* it is one of those, at least */ DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO)); /* BODY is only BODY (with optional EOS) */ @@ -405,69 +82,98 @@ ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0)); if(!data->req.writer_stack) { - result = do_init_stack(data); + result = do_init_writer_stack(data); if(result) return result; DEBUGASSERT(data->req.writer_stack); } - return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen); -} - -CURLcode Curl_client_unpause(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - - if(data->state.tempcount) { - /* there are buffers for sending that can be delivered as the receive - pausing is lifted! */ - unsigned int i; - unsigned int count = data->state.tempcount; - struct tempbuf writebuf[3]; /* there can only be three */ - - /* copy the structs to allow for immediate re-pausing */ - for(i = 0; i < data->state.tempcount; i++) { - writebuf[i] = data->state.tempwrite[i]; - Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER); - } - data->state.tempcount = 0; - - for(i = 0; i < count; i++) { - /* even if one function returns error, this loops through and frees - all buffers */ - if(!result) - result = chop_write(data, writebuf[i].type, - !writebuf[i].paused_body, - Curl_dyn_ptr(&writebuf[i].b), - Curl_dyn_len(&writebuf[i].b)); - Curl_dyn_free(&writebuf[i].b); - } - } + 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; } -void Curl_client_cleanup(struct Curl_easy *data) +static void cl_reset_writer(struct Curl_easy *data) { struct Curl_cwriter *writer = data->req.writer_stack; - size_t i; - while(writer) { data->req.writer_stack = writer->next; writer->cwt->do_close(data, writer); free(writer); writer = data->req.writer_stack; } +} - for(i = 0; i < data->state.tempcount; i++) { - Curl_dyn_free(&data->state.tempwrite[i].b); +static void cl_reset_reader(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + while(reader) { + data->req.reader_stack = reader->next; + reader->crt->do_close(data, reader); + free(reader); + reader = data->req.reader_stack; } - data->state.tempcount = 0; +} + +void Curl_client_cleanup(struct Curl_easy *data) +{ + cl_reset_reader(data); + cl_reset_writer(data); + data->req.bytecount = 0; data->req.headerline = 0; } -/* Write data using an unencoding writer stack. "nbytes" is not - allowed to be 0. */ +void Curl_client_reset(struct Curl_easy *data) +{ + if(data->req.rewind_read) { + /* already requested */ + CURL_TRC_READ(data, "client_reset, will rewind reader"); + } + else { + CURL_TRC_READ(data, "client_reset, clear readers"); + cl_reset_reader(data); + } + cl_reset_writer(data); + + data->req.bytecount = 0; + data->req.headerline = 0; +} + +CURLcode Curl_client_start(struct Curl_easy *data) +{ + if(data->req.rewind_read) { + struct Curl_creader *r = data->req.reader_stack; + CURLcode result = CURLE_OK; + + CURL_TRC_READ(data, "client start, rewind readers"); + while(r) { + result = r->crt->rewind(data, r); + if(result) { + failf(data, "rewind of client reader '%s' failed: %d", + r->crt->name, result); + return result; + } + r = r->next; + } + data->req.rewind_read = FALSE; + cl_reset_reader(data); + } + return CURLE_OK; +} + +bool Curl_creader_will_rewind(struct Curl_easy *data) +{ + return data->req.rewind_read; +} + +void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) +{ + data->req.rewind_read = !!enable; +} + +/* Write data using an unencoding writer stack. */ CURLcode Curl_cwriter_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) @@ -499,26 +205,6 @@ (void) writer; } -/* Real client writer to installed callbacks. */ -static CURLcode cw_client_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) -{ - (void)writer; - if(!nbytes) - return CURLE_OK; - return chop_write(data, type, FALSE, (char *)buf, nbytes); -} - -static const struct Curl_cwtype cw_client = { - "client", - NULL, - Curl_cwriter_def_init, - cw_client_write, - Curl_cwriter_def_close, - sizeof(struct Curl_cwriter) -}; - static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit) { if(limit != -1) { @@ -541,26 +227,33 @@ return SIZE_T_MAX; } +struct cw_download_ctx { + struct Curl_cwriter super; + BIT(started_response); +}; /* Download client writer in phase CURL_CW_PROTOCOL that * sees the "real" download body data. */ static CURLcode cw_download_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { + struct cw_download_ctx *ctx = writer->ctx; CURLcode result; size_t nwrite, excess_len = 0; + bool is_connect = !!(type & CLIENTWRITE_CONNECT); - if(!(type & CLIENTWRITE_BODY)) { - if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers) - return CURLE_OK; - return Curl_cwriter_write(data, writer->next, type, buf, nbytes); + if(!is_connect && !ctx->started_response) { + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + ctx->started_response = TRUE; } - if(!data->req.bytecount) { - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - if(data->req.exp100 > EXP100_SEND_DATA) - /* set time stamp to compare with when waiting for the 100 */ - data->req.start100 = Curl_now(); + if(!(type & CLIENTWRITE_BODY)) { + if(is_connect && data->set.suppress_connect_headers) + return CURLE_OK; + result = Curl_cwriter_write(data, writer->next, type, buf, nbytes); + CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d", + type, nbytes, result); + return result; } /* Here, we deal with REAL BODY bytes. All filtering and transfer @@ -572,9 +265,12 @@ if(data->req.no_body && nbytes > 0) { /* BODY arrives although we want none, bail out */ streamclose(data->conn, "ignoring body"); - DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes", - nbytes)); + CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), " + "did not want a BODY", type, nbytes); data->req.download_done = TRUE; + if(data->info.header_size) + /* if headers have been received, this is fine */ + return CURLE_OK; return CURLE_WEIRD_SERVER_REPLY; } @@ -604,14 +300,16 @@ } } - /* Update stats, write and report progress */ - data->req.bytecount += nwrite; - ++data->req.bodywrites; - if(!data->req.ignorebody && nwrite) { + if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) { result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); + CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d", + type, nbytes, result); if(result) return result; } + /* Update stats, write and report progress */ + data->req.bytecount += nwrite; + ++data->req.bodywrites; result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); if(result) return result; @@ -641,12 +339,12 @@ } static const struct Curl_cwtype cw_download = { - "download", + "protocol", NULL, Curl_cwriter_def_init, cw_download_write, Curl_cwriter_def_close, - sizeof(struct Curl_cwriter) + sizeof(struct cw_download_ctx) }; /* RAW client writer in phase CURL_CW_RAW that @@ -676,15 +374,18 @@ const struct Curl_cwtype *cwt, Curl_cwriter_phase phase) { - struct Curl_cwriter *writer; + struct Curl_cwriter *writer = NULL; CURLcode result = CURLE_OUT_OF_MEMORY; + void *p; DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter)); - writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size); - if(!writer) + p = calloc(1, cwt->cwriter_size); + if(!p) goto out; + writer = (struct Curl_cwriter *)p; writer->cwt = cwt; + writer->ctx = p; writer->phase = phase; result = cwt->do_init(data, writer); @@ -716,14 +417,14 @@ return n; } -static CURLcode do_init_stack(struct Curl_easy *data) +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, &cw_client, CURL_CW_CLIENT); + data, &Curl_cwt_out, CURL_CW_CLIENT); if(result) return result; @@ -752,7 +453,7 @@ struct Curl_cwriter **anchor = &data->req.writer_stack; if(!*anchor) { - result = do_init_stack(data); + result = do_init_writer_stack(data); if(result) return result; } @@ -766,6 +467,28 @@ return CURLE_OK; } +struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, + const char *name) +{ + struct Curl_cwriter *writer; + for(writer = data->req.writer_stack; writer; writer = writer->next) { + if(!strcmp(name, writer->cwt->name)) + return writer; + } + return NULL; +} + +struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, + const struct Curl_cwtype *cwt) +{ + struct Curl_cwriter *writer; + for(writer = data->req.writer_stack; writer; writer = writer->next) { + if(writer->cwt == cwt) + return writer; + } + return NULL; +} + void Curl_cwriter_remove_by_name(struct Curl_easy *data, const char *name) { @@ -782,40 +505,874 @@ } } -/* - * Internal read-from-socket function. This is meant to deal with plain - * sockets, SSL sockets and kerberos sockets. - * - * Returns a regular CURLcode value. - */ -CURLcode Curl_read(struct Curl_easy *data, /* transfer */ - curl_socket_t sockfd, /* read from this socket */ - char *buf, /* store read data here */ - size_t sizerequested, /* max amount to read */ - ssize_t *n) /* amount bytes read */ +bool Curl_cwriter_is_paused(struct Curl_easy *data) { - CURLcode result = CURLE_RECV_ERROR; - ssize_t nread = 0; - size_t bytesfromsocket = 0; - char *buffertofill = NULL; - struct connectdata *conn = data->conn; + return Curl_cw_out_is_paused(data); +} - /* Set 'num' to 0 or 1, depending on which socket that has been sent here. - If it is the second socket, we set num to 1. Otherwise to 0. This lets - us use the correct ssl handle. */ - int num = (sockfd == conn->sock[SECONDARYSOCKET]); +CURLcode Curl_cwriter_unpause(struct Curl_easy *data) +{ + return Curl_cw_out_unpause(data); +} - *n = 0; /* reset amount to zero */ +CURLcode Curl_creader_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, size_t *nread, bool *eos) +{ + *nread = 0; + *eos = FALSE; + if(!reader) + return CURLE_READ_ERROR; + return reader->crt->do_read(data, reader, buf, blen, nread, eos); +} - bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size); - buffertofill = buf; +CURLcode Curl_creader_def_init(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return CURLE_OK; +} - nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result); - if(nread < 0) +void Curl_creader_def_close(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; +} + +CURLcode Curl_creader_def_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos) +{ + if(reader->next) + return reader->next->crt->do_read(data, reader->next, buf, blen, + nread, eos); + else { + *nread = 0; + *eos = FALSE; + return CURLE_READ_ERROR; + } +} + +bool Curl_creader_def_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return FALSE; +} + +curl_off_t Curl_creader_def_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + return reader->next? + reader->next->crt->total_length(data, reader->next) : -1; +} + +CURLcode Curl_creader_def_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + (void)data; + (void)reader; + (void)offset; + return CURLE_READ_ERROR; +} + +CURLcode Curl_creader_def_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return CURLE_OK; +} + +CURLcode Curl_creader_def_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return CURLE_OK; +} + +void Curl_creader_def_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature) +{ + (void)data; + (void)reader; + (void)premature; +} + +struct cr_in_ctx { + struct Curl_creader super; + curl_read_callback read_cb; + void *cb_user_data; + curl_off_t total_len; + curl_off_t read_len; + CURLcode error_result; + BIT(seen_eos); + BIT(errored); + BIT(has_used_cb); +}; + +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; + ctx->read_len = 0; + return CURLE_OK; +} + +/* Real client reader to installed client callbacks. */ +static CURLcode cr_in_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_in_ctx *ctx = reader->ctx; + size_t nread; + + /* Once we have errored, we will return the same error forever */ + if(ctx->errored) { + *pnread = 0; + *peos = FALSE; + return ctx->error_result; + } + if(ctx->seen_eos) { + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + /* respect length limitations */ + if(ctx->total_len >= 0) { + curl_off_t remain = ctx->total_len - ctx->read_len; + if(remain <= 0) + blen = 0; + else if(remain < (curl_off_t)blen) + blen = (size_t)remain; + } + nread = 0; + if(ctx->read_cb && blen) { + Curl_set_in_callback(data, true); + nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data); + Curl_set_in_callback(data, false); + ctx->has_used_cb = TRUE; + } + + switch(nread) { + case 0: + if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { + failf(data, "client read function EOF fail, " + "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T + " of needed bytes read", ctx->read_len, ctx->total_len); + return CURLE_READ_ERROR; + } + *pnread = 0; + *peos = TRUE; + ctx->seen_eos = TRUE; + break; + + case CURL_READFUNC_ABORT: + failf(data, "operation aborted by callback"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_ABORTED_BY_CALLBACK; + return CURLE_ABORTED_BY_CALLBACK; + + case CURL_READFUNC_PAUSE: + if(data->conn->handler->flags & PROTOPT_NONETWORK) { + /* protocols that work without network cannot be paused. This is + actually only FILE:// just now, and it can't pause since the transfer + isn't done using the "normal" procedure. */ + failf(data, "Read callback asked for PAUSE when not supported"); + return CURLE_READ_ERROR; + } + /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ + data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ + *pnread = 0; + *peos = FALSE; + break; /* nothing was read */ + + default: + if(nread > blen) { + /* the read function returned a too large value */ + failf(data, "read function returned funny value"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_READ_ERROR; + return CURLE_READ_ERROR; + } + ctx->read_len += nread; + if(ctx->total_len >= 0) + ctx->seen_eos = (ctx->read_len >= ctx->total_len); + *pnread = nread; + *peos = ctx->seen_eos; + break; + } + CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T + ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, nread=%zu, eos=%d", + blen, ctx->total_len, ctx->read_len, CURLE_OK, + *pnread, *peos); + return CURLE_OK; +} + +static bool cr_in_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->has_used_cb; +} + +static curl_off_t cr_in_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->total_len; +} + +static CURLcode cr_in_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + struct cr_in_ctx *ctx = reader->ctx; + int seekerr = CURL_SEEKFUNC_CANTSEEK; + + DEBUGASSERT(data->conn); + /* already started reading? */ + if(ctx->read_len) + return CURLE_READ_ERROR; + + if(data->set.seek_func) { + Curl_set_in_callback(data, true); + seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET); + Curl_set_in_callback(data, false); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_READ_ERROR; + } + /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (offset - passed > (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : + curlx_sotouz(offset - passed); + size_t actuallyread; + + Curl_set_in_callback(data, true); + actuallyread = ctx->read_cb(scratch, 1, readthisamountnow, + ctx->cb_user_data); + Curl_set_in_callback(data, false); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T + " bytes from the input", passed); + return CURLE_READ_ERROR; + } + } while(passed < offset); + } + + /* now, decrease the size of the read */ + if(ctx->total_len > 0) { + ctx->total_len -= offset; + + if(ctx->total_len <= 0) { + failf(data, "File already completely uploaded"); + return CURLE_PARTIAL_FILE; + } + } + /* we've passed, proceed as normal */ + return CURLE_OK; +} + +static CURLcode cr_in_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + + /* If we never invoked the callback, there is noting to rewind */ + if(!ctx->has_used_cb) + return CURLE_OK; + + if(data->set.seek_func) { + int err; + + Curl_set_in_callback(data, true); + err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); + Curl_set_in_callback(data, false); + CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err); + if(err) { + failf(data, "seek callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else if(data->set.ioctl_func) { + curlioerr err; + + Curl_set_in_callback(data, true); + err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); + Curl_set_in_callback(data, false); + CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err); + if(err) { + failf(data, "ioctl callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else { + /* If no CURLOPT_READFUNCTION is used, we know that we operate on a + given FILE * stream and we can actually attempt to rewind that + ourselves with fseek() */ + if(data->state.fread_func == (curl_read_callback)fread) { + int err = fseek(data->state.in, 0, SEEK_SET); + CURL_TRC_READ(data, "cr_in, rewind via fseek -> %d(%d)", + (int)err, (int)errno); + if(-1 != err) + /* successful rewind */ + return CURLE_OK; + } + + /* no callback set or failure above, makes us fail at once */ + failf(data, "necessary data rewind wasn't possible"); + return CURLE_SEND_FAIL_REWIND; + } + return CURLE_OK; +} + + +static const struct Curl_crtype cr_in = { + "cr-in", + cr_in_init, + cr_in_read, + Curl_creader_def_close, + cr_in_needs_rewind, + cr_in_total_length, + cr_in_resume_from, + cr_in_rewind, + Curl_creader_def_unpause, + Curl_creader_def_done, + sizeof(struct cr_in_ctx) +}; + +CURLcode Curl_creader_create(struct Curl_creader **preader, + struct Curl_easy *data, + const struct Curl_crtype *crt, + Curl_creader_phase phase) +{ + struct Curl_creader *reader = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; + void *p; + + DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader)); + p = calloc(1, crt->creader_size); + if(!p) goto out; - *n += nread; - result = CURLE_OK; + reader = (struct Curl_creader *)p; + reader->crt = crt; + reader->ctx = p; + reader->phase = phase; + result = crt->do_init(data, reader); + out: + *preader = result? NULL : reader; + if(result) + free(reader); return result; } + +void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader) +{ + if(reader) { + reader->crt->do_close(data, reader); + free(reader); + } +} + +struct cr_lc_ctx { + struct Curl_creader super; + struct bufq buf; + BIT(read_eos); /* we read an EOS from the next reader */ + BIT(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_lc_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); + return CURLE_OK; +} + +static void cr_lc_close(struct Curl_easy *data, struct Curl_creader *reader) +{ + struct cr_lc_ctx *ctx = reader->ctx; + (void)data; + Curl_bufq_free(&ctx->buf); +} + +/* client reader doing line end conversions. */ +static CURLcode cr_lc_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_lc_ctx *ctx = reader->ctx; + CURLcode result; + size_t nread, i, start, n; + bool eos; + + if(ctx->eos) { + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + + if(Curl_bufq_is_empty(&ctx->buf)) { + if(ctx->read_eos) { + ctx->eos = TRUE; + *pnread = 0; + *peos = TRUE; + return CURLE_OK; + } + /* Still getting data form the next reader, ctx->buf is empty */ + result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); + if(result) + return result; + ctx->read_eos = eos; + + if(!nread || !memchr(buf, '\n', nread)) { + /* nothing to convert, return this right away */ + if(ctx->read_eos) + ctx->eos = TRUE; + *pnread = nread; + *peos = ctx->eos; + goto out; + } + + /* at least one \n needs conversion to '\r\n', place into ctx->buf */ + for(i = start = 0; i < nread; ++i) { + if(buf[i] != '\n') + continue; + /* on a soft limit bufq, we do not need to check length */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(!result) + result = Curl_bufq_cwrite(&ctx->buf, STRCONST("\r\n"), &n); + if(result) + return result; + start = i + 1; + if(!data->set.crlf && (data->state.infilesize != -1)) { + /* we're here only because FTP is in ASCII mode... + bump infilesize for the LF we just added */ + data->state.infilesize++; + /* comment: this might work for FTP, but in HTTP we could not change + * the content length after having started the request... */ + } + } + } + + DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf)); + *peos = FALSE; + result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); + if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* no more data, read all, done. */ + ctx->eos = TRUE; + *peos = TRUE; + } + +out: + CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d", + blen, result, *pnread, *peos); + return result; +} + +static curl_off_t cr_lc_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_lc = { + "cr-lineconv", + cr_lc_init, + cr_lc_read, + cr_lc_close, + Curl_creader_def_needs_rewind, + cr_lc_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_done, + sizeof(struct cr_lc_ctx) +}; + +static CURLcode cr_lc_add(struct Curl_easy *data) +{ + struct Curl_creader *reader = NULL; + CURLcode result; + + result = Curl_creader_create(&reader, data, &cr_lc, + CURL_CR_CONTENT_ENCODE); + if(!result) + result = Curl_creader_add(data, reader); + + if(result && reader) + Curl_creader_free(data, reader); + return result; +} + +static CURLcode do_init_reader_stack(struct Curl_easy *data, + struct Curl_creader *r) +{ + CURLcode result = CURLE_OK; + curl_off_t clen; + + DEBUGASSERT(r); + DEBUGASSERT(r->crt); + DEBUGASSERT(r->phase == CURL_CR_CLIENT); + DEBUGASSERT(!data->req.reader_stack); + + data->req.reader_stack = r; + clen = r->crt->total_length(data, r); + /* if we do not have 0 length init, and crlf conversion is wanted, + * add the reader for it */ + if(clen && (data->set.crlf +#ifdef CURL_DO_LINEEND_CONV + || data->state.prefer_ascii +#endif + )) { + result = cr_lc_add(data); + if(result) + return result; + } + + return result; +} + +CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) +{ + CURLcode result; + struct Curl_creader *r; + struct cr_in_ctx *ctx; + + result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT); + if(result) + goto out; + ctx = r->ctx; + ctx->total_len = len; + + cl_reset_reader(data); + result = do_init_reader_stack(data, r); +out: + CURL_TRC_READ(data, "add fread reader, len=%"CURL_FORMAT_CURL_OFF_T + " -> %d", len, result); + return result; +} + +CURLcode Curl_creader_add(struct Curl_easy *data, + struct Curl_creader *reader) +{ + CURLcode result; + struct Curl_creader **anchor = &data->req.reader_stack; + + if(!*anchor) { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + return result; + } + + /* Insert the writer as first in its phase. + * Skip existing readers of lower phases. */ + while(*anchor && (*anchor)->phase < reader->phase) + anchor = &((*anchor)->next); + reader->next = *anchor; + *anchor = reader; + return CURLE_OK; +} + +CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r) +{ + CURLcode result; + + DEBUGASSERT(r); + DEBUGASSERT(r->crt); + DEBUGASSERT(r->phase == CURL_CR_CLIENT); + + cl_reset_reader(data); + result = do_init_reader_stack(data, r); + if(result) + Curl_creader_free(data, r); + return result; +} + +CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, + size_t *nread, bool *eos) +{ + CURLcode result; + + DEBUGASSERT(buf); + DEBUGASSERT(blen); + DEBUGASSERT(nread); + DEBUGASSERT(eos); + + if(!data->req.reader_stack) { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + return result; + DEBUGASSERT(data->req.reader_stack); + } + + result = Curl_creader_read(data, data->req.reader_stack, buf, blen, + nread, eos); + CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d", + blen, result, *nread, *eos); + return result; +} + +bool Curl_creader_needs_rewind(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + while(reader) { + if(reader->crt->needs_rewind(data, reader)) { + CURL_TRC_READ(data, "client reader needs rewind before next request"); + return TRUE; + } + reader = reader->next; + } + return FALSE; +} + +static CURLcode cr_null_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + (void)data; + (void)reader; + (void)buf; + (void)blen; + *pnread = 0; + *peos = TRUE; + return CURLE_OK; +} + +static curl_off_t cr_null_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + /* this reader changes length depending on input */ + (void)data; + (void)reader; + return 0; +} + +static const struct Curl_crtype cr_null = { + "cr-null", + Curl_creader_def_init, + cr_null_read, + Curl_creader_def_close, + Curl_creader_def_needs_rewind, + cr_null_total_length, + Curl_creader_def_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_done, + sizeof(struct Curl_creader) +}; + +CURLcode Curl_creader_set_null(struct Curl_easy *data) +{ + struct Curl_creader *r; + CURLcode result; + + result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT); + if(result) + return result; + + cl_reset_reader(data); + return do_init_reader_stack(data, r); +} + +struct cr_buf_ctx { + struct Curl_creader super; + const char *buf; + size_t blen; + size_t index; +}; + +static CURLcode cr_buf_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *pnread, bool *peos) +{ + struct cr_buf_ctx *ctx = reader->ctx; + size_t nread = ctx->blen - ctx->index; + + (void)data; + if(!nread || !ctx->buf) { + *pnread = 0; + *peos = TRUE; + } + else { + if(nread > blen) + nread = blen; + memcpy(buf, ctx->buf + ctx->index, nread); + *pnread = nread; + ctx->index += nread; + *peos = (ctx->index == ctx->blen); + } + CURL_TRC_READ(data, "cr_buf_read(len=%zu) -> 0, nread=%zu, eos=%d", + blen, *pnread, *peos); + return CURLE_OK; +} + +static bool cr_buf_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_buf_ctx *ctx = reader->ctx; + (void)data; + return ctx->index > 0; +} + +static curl_off_t cr_buf_total_length(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_buf_ctx *ctx = reader->ctx; + (void)data; + return (curl_off_t)ctx->blen; +} + +static CURLcode cr_buf_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset) +{ + struct cr_buf_ctx *ctx = reader->ctx; + size_t boffset; + + (void)data; + DEBUGASSERT(data->conn); + /* already started reading? */ + if(ctx->index) + return CURLE_READ_ERROR; + if(offset <= 0) + return CURLE_OK; + boffset = (size_t)offset; + if(boffset > ctx->blen) + return CURLE_READ_ERROR; + + ctx->buf += boffset; + ctx->blen -= boffset; + return CURLE_OK; +} + +static const struct Curl_crtype cr_buf = { + "cr-buf", + Curl_creader_def_init, + cr_buf_read, + Curl_creader_def_close, + cr_buf_needs_rewind, + cr_buf_total_length, + cr_buf_resume_from, + Curl_creader_def_rewind, + Curl_creader_def_unpause, + Curl_creader_def_done, + sizeof(struct cr_buf_ctx) +}; + +CURLcode Curl_creader_set_buf(struct Curl_easy *data, + const char *buf, size_t blen) +{ + CURLcode result; + struct Curl_creader *r; + struct cr_buf_ctx *ctx; + + result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT); + if(result) + goto out; + ctx = r->ctx; + ctx->buf = buf; + ctx->blen = blen; + ctx->index = 0; + + cl_reset_reader(data); + result = do_init_reader_stack(data, r); +out: + CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result); + return result; +} + +curl_off_t Curl_creader_total_length(struct Curl_easy *data) +{ + struct Curl_creader *r = data->req.reader_stack; + return r? r->crt->total_length(data, r) : -1; +} + +curl_off_t Curl_creader_client_length(struct Curl_easy *data) +{ + struct Curl_creader *r = data->req.reader_stack; + while(r && r->phase != CURL_CR_CLIENT) + r = r->next; + return r? r->crt->total_length(data, r) : -1; +} + +CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset) +{ + struct Curl_creader *r = data->req.reader_stack; + while(r && r->phase != CURL_CR_CLIENT) + r = r->next; + return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR; +} + +CURLcode Curl_creader_unpause(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + CURLcode result = CURLE_OK; + + while(reader) { + result = reader->crt->unpause(data, reader); + if(result) + break; + reader = reader->next; + } + return result; +} + +void Curl_creader_done(struct Curl_easy *data, int premature) +{ + struct Curl_creader *reader = data->req.reader_stack; + while(reader) { + reader->crt->done(data, reader, premature); + reader = reader->next; + } +} + +struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, + const struct Curl_crtype *crt) +{ + struct Curl_creader *r; + for(r = data->req.reader_stack; r; r = r->next) { + if(r->crt == crt) + return r; + } + return NULL; + +}
diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 7deae2a..82a2902 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h
@@ -55,22 +55,27 @@ * Write `len` bytes at `prt` to the client. `type` indicates what * kind of data is being written. */ -CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, +CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr, size_t len) WARN_UNUSED_RESULT; /** - * For a paused transfer, there might be buffered data held back. - * Attempt to flush this data to the client. This *may* trigger - * another pause of the transfer. - */ -CURLcode Curl_client_unpause(struct Curl_easy *data); - -/** * Free all resources related to client writing. */ void Curl_client_cleanup(struct Curl_easy *data); /** + * Reset readers and writer chains, keep rewind information + * when necessary. + */ +void Curl_client_reset(struct Curl_easy *data); + +/** + * A new request is starting, perform any ops like rewinding + * previous readers when needed. + */ +CURLcode Curl_client_start(struct Curl_easy *data); + +/** * Client Writers - a chain passing transfer BODY data to the client. * Main application: HTTP and related protocols * Other uses: monitoring of download progress @@ -112,10 +117,16 @@ size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */ }; -/* Client writer instance */ +/* Client writer instance, allocated on creation. + * `void *ctx` is the pointer from the allocation of + * the `struct Curl_cwriter` itself. This is suitable for "downcasting" + * by the writers implementation. See https://github.com/curl/curl/pull/13054 + * for the alignment problems that arise otherwise. + */ struct Curl_cwriter { const struct Curl_cwtype *cwt; /* type implementation */ struct Curl_cwriter *next; /* Downstream writer. */ + void *ctx; /* allocated instance pointer */ Curl_cwriter_phase phase; /* phase at which it operates */ }; @@ -148,9 +159,19 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *writer); +/** + * Look up an installed client writer on `data` by its type. + * @return first writer with that type or NULL + */ +struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data, + const struct Curl_cwtype *cwt); + void Curl_cwriter_remove_by_name(struct Curl_easy *data, const char *name); +struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data, + const char *name); + /** * Convenience method for calling `writer->do_write()` that * checks for NULL writer. @@ -160,6 +181,16 @@ const char *buf, size_t nbytes); /** + * Return TRUE iff client writer is paused. + */ +bool Curl_cwriter_is_paused(struct Curl_easy *data); + +/** + * Unpause client writer and flush any buffered date to the client. + */ +CURLcode Curl_cwriter_unpause(struct Curl_easy *data); + +/** * Default implementations for do_init, do_write, do_close that * do nothing and pass the data through. */ @@ -172,22 +203,205 @@ struct Curl_cwriter *writer); -/* internal read-function, does plain socket, SSL and krb4 */ -CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd, - char *buf, size_t buffersize, - ssize_t *n); -/* internal write-function, does plain socket, SSL, SCP, SFTP and krb4 */ -CURLcode Curl_write(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, size_t len, - ssize_t *written); +/* Client Reader Type, provides the implementation */ +struct Curl_crtype { + const char *name; /* writer name. */ + CURLcode (*do_init)(struct Curl_easy *data, struct Curl_creader *reader); + CURLcode (*do_read)(struct Curl_easy *data, struct Curl_creader *reader, + char *buf, size_t blen, size_t *nread, bool *eos); + void (*do_close)(struct Curl_easy *data, struct Curl_creader *reader); + bool (*needs_rewind)(struct Curl_easy *data, struct Curl_creader *reader); + curl_off_t (*total_length)(struct Curl_easy *data, + struct Curl_creader *reader); + CURLcode (*resume_from)(struct Curl_easy *data, + struct Curl_creader *reader, curl_off_t offset); + CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader); + CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader); + void (*done)(struct Curl_easy *data, + struct Curl_creader *reader, int premature); + size_t creader_size; /* sizeof() allocated struct Curl_creader */ +}; -/* internal write-function, using sockindex for connection destination */ -CURLcode Curl_nwrite(struct Curl_easy *data, - int sockindex, - const void *buf, - size_t blen, - ssize_t *pnwritten); +/* Phase a reader operates at. */ +typedef enum { + CURL_CR_NET, /* data send to the network (connection filters) */ + CURL_CR_TRANSFER_ENCODE, /* add transfer-encodings */ + CURL_CR_PROTOCOL, /* before transfer, but after content decoding */ + CURL_CR_CONTENT_ENCODE, /* add content-encodings */ + CURL_CR_CLIENT /* data read from client */ +} Curl_creader_phase; + +/* Client reader instance, allocated on creation. + * `void *ctx` is the pointer from the allocation of + * the `struct Curl_cwriter` itself. This is suitable for "downcasting" + * by the writers implementation. See https://github.com/curl/curl/pull/13054 + * for the alignment problems that arise otherwise. + */ +struct Curl_creader { + const struct Curl_crtype *crt; /* type implementation */ + struct Curl_creader *next; /* Downstream reader. */ + void *ctx; + Curl_creader_phase phase; /* phase at which it operates */ +}; + +/** + * Default implementations for do_init, do_write, do_close that + * do nothing and pass the data through. + */ +CURLcode Curl_creader_def_init(struct Curl_easy *data, + struct Curl_creader *reader); +void Curl_creader_def_close(struct Curl_easy *data, + struct Curl_creader *reader); +CURLcode Curl_creader_def_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, + size_t *nread, bool *eos); +bool Curl_creader_def_needs_rewind(struct Curl_easy *data, + struct Curl_creader *reader); +curl_off_t Curl_creader_def_total_length(struct Curl_easy *data, + struct Curl_creader *reader); +CURLcode Curl_creader_def_resume_from(struct Curl_easy *data, + struct Curl_creader *reader, + curl_off_t offset); +CURLcode Curl_creader_def_rewind(struct Curl_easy *data, + struct Curl_creader *reader); +CURLcode Curl_creader_def_unpause(struct Curl_easy *data, + struct Curl_creader *reader); +void Curl_creader_def_done(struct Curl_easy *data, + struct Curl_creader *reader, int premature); + +/** + * Convenience method for calling `reader->do_read()` that + * checks for NULL reader. + */ +CURLcode Curl_creader_read(struct Curl_easy *data, + struct Curl_creader *reader, + char *buf, size_t blen, size_t *nread, bool *eos); + +/** + * Create a new creader instance with given type and phase. Is not + * inserted into the writer chain by this call. + * Invokes `reader->do_init()`. + */ +CURLcode Curl_creader_create(struct Curl_creader **preader, + struct Curl_easy *data, + const struct Curl_crtype *cr_handler, + Curl_creader_phase phase); + +/** + * Free a creader instance. + * Invokes `reader->do_close()`. + */ +void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader); + +/** + * Adds a reader to the transfer's reader chain. + * The readers `phase` determines where in the chain it is inserted. + */ +CURLcode Curl_creader_add(struct Curl_easy *data, + struct Curl_creader *reader); + +/** + * Set the given reader, which needs to be of type CURL_CR_CLIENT, + * as the new first reader. Discard any installed readers and init + * the reader chain anew. + * The function takes ownership of `r`. + */ +CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r); + +/** + * Read at most `blen` bytes at `buf` from the client. + * @param data the transfer to read client bytes for + * @param buf the memory location to read to + * @param blen the amount of memory at `buf` + * @param nread on return the number of bytes read into `buf` + * @param eos TRUE iff bytes are the end of data from client + * @return CURLE_OK on successful read (even 0 length) or error + */ +CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, + size_t *nread, bool *eos) WARN_UNUSED_RESULT; + +/** + * TRUE iff client reader needs rewing before it can be used for + * a retry request. + */ +bool Curl_creader_needs_rewind(struct Curl_easy *data); + +/** + * TRUE iff client reader will rewind at next start + */ +bool Curl_creader_will_rewind(struct Curl_easy *data); + +/** + * En-/disable rewind of client reader at next start. + */ +void Curl_creader_set_rewind(struct Curl_easy *data, bool enable); + +/** + * Get the total length of bytes provided by the installed readers. + * This is independent of the amount already delivered and is calculated + * by all readers in the stack. If a reader like "chunked" or + * "crlf conversion" is installed, the returned length will be -1. + * @return -1 if length is indeterminate + */ +curl_off_t Curl_creader_total_length(struct Curl_easy *data); + +/** + * Get the total length of bytes provided by the reader at phase + * CURL_CR_CLIENT. This may not match the amount of bytes read + * for a request, depending if other, encoding readers are also installed. + * However it allows for rough estimation of the overall length. + * @return -1 if length is indeterminate + */ +curl_off_t Curl_creader_client_length(struct Curl_easy *data); + +/** + * Ask the installed reader at phase CURL_CR_CLIENT to start + * reading from the given offset. On success, this will reduce + * the `total_length()` by the amount. + * @param data the transfer to read client bytes for + * @param offset the offset where to start reads from, negative + * values will be ignored. + * @return CURLE_OK if offset could be set + * CURLE_READ_ERROR if not supported by reader or seek/read failed + * of offset larger then total length + * CURLE_PARTIAL_FILE if offset led to 0 total length + */ +CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); + +/** + * Unpause all installed readers. + */ +CURLcode Curl_creader_unpause(struct Curl_easy *data); + +/** + * Tell all client readers that they are done. + */ +void Curl_creader_done(struct Curl_easy *data, int premature); + +/** + * Look up an installed client reader on `data` by its type. + * @return first reader with that type or NULL + */ +struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, + const struct Curl_crtype *crt); + + +/** + * Set the client reader to provide 0 bytes, immediate EOS. + */ +CURLcode Curl_creader_set_null(struct Curl_easy *data); + +/** + * Set the client reader the reads from fread callback. + */ +CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len); + +/** + * Set the client reader the reads from the supplied buf (NOT COPIED). + */ +CURLcode Curl_creader_set_buf(struct Curl_easy *data, + const char *buf, size_t blen); #endif /* HEADER_CURL_SENDF_H */
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index a527077..e8b2545 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c
@@ -52,6 +52,8 @@ #include "hsts.h" #include "tftp.h" #include "strdup.h" +#include "escape.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -109,45 +111,32 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) { - CURLcode result = CURLE_OK; char *user = NULL; char *passwd = NULL; + DEBUGASSERT(userp); + DEBUGASSERT(passwdp); + /* Parse the login details if specified. It not then we treat NULL as a hint to clear the existing data */ if(option) { size_t len = strlen(option); + CURLcode result; if(len > CURL_MAX_INPUT_LENGTH) return CURLE_BAD_FUNCTION_ARGUMENT; - result = Curl_parse_login_details(option, len, - (userp ? &user : NULL), - (passwdp ? &passwd : NULL), - NULL); + result = Curl_parse_login_details(option, len, &user, &passwd, NULL); + if(result) + return result; } - if(!result) { - /* Store the username part of option if required */ - if(userp) { - if(!user && option && option[0] == ':') { - /* Allocate an empty string instead of returning NULL as user name */ - user = strdup(""); - if(!user) - result = CURLE_OUT_OF_MEMORY; - } + free(*userp); + *userp = user; - Curl_safefree(*userp); - *userp = user; - } + free(*passwdp); + *passwdp = passwd; - /* Store the password part of option if required */ - if(passwdp) { - Curl_safefree(*passwdp); - *passwdp = passwd; - } - } - - return result; + return CURLE_OK; } #define C_SSLVERSION_VALUE(x) (x & 0xffff) @@ -155,6 +144,12 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) { + /* + * We are asked to cherry-pick protocols, so play it safe and disallow all + * protocols to start with, and re-add the wanted ones back in. + */ + *val = 0; + if(!str) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -163,8 +158,6 @@ return CURLE_OK; } - *val = 0; - do { const char *token = str; size_t tlen; @@ -514,11 +507,12 @@ data. */ char *p = Curl_memdup0(argptr, (size_t)data->set.postfieldsize); - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); if(!p) result = CURLE_OUT_OF_MEMORY; - else + else { + free(data->set.str[STRING_COPYPOSTFIELDS]); data->set.str[STRING_COPYPOSTFIELDS] = p; + } } } @@ -532,7 +526,7 @@ */ data->set.postfields = va_arg(param, void *); /* Release old copied data. */ - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); data->set.method = HTTPREQ_POST; break; @@ -548,7 +542,7 @@ if(data->set.postfieldsize < bigsize && data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); data->set.postfields = NULL; } @@ -567,7 +561,7 @@ if(data->set.postfieldsize < bigsize && data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); data->set.postfields = NULL; } @@ -785,22 +779,20 @@ /* * Set cookie file name to dump all cookies to when we're done. */ - { - struct CookieInfo *newcookies; result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], va_arg(param, char *)); - - /* - * Activate the cookie parser. This may or may not already - * have been made. - */ - newcookies = Curl_cookie_init(data, NULL, data->cookies, - data->set.cookiesession); - if(!newcookies) - result = CURLE_OUT_OF_MEMORY; - data->cookies = newcookies; - } - break; + if(!result) { + /* + * Activate the cookie parser. This may or may not already + * have been made. + */ + struct CookieInfo *newcookies = + Curl_cookie_init(data, NULL, data->cookies, data->set.cookiesession); + if(!newcookies) + result = CURLE_OUT_OF_MEMORY; + data->cookies = newcookies; + } + break; case CURLOPT_COOKIESESSION: /* @@ -909,7 +901,7 @@ /* accepted */ break; #endif -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 case CURL_HTTP_VERSION_3: case CURL_HTTP_VERSION_3ONLY: /* accepted */ @@ -1018,9 +1010,6 @@ /* switch off bits we can't support */ #ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ #endif #ifndef USE_SPNEGO auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without @@ -1099,9 +1088,6 @@ /* switch off bits we can't support */ #ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ #endif #ifndef USE_SPNEGO auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without @@ -1314,6 +1300,7 @@ return CURLE_BAD_FUNCTION_ARGUMENT; data->set.ftpsslauth = (unsigned char)(curl_ftpauth)arg; break; +#ifdef HAVE_GSSAPI case CURLOPT_KRBLEVEL: /* * A string that defines the kerberos security level. @@ -1323,6 +1310,7 @@ data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]); break; #endif +#endif #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) case CURLOPT_FTP_CREATE_MISSING_DIRS: /* @@ -1589,13 +1577,24 @@ break; #ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: + case CURLOPT_PROXYUSERPWD: { /* * user:password needed to use the proxy */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_PROXYUSERNAME], - &data->set.str[STRING_PROXYPASSWORD]); + char *u = NULL; + char *p = NULL; + result = setstropt_userpwd(va_arg(param, char *), &u, &p); + + /* URL decode the components */ + if(!result && u) + result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + if(!result && p) + result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL, + REJECT_ZERO); + free(u); + free(p); + } break; case CURLOPT_PROXYUSERNAME: /* @@ -1847,7 +1846,7 @@ /* * flag to set engine as default. */ - Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], NULL); + Curl_safefree(data->set.str[STRING_SSL_ENGINE]); result = Curl_ssl_set_engine_default(data); break; case CURLOPT_CRLF: @@ -2210,9 +2209,6 @@ * The application kindly asks for a differently sized receive buffer. * If it seems reasonable, we'll use it. */ - if(data->state.buffer) - return CURLE_BAD_FUNCTION_ARGUMENT; - arg = va_arg(param, long); if(arg > READBUFFER_MAX) @@ -2238,7 +2234,6 @@ arg = UPLOADBUFFER_MIN; data->set.upload_buffer_size = (unsigned int)arg; - Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */ break; case CURLOPT_NOSIGNAL: @@ -2315,7 +2310,7 @@ Curl_hsts_cleanup(&data->hsts); data->hsts = data->share->hsts; } -#endif /* CURL_DISABLE_HTTP */ +#endif #ifdef USE_SSL if(data->share->sslsession) { data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; @@ -2625,7 +2620,7 @@ break; #endif -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case CURLOPT_ADDRESS_SCOPE: /* * Use this scope id when using IPv6 @@ -2657,22 +2652,18 @@ break; case CURLOPT_PROTOCOLS_STR: { - curl_prot_t prot; argptr = va_arg(param, char *); - result = protocol2num(argptr, &prot); + result = protocol2num(argptr, &data->set.allowed_protocols); if(result) return result; - data->set.allowed_protocols = prot; break; } case CURLOPT_REDIR_PROTOCOLS_STR: { - curl_prot_t prot; argptr = va_arg(param, char *); - result = protocol2num(argptr, &prot); + result = protocol2num(argptr, &data->set.redir_protocols); if(result) return result; - data->set.redir_protocols = prot; break; } @@ -2867,13 +2858,13 @@ #endif case CURLOPT_TLSAUTH_TYPE: argptr = va_arg(param, char *); - if(argptr && !strncasecompare(argptr, "SRP", strlen("SRP"))) + if(argptr && !strcasecompare(argptr, "SRP")) return CURLE_BAD_FUNCTION_ARGUMENT; break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: argptr = va_arg(param, char *); - if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP"))) + if(argptr && !strcasecompare(argptr, "SRP")) return CURLE_BAD_FUNCTION_ARGUMENT; break; #endif @@ -3137,6 +3128,49 @@ break; } #endif +#ifdef USE_ECH + case CURLOPT_ECH: { + size_t plen = 0; + + argptr = va_arg(param, char *); + if(!argptr) { + data->set.tls_ech = CURLECH_DISABLE; + result = CURLE_BAD_FUNCTION_ARGUMENT; + return result; + } + plen = strlen(argptr); + if(plen > CURL_MAX_INPUT_LENGTH) { + data->set.tls_ech = CURLECH_DISABLE; + result = CURLE_BAD_FUNCTION_ARGUMENT; + return result; + } + /* set tls_ech flag value, preserving CLA_CFG bit */ + if(plen == 5 && !strcmp(argptr, "false")) + data->set.tls_ech = CURLECH_DISABLE + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen == 6 && !strcmp(argptr, "grease")) + data->set.tls_ech = CURLECH_GREASE + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen == 4 && !strcmp(argptr, "true")) + data->set.tls_ech = CURLECH_ENABLE + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen == 4 && !strcmp(argptr, "hard")) + data->set.tls_ech = CURLECH_HARD + | (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen > 5 && !strncmp(argptr, "ecl:", 4)) { + result = Curl_setstropt(&data->set.str[STRING_ECH_CONFIG], argptr + 4); + if(result) + return result; + data->set.tls_ech |= CURLECH_CLA_CFG; + } + else if(plen > 4 && !strncmp(argptr, "pn:", 3)) { + result = Curl_setstropt(&data->set.str[STRING_ECH_PUBLIC], argptr + 3); + if(result) + return result; + } + break; + } +#endif case CURLOPT_QUICK_EXIT: data->set.quick_exit = (0 != va_arg(param, long)) ? 1L:0L; break;
diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h index 3c14a05..b023746 100644 --- a/Utilities/cmcurl/lib/setopt.h +++ b/Utilities/cmcurl/lib/setopt.h
@@ -24,9 +24,10 @@ * ***************************************************************************/ -CURLcode Curl_setstropt(char **charp, const char *s); +CURLcode Curl_setstropt(char **charp, const char *s) WARN_UNUSED_RESULT; CURLcode Curl_setblobopt(struct curl_blob **blobp, - const struct curl_blob *blob); -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg); + const struct curl_blob *blob) WARN_UNUSED_RESULT; +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg) + WARN_UNUSED_RESULT; #endif /* HEADER_CURL_SETOPT_H */
diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h index 645cc1a..ea3936c 100644 --- a/Utilities/cmcurl/lib/setup-vms.h +++ b/Utilities/cmcurl/lib/setup-vms.h
@@ -374,8 +374,8 @@ #ifdef HAVE_NETDB_H #include <netdb.h> #ifndef AI_NUMERICHOST -#ifdef ENABLE_IPV6 -#undef ENABLE_IPV6 +#ifdef USE_IPV6 +#undef USE_IPV6 #endif #endif #endif
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 1d1867c..cab1e75 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c
@@ -259,7 +259,7 @@ * SMB handler interface */ const struct Curl_handler Curl_handler_smb = { - "SMB", /* scheme */ + "smb", /* scheme */ smb_setup_connection, /* setup_connection */ smb_do, /* do_it */ ZERO_NULL, /* done */ @@ -273,6 +273,7 @@ ZERO_NULL, /* perform_getsock */ smb_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SMB, /* defport */ @@ -286,7 +287,7 @@ * SMBS handler interface */ const struct Curl_handler Curl_handler_smbs = { - "SMBS", /* scheme */ + "smbs", /* scheme */ smb_setup_connection, /* setup_connection */ smb_do, /* do_it */ ZERO_NULL, /* done */ @@ -300,6 +301,7 @@ ZERO_NULL, /* perform_getsock */ smb_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SMBS, /* defport */ @@ -456,6 +458,9 @@ smbc->recv_buf = malloc(MAX_MESSAGE_SIZE); if(!smbc->recv_buf) return CURLE_OUT_OF_MEMORY; + smbc->send_buf = malloc(MAX_MESSAGE_SIZE); + if(!smbc->send_buf) + return CURLE_OUT_OF_MEMORY; /* Multiple requests are allowed with this connection */ connkeep(conn, "SMB default"); @@ -485,7 +490,6 @@ static CURLcode smb_recv_message(struct Curl_easy *data, void **msg) { struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; char *buf = smbc->recv_buf; ssize_t bytes_read; @@ -494,7 +498,7 @@ size_t len = MAX_MESSAGE_SIZE - smbc->got; CURLcode result; - result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read); + result = Curl_xfer_recv(data, buf + smbc->got, len, &bytes_read); if(result) return result; @@ -560,16 +564,15 @@ h->pid = smb_swap16((unsigned short) pid); } -static CURLcode smb_send(struct Curl_easy *data, ssize_t len, +static CURLcode smb_send(struct Curl_easy *data, size_t len, size_t upload_size) { struct connectdata *conn = data->conn; struct smb_conn *smbc = &conn->proto.smbc; - ssize_t bytes_written; + size_t bytes_written; CURLcode result; - result = Curl_nwrite(data, FIRSTSOCKET, data->state.ulbuf, - len, &bytes_written); + result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written); if(result) return result; @@ -587,16 +590,15 @@ { struct connectdata *conn = data->conn; struct smb_conn *smbc = &conn->proto.smbc; - ssize_t bytes_written; - ssize_t len = smbc->send_size - smbc->sent; + size_t bytes_written; + size_t len = smbc->send_size - smbc->sent; CURLcode result; if(!smbc->send_size) return CURLE_OK; - result = Curl_nwrite(data, FIRSTSOCKET, - data->state.ulbuf + smbc->sent, - len, &bytes_written); + result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len, + &bytes_written); if(result) return result; @@ -611,13 +613,13 @@ static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd, const void *msg, size_t msg_len) { - CURLcode result = Curl_get_upload_buffer(data); - if(result) - return result; - smb_format_message(data, (struct smb_header *)data->state.ulbuf, + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; + + smb_format_message(data, (struct smb_header *)smbc->send_buf, cmd, msg_len); - memcpy(data->state.ulbuf + sizeof(struct smb_header), - msg, msg_len); + DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE); + memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len); return smb_send(data, sizeof(struct smb_header) + msg_len, 0); } @@ -775,15 +777,14 @@ static CURLcode smb_send_write(struct Curl_easy *data) { + struct connectdata *conn = data->conn; + struct smb_conn *smbc = &conn->proto.smbc; struct smb_write *msg; struct smb_request *req = data->req.p.smb; curl_off_t offset = data->req.offset; curl_off_t upload_size = data->req.size - data->req.bytecount; - CURLcode result = Curl_get_upload_buffer(data); - if(result) - return result; - msg = (struct smb_write *)data->state.ulbuf; + msg = (struct smb_write *)smbc->send_buf; if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */ upload_size = MAX_PAYLOAD_SIZE - 1; @@ -812,10 +813,11 @@ /* Check if there is data in the transfer buffer */ if(!smbc->send_size && smbc->upload_size) { - size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ? - (size_t)data->set.upload_buffer_size : smbc->upload_size; - data->req.upload_fromhere = data->state.ulbuf; - result = Curl_fillreadbuffer(data, nread, &nread); + size_t nread = smbc->upload_size > (size_t)MAX_MESSAGE_SIZE ? + (size_t)MAX_MESSAGE_SIZE : smbc->upload_size; + bool eos; + + result = Curl_client_read(data, smbc->send_buf, nread, &nread, &eos); if(result && result != CURLE_AGAIN) return result; if(!nread) @@ -1133,6 +1135,7 @@ Curl_safefree(smbc->share); Curl_safefree(smbc->domain); Curl_safefree(smbc->recv_buf); + Curl_safefree(smbc->send_buf); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h index 437f4a5..9ea2a8c 100644 --- a/Utilities/cmcurl/lib/smb.h +++ b/Utilities/cmcurl/lib/smb.h
@@ -42,6 +42,7 @@ unsigned int session_key; unsigned short uid; char *recv_buf; + char *send_buf; size_t upload_size; size_t send_size; size_t sent;
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index bfe7b8f..dd231a5 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c
@@ -111,13 +111,14 @@ 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); /* * SMTP protocol handler. */ const struct Curl_handler Curl_handler_smtp = { - "SMTP", /* scheme */ + "smtp", /* scheme */ smtp_setup_connection, /* setup_connection */ smtp_do, /* do_it */ smtp_done, /* done */ @@ -131,6 +132,7 @@ ZERO_NULL, /* perform_getsock */ smtp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SMTP, /* defport */ @@ -146,7 +148,7 @@ */ const struct Curl_handler Curl_handler_smtps = { - "SMTPS", /* scheme */ + "smtps", /* scheme */ smtp_setup_connection, /* setup_connection */ smtp_do, /* do_it */ smtp_done, /* done */ @@ -160,6 +162,7 @@ ZERO_NULL, /* perform_getsock */ smtp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SMTPS, /* defport */ @@ -618,7 +621,7 @@ result = smtp_parse_address(data->set.str[STRING_MAIL_FROM], &address, &host); if(result) - return result; + goto out; /* 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 */ @@ -642,8 +645,10 @@ /* Null reverse-path, RFC-5321, sect. 3.6.3 */ from = strdup("<>"); - if(!from) - return CURLE_OUT_OF_MEMORY; + if(!from) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } /* Calculate the optional AUTH parameter */ if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { @@ -655,10 +660,8 @@ converting the host name to an IDN A-label if necessary */ result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH], &address, &host); - if(result) { - free(from); - return result; - } + if(result) + goto out; /* 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 */ @@ -676,7 +679,6 @@ /* An invalid mailbox was provided but we'll simply let the server worry about it */ auth = aprintf("<%s>", address); - free(address); } else @@ -684,12 +686,12 @@ auth = strdup("<>"); if(!auth) { - free(from); - - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } } +#ifndef CURL_DISABLE_MIME /* Prepare the mime data if some. */ if(data->set.mimepost.kind != MIMEKIND_NONE) { /* Use the whole structure as data. */ @@ -705,22 +707,18 @@ result = Curl_mime_add_header(&data->set.mimepost.curlheaders, "Mime-Version: 1.0"); - /* Make sure we will read the entire mime structure. */ if(!result) - result = Curl_mime_rewind(&data->set.mimepost); - - if(result) { - free(from); - free(auth); - - return result; - } - - data->state.infilesize = Curl_mime_size(&data->set.mimepost); - - /* Read from mime structure. */ - data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) &data->set.mimepost; + result = Curl_creader_set_mime(data, &data->set.mimepost); + if(result) + goto out; + data->state.infilesize = Curl_creader_total_length(data); + } + else +#endif + { + result = Curl_creader_set_fread(data, data->state.infilesize); + if(result) + goto out; } /* Calculate the optional SIZE parameter */ @@ -728,10 +726,8 @@ size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); if(!size) { - free(from); - free(auth); - - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } } @@ -752,6 +748,11 @@ } } + /* Add the client reader doing STMP EOB escaping */ + result = cr_eob_add(data); + if(result) + goto out; + /* Send the MAIL command */ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "MAIL FROM:%s%s%s%s%s%s", @@ -763,6 +764,7 @@ utf8 ? " SMTPUTF8" /* Internationalised mailbox */ : ""); /* included in our envelope */ +out: free(from); free(auth); free(size); @@ -1162,7 +1164,7 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); /* SMTP upload */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); /* End of DO phase */ smtp_state(data, SMTP_STOP); @@ -1194,7 +1196,6 @@ struct connectdata *conn) { CURLcode result = CURLE_OK; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; int smtpcode; struct smtp_conn *smtpc = &conn->proto.smtpc; struct pingpong *pp = &smtpc->pp; @@ -1210,7 +1211,7 @@ do { /* Read the response from the server */ - result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread); + result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &smtpcode, &nread); if(result) return result; @@ -1392,10 +1393,6 @@ CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct SMTP *smtp = data->req.p.smtp; - struct pingpong *pp = &conn->proto.smtpc.pp; - char *eob; - ssize_t len; - ssize_t bytes_written; (void)premature; @@ -1410,47 +1407,7 @@ result = status; /* use the already set error code */ } else if(!data->set.connect_only && data->set.mail_rcpt && - (data->state.upload || data->set.mimepost.kind)) { - /* Calculate the EOB taking into account any terminating CRLF from the - previous line of the email or the CRLF of the DATA command when there - is "no mail data". RFC-5321, sect. 4.1.1.4. - - Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to - fail when using a different pointer following a previous write, that - returned CURLE_AGAIN, we duplicate the EOB now rather than when the - bytes written doesn't equal len. */ - if(smtp->trailing_crlf || !data->state.infilesize) { - eob = strdup(&SMTP_EOB[2]); - len = SMTP_EOB_LEN - 2; - } - else { - eob = strdup(SMTP_EOB); - len = SMTP_EOB_LEN; - } - - if(!eob) - return CURLE_OUT_OF_MEMORY; - - /* Send the end of block data */ - result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written); - if(result) { - free(eob); - return result; - } - - if(bytes_written != len) { - /* The whole chunk was not sent so keep it around and adjust the - pingpong structure accordingly */ - pp->sendthis = eob; - pp->sendsize = len; - pp->sendleft = len - bytes_written; - } - else { - /* Successfully sent so adjust the response timeout relative to now */ - pp->response = Curl_now(); - - free(eob); - } + (data->state.upload || IS_MIME_POST(data))) { smtp_state(data, SMTP_POSTDATA); @@ -1502,7 +1459,7 @@ smtp->eob = 2; /* Start the first command in the DO phase */ - if((data->state.upload || data->set.mimepost.kind) && data->set.mail_rcpt) + if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt) /* MAIL transfer */ result = smtp_perform_mail(data); else @@ -1593,7 +1550,7 @@ if(smtp->transfer != PPTRANSFER_BODY) /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); return CURLE_OK; } @@ -1818,108 +1775,173 @@ return result; } -CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, - const ssize_t nread, - const ssize_t offset) +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(eos); /* we have returned an EOS */ +}; + +static CURLcode cr_eob_init(struct Curl_easy *data, + struct Curl_creader *reader) { - /* When sending a SMTP payload we must detect CRLF. sequences making sure - they are sent as CRLF.. instead, as a . on the beginning of a line will - be deleted by the server when not part of an EOB terminator and a - genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of - data by the server - */ - ssize_t i; - ssize_t si; - struct SMTP *smtp = data->req.p.smtp; - char *scratch = data->state.scratch; - char *newscratch = NULL; - char *oldscratch = NULL; - size_t eob_sent; + 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; +} - /* Do we need to allocate a scratch buffer? */ - if(!scratch || data->set.crlf) { - oldscratch = scratch; +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); +} - scratch = newscratch = malloc(2 * data->set.upload_buffer_size); - if(!newscratch) { - failf(data, "Failed to alloc scratch buffer"); +/* this is the 5-bytes End-Of-Body marker for SMTP */ +#define SMTP_EOB "\r\n.\r\n" +#define SMTP_EOB_FIND_LEN 3 - return CURLE_OUT_OF_MEMORY; +/* 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); + 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; + } + } + + if(ctx->read_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; + 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 escpe the last '.' */ + eob = "." SMTP_EOB; + break; + default: + break; + } + result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); + if(result) + return result; } } - DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread); - /* Have we already sent part of the EOB? */ - eob_sent = smtp->eob; - - /* This loop can be improved by some kind of Boyer-Moore style of - approach but that is saved for later... */ - if(offset) - memcpy(scratch, data->req.upload_fromhere, offset); - for(i = offset, si = offset; i < nread; i++) { - if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { - smtp->eob++; - - /* Is the EOB potentially the terminating CRLF? */ - if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) - smtp->trailing_crlf = TRUE; - else - smtp->trailing_crlf = FALSE; - } - else if(smtp->eob) { - /* A previous substring matched so output that first */ - memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); - si += smtp->eob - eob_sent; - - /* Then compare the first byte */ - if(SMTP_EOB[0] == data->req.upload_fromhere[i]) - smtp->eob = 1; - else - smtp->eob = 0; - - eob_sent = 0; - - /* Reset the trailing CRLF flag as there was more data */ - smtp->trailing_crlf = FALSE; - } - - /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ - if(SMTP_EOB_FIND_LEN == smtp->eob) { - /* Copy the replacement data to the target buffer */ - memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent], - SMTP_EOB_REPL_LEN - eob_sent); - si += SMTP_EOB_REPL_LEN - eob_sent; - smtp->eob = 0; - eob_sent = 0; - } - else if(!smtp->eob) - scratch[si++] = data->req.upload_fromhere[i]; - } - - if(smtp->eob - eob_sent) { - /* A substring matched before processing ended so output that now */ - memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); - si += smtp->eob - eob_sent; - } - - /* Only use the new buffer if we replaced something */ - if(si != nread) { - /* Upload from the new (replaced) buffer instead */ - data->req.upload_fromhere = scratch; - - /* Save the buffer so it can be freed later */ - data->state.scratch = scratch; - - /* Free the old scratch buffer */ - free(oldscratch); - - /* Set the new amount too */ - data->req.upload_present = si; + *peos = FALSE; + if(!Curl_bufq_is_empty(&ctx->buf)) { + result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); } else - free(newscratch); + *pnread = 0; - return CURLE_OK; + if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { + /* no more data, read all, done. */ + 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_rewind, + Curl_creader_def_unpause, + 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 7a04c21..7c2af68 100644 --- a/Utilities/cmcurl/lib/smtp.h +++ b/Utilities/cmcurl/lib/smtp.h
@@ -84,17 +84,4 @@ extern const struct Curl_handler Curl_handler_smtp; extern const struct Curl_handler Curl_handler_smtps; -/* this is the 5-bytes End-Of-Body marker for SMTP */ -#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a" -#define SMTP_EOB_LEN 5 -#define SMTP_EOB_FIND_LEN 3 - -/* if found in data, replace it with this string instead */ -#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e" -#define SMTP_EOB_REPL_LEN 4 - -CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, - const ssize_t nread, - const ssize_t offset); - #endif /* HEADER_CURL_SMTP_H */
diff --git a/Utilities/cmcurl/lib/sockaddr.h b/Utilities/cmcurl/lib/sockaddr.h index 5a6bb20..2e2d375 100644 --- a/Utilities/cmcurl/lib/sockaddr.h +++ b/Utilities/cmcurl/lib/sockaddr.h
@@ -30,7 +30,7 @@ union { struct sockaddr sa; struct sockaddr_in sa_in; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct sockaddr_in6 sa_in6; #endif #ifdef HAVE_STRUCT_SOCKADDR_STORAGE
diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c index d01b255..d7e3afd 100644 --- a/Utilities/cmcurl/lib/socketpair.c +++ b/Utilities/cmcurl/lib/socketpair.c
@@ -27,6 +27,27 @@ #include "urldata.h" #include "rand.h" +#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) +#include <fcntl.h> + +int Curl_pipe(curl_socket_t socks[2]) +{ + if(pipe(socks)) + return -1; + + if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || + fcntl(socks[1], F_SETFD, FD_CLOEXEC) ) { + close(socks[0]); + close(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } + + return 0; +} +#endif + + #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) #ifdef _WIN32 /*
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index bd499ab..ddd4437 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h
@@ -31,17 +31,41 @@ #define wakeup_write write #define wakeup_read read #define wakeup_close close -#define wakeup_create pipe +#define wakeup_create(p) Curl_pipe(p) + +#ifdef HAVE_FCNTL +#include <curl/curl.h> +int Curl_pipe(curl_socket_t socks[2]); +#else +#define Curl_pipe(p) pipe(p) +#endif #else /* HAVE_PIPE */ #define wakeup_write swrite #define wakeup_read sread #define wakeup_close sclose -#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p) + +#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 wakeup_create(p)\ +Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p) #endif /* HAVE_PIPE */ + #ifndef HAVE_SOCKETPAIR #include <curl/curl.h>
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index ecd2f7e..4ade4eb 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c
@@ -341,7 +341,7 @@ case CONNECT_RESOLVING: /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port); + dns = Curl_fetch_addr(data, sx->hostname, conn->primary.remote_port); if(dns) { #ifdef CURLRES_ASYNCH @@ -838,7 +838,7 @@ struct Curl_addrinfo *hp = NULL; if(dns) hp = dns->addr; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? AF_INET : AF_INET6; @@ -872,7 +872,7 @@ infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest, sx->remote_port); } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else if(hp->ai_family == AF_INET6) { int i; struct sockaddr_in6 *saddr_in6; @@ -909,7 +909,7 @@ IPv6 == 4, IPv4 == 1 */ unsigned char ip4[4]; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(conn->bits.ipv6_ip) { char ip6[16]; if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6)) @@ -1175,7 +1175,7 @@ result = connect_SOCKS(cf, sx, data); if(!result && sx->state == CONNECT_DONE) { cf->connected = TRUE; - Curl_verboseconnect(data, conn); + Curl_verboseconnect(data, conn, cf->sockindex); socks_proxy_cf_free(cf); } @@ -1244,7 +1244,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { "SOCKS-PROXYY", - CF_TYPE_IP_CONNECT, + CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 0, socks_proxy_cf_destroy, socks_proxy_cf_connect,
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index 2437150..c0b42b8 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -475,7 +475,7 @@ gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { - failf(data, "Failed to receive GSS-API encryptrion type."); + failf(data, "Failed to receive GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_recv_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT;
diff --git a/Utilities/cmcurl/lib/strcase.c b/Utilities/cmcurl/lib/strcase.c index 7c0b4ef..14d76f7 100644 --- a/Utilities/cmcurl/lib/strcase.c +++ b/Utilities/cmcurl/lib/strcase.c
@@ -71,7 +71,7 @@ altered by the current locale. */ char Curl_raw_toupper(char in) { - return touppermap[(unsigned char) in]; + return (char)touppermap[(unsigned char) in]; } @@ -79,7 +79,7 @@ altered by the current locale. */ char Curl_raw_tolower(char in) { - return tolowermap[(unsigned char) in]; + return (char)tolowermap[(unsigned char) in]; } /*
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index a900e78..f142cf1 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c
@@ -322,6 +322,9 @@ case CURLE_TOO_LARGE: return "A value or data field grew larger than allowed"; + case CURLE_ECH_REQUIRED: + return "ECH attempted but failed"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE20: case CURLE_OBSOLETE24:
diff --git a/Utilities/cmcurl/lib/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c index 077b257..580fd23 100644 --- a/Utilities/cmcurl/lib/strtoofft.c +++ b/Utilities/cmcurl/lib/strtoofft.c
@@ -79,11 +79,10 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base) { char *end; - int is_negative = 0; - int overflow; + bool is_negative = FALSE; + bool overflow = FALSE; int i; curl_off_t value = 0; - curl_off_t newval; /* Skip leading whitespace. */ end = (char *)nptr; @@ -93,7 +92,7 @@ /* Handle the sign, if any. */ if(end[0] == '-') { - is_negative = 1; + is_negative = TRUE; end++; } else if(end[0] == '+') { @@ -129,19 +128,15 @@ } /* Loop handling digits. */ - value = 0; - overflow = 0; for(i = get_char(end[0], base); i != -1; end++, i = get_char(end[0], base)) { - newval = base * value + i; - if(newval < value) { - /* We've overflowed. */ - overflow = 1; + + if(value > (CURL_OFF_T_MAX - i) / base) { + overflow = TRUE; break; } - else - value = newval; + value = base * value + i; } if(!overflow) { @@ -217,7 +212,7 @@ CURLofft curlx_strtoofft(const char *str, char **endp, int base, curl_off_t *num) { - char *end; + char *end = NULL; curl_off_t number; errno = 0; *num = 0; /* clear by default */
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index 34dc5e8..227a166 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c
@@ -173,7 +173,7 @@ */ const struct Curl_handler Curl_handler_telnet = { - "TELNET", /* scheme */ + "telnet", /* scheme */ ZERO_NULL, /* setup_connection */ telnet_do, /* do_it */ telnet_done, /* done */ @@ -187,6 +187,7 @@ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_TELNET, /* defport */ @@ -1231,20 +1232,24 @@ static CURLcode send_telnet_data(struct Curl_easy *data, char *buffer, ssize_t nread) { - ssize_t i, outlen; + size_t i, outlen; unsigned char *outbuf; CURLcode result = CURLE_OK; - ssize_t bytes_written, total_written = 0; + size_t bytes_written; + size_t total_written = 0; struct connectdata *conn = data->conn; struct TELNET *tn = data->req.p.telnet; 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 */ Curl_dyn_reset(&tn->out); - for(i = 0; i < nread && !result; i++) { + for(i = 0; i < (size_t)nread && !result; i++) { result = Curl_dyn_addn(&tn->out, &buffer[i], 1); if(!result && ((unsigned char)buffer[i] == CURL_IAC)) /* IAC is FF in hex */ @@ -1255,7 +1260,7 @@ outbuf = Curl_dyn_uptr(&tn->out); } else { - outlen = nread; + outlen = (size_t)nread; outbuf = (unsigned char *)buffer; } while(!result && total_written < outlen) { @@ -1270,8 +1275,8 @@ break; default: /* write! */ bytes_written = 0; - result = Curl_nwrite(data, FIRSTSOCKET, outbuf + total_written, - outlen - total_written, &bytes_written); + result = Curl_xfer_send(data, outbuf + total_written, + outlen - total_written, &bytes_written); total_written += bytes_written; break; } @@ -1464,7 +1469,7 @@ } if(events.lNetworkEvents & FD_READ) { /* read data from network */ - result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread); + result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); /* read would've blocked. Loop again */ if(result == CURLE_AGAIN) break; @@ -1530,6 +1535,11 @@ pfd[1].events = POLLIN; poll_cnt = 2; interval_ms = 1 * 1000; + if(pfd[1].fd < 0) { + failf(data, "cannot read input"); + result = CURLE_RECV_ERROR; + keepon = FALSE; + } } while(keepon) { @@ -1545,7 +1555,7 @@ default: /* read! */ if(pfd[0].revents & POLLIN) { /* read data from network */ - result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread); + result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); /* read would've blocked. Loop again */ if(result == CURLE_AGAIN) break; @@ -1635,7 +1645,7 @@ } #endif /* mark this as "no further transfer wanted" */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); return result; }
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index 4288110..310a0fa 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c
@@ -168,7 +168,7 @@ */ const struct Curl_handler Curl_handler_tftp = { - "TFTP", /* scheme */ + "tftp", /* scheme */ tftp_setup_connection, /* setup_connection */ tftp_do, /* do_it */ tftp_done, /* done */ @@ -182,6 +182,7 @@ ZERO_NULL, /* perform_getsock */ tftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_TFTP, /* defport */ @@ -452,8 +453,6 @@ if(data->state.upload) { /* If we are uploading, send an WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); - state->data->req.upload_fromhere = - (char *)state->spacket.data + 4; if(data->state.infilesize != -1) Curl_pgrsSetUploadSize(data, data->state.infilesize); } @@ -708,6 +707,8 @@ struct SingleRequest *k = &data->req; size_t cb; /* Bytes currently read */ char buffer[STRERROR_LEN]; + char *bufptr; + bool eos; switch(event) { @@ -771,13 +772,14 @@ * data block. * */ state->sbytes = 0; - state->data->req.upload_fromhere = (char *)state->spacket.data + 4; + bufptr = (char *)state->spacket.data + 4; do { - result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb); + result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, + &cb, &eos); if(result) return result; state->sbytes += (int)cb; - state->data->req.upload_fromhere += cb; + bufptr += cb; } while(state->sbytes < state->blksize && cb); sbytes = sendto(state->sockfd, (void *) state->spacket.data, @@ -1202,7 +1204,7 @@ state->state = TFTP_STATE_FIN; return 0; } - time(¤t); + current = time(NULL); if(current > state->rx_time + state->retry_time) { if(event) *event = TFTP_EVENT_TIMEOUT; @@ -1240,7 +1242,7 @@ *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; if(*done) /* Tell curl we're done */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); } else { /* no timeouts to handle, check our socket */ @@ -1263,7 +1265,7 @@ *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; if(*done) /* Tell curl we're done */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); } /* if rc == 0, then select() timed out */ }
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 3ae4b61..744227e 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c
@@ -63,6 +63,7 @@ #include "content_encoding.h" #include "hostip.h" #include "cfilters.h" +#include "cw-out.h" #include "transfer.h" #include "sendf.h" #include "speedcheck.h" @@ -114,260 +115,6 @@ } #endif -CURLcode Curl_get_upload_buffer(struct Curl_easy *data) -{ - if(!data->state.ulbuf) { - data->state.ulbuf = malloc(data->set.upload_buffer_size); - if(!data->state.ulbuf) - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} - -#ifndef CURL_DISABLE_HTTP -/* - * This function will be called to loop through the trailers buffer - * until no more data is available for sending. - */ -static size_t trailers_read(char *buffer, size_t size, size_t nitems, - void *raw) -{ - struct Curl_easy *data = (struct Curl_easy *)raw; - struct dynbuf *trailers_buf = &data->state.trailers_buf; - size_t bytes_left = Curl_dyn_len(trailers_buf) - - data->state.trailers_bytes_sent; - size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left; - if(to_copy) { - memcpy(buffer, - Curl_dyn_ptr(trailers_buf) + data->state.trailers_bytes_sent, - to_copy); - data->state.trailers_bytes_sent += to_copy; - } - return to_copy; -} - -static size_t trailers_left(void *raw) -{ - struct Curl_easy *data = (struct Curl_easy *)raw; - struct dynbuf *trailers_buf = &data->state.trailers_buf; - return Curl_dyn_len(trailers_buf) - data->state.trailers_bytes_sent; -} -#endif - -/* - * This function will call the read callback to fill our buffer with data - * to upload. - */ -CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, - size_t *nreadp) -{ - size_t buffersize = bytes; - size_t nread; - curl_read_callback readfunc = NULL; - void *extra_data = NULL; - int eof_index = 0; - -#ifndef CURL_DISABLE_HTTP - if(data->state.trailers_state == TRAILERS_INITIALIZED) { - struct curl_slist *trailers = NULL; - CURLcode result; - int trailers_ret_code; - - /* at this point we already verified that the callback exists - so we compile and store the trailers buffer, then proceed */ - infof(data, - "Moving trailers state machine from initialized to sending."); - data->state.trailers_state = TRAILERS_SENDING; - Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS); - - data->state.trailers_bytes_sent = 0; - Curl_set_in_callback(data, true); - trailers_ret_code = data->set.trailer_callback(&trailers, - data->set.trailer_data); - Curl_set_in_callback(data, false); - if(trailers_ret_code == CURL_TRAILERFUNC_OK) { - result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf, - data); - } - else { - failf(data, "operation aborted by trailing headers callback"); - *nreadp = 0; - result = CURLE_ABORTED_BY_CALLBACK; - } - if(result) { - Curl_dyn_free(&data->state.trailers_buf); - curl_slist_free_all(trailers); - return result; - } - infof(data, "Successfully compiled trailers."); - curl_slist_free_all(trailers); - } -#endif - -#ifndef CURL_DISABLE_HTTP - /* if we are transmitting trailing data, we don't need to write - a chunk size so we skip this */ - if(data->req.upload_chunky && - data->state.trailers_state == TRAILERS_NONE) { - /* if chunked Transfer-Encoding */ - buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */ - data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */ - } - - if(data->state.trailers_state == TRAILERS_SENDING) { - /* if we're here then that means that we already sent the last empty chunk - but we didn't send a final CR LF, so we sent 0 CR LF. We then start - pulling trailing data until we have no more at which point we - simply return to the previous point in the state machine as if - nothing happened. - */ - readfunc = trailers_read; - extra_data = (void *)data; - eof_index = 1; - } - else -#endif - { - readfunc = data->state.fread_func; - extra_data = data->state.in; - } - - if(!data->req.fread_eof[eof_index]) { - Curl_set_in_callback(data, true); - nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data); - Curl_set_in_callback(data, false); - /* make sure the callback is not called again after EOF */ - data->req.fread_eof[eof_index] = !nread; - } - else - nread = 0; - - if(nread == CURL_READFUNC_ABORT) { - failf(data, "operation aborted by callback"); - *nreadp = 0; - return CURLE_ABORTED_BY_CALLBACK; - } - if(nread == CURL_READFUNC_PAUSE) { - struct SingleRequest *k = &data->req; - - if(data->conn->handler->flags & PROTOPT_NONETWORK) { - /* protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it can't pause since the transfer - isn't done using the "normal" procedure. */ - failf(data, "Read callback asked for PAUSE when not supported"); - return CURLE_READ_ERROR; - } - - /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ - k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ - if(data->req.upload_chunky) { - /* Back out the preallocation done above */ - data->req.upload_fromhere -= (8 + 2); - } - *nreadp = 0; - - return CURLE_OK; /* nothing was read */ - } - else if(nread > buffersize) { - /* the read function returned a too large value */ - *nreadp = 0; - failf(data, "read function returned funny value"); - return CURLE_READ_ERROR; - } - -#ifndef CURL_DISABLE_HTTP - if(!data->req.forbidchunk && data->req.upload_chunky) { - /* if chunked Transfer-Encoding - * build chunk: - * - * <HEX SIZE> CRLF - * <DATA> CRLF - */ - /* On non-ASCII platforms the <DATA> may or may not be - translated based on state.prefer_ascii while the protocol - portion must always be translated to the network encoding. - To further complicate matters, line end conversion might be - done later on, so we need to prevent CRLFs from becoming - CRCRLFs if that's the case. To do this we use bare LFs - here, knowing they'll become CRLFs later on. - */ - - bool added_crlf = FALSE; - int hexlen = 0; - const char *endofline_native; - const char *endofline_network; - - if( -#ifdef CURL_DO_LINEEND_CONV - (data->state.prefer_ascii) || -#endif - (data->set.crlf)) { - /* \n will become \r\n later on */ - endofline_native = "\n"; - endofline_network = "\x0a"; - } - else { - endofline_native = "\r\n"; - endofline_network = "\x0d\x0a"; - } - - /* if we're not handling trailing data, proceed as usual */ - if(data->state.trailers_state != TRAILERS_SENDING) { - char hexbuffer[11] = ""; - hexlen = msnprintf(hexbuffer, sizeof(hexbuffer), - "%zx%s", nread, endofline_native); - - /* move buffer pointer */ - data->req.upload_fromhere -= hexlen; - nread += hexlen; - - /* copy the prefix to the buffer, leaving out the NUL */ - memcpy(data->req.upload_fromhere, hexbuffer, hexlen); - - /* always append ASCII CRLF to the data unless - we have a valid trailer callback */ - if((nread-hexlen) == 0 && - data->set.trailer_callback != NULL && - data->state.trailers_state == TRAILERS_NONE) { - data->state.trailers_state = TRAILERS_INITIALIZED; - } - else { - memcpy(data->req.upload_fromhere + nread, - endofline_network, - strlen(endofline_network)); - added_crlf = TRUE; - } - } - - if(data->state.trailers_state == TRAILERS_SENDING && - !trailers_left(data)) { - Curl_dyn_free(&data->state.trailers_buf); - data->state.trailers_state = TRAILERS_DONE; - data->set.trailer_data = NULL; - data->set.trailer_callback = NULL; - /* mark the transfer as done */ - data->req.upload_done = TRUE; - infof(data, "Signaling end of chunked upload after trailers."); - } - else - if((nread - hexlen) == 0 && - data->state.trailers_state != TRAILERS_INITIALIZED) { - /* mark this as done once this chunk is transferred */ - data->req.upload_done = TRUE; - infof(data, - "Signaling end of chunked upload via terminating chunk."); - } - - if(added_crlf) - nread += strlen(endofline_network); /* for the added end of line */ - } -#endif - - *nreadp = nread; - - return CURLE_OK; -} - static int data_pending(struct Curl_easy *data) { struct connectdata *conn = data->conn; @@ -447,11 +194,10 @@ return 0; } - *err = Curl_read(data, data->conn->sockfd, buf, blen, &nread); + *err = Curl_xfer_recv(data, buf, blen, &nread); if(*err) return -1; DEBUGASSERT(nread >= 0); - *err = CURLE_OK; return nread; } @@ -462,18 +208,19 @@ */ static CURLcode readwrite_data(struct Curl_easy *data, struct SingleRequest *k, - int *didwhat, bool *done) + int *didwhat) { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - char *buf; - size_t blen; + char *buf, *xfer_buf; + size_t blen, xfer_blen; int maxloops = 10; curl_off_t total_received = 0; bool is_multiplex = FALSE; - DEBUGASSERT(data->state.buffer); - *done = FALSE; + result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); + if(result) + goto out; /* This is where we loop until we have read everything there is to read or we get a CURLE_AGAIN */ @@ -489,16 +236,17 @@ is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); } - buf = data->state.buffer; - bytestoread = data->set.buffer_size; + buf = xfer_buf; + bytestoread = xfer_blen; - /* Observe any imposed speed limit */ if(bytestoread && data->set.max_recv_speed) { - curl_off_t net_limit = data->set.max_recv_speed - total_received; - if(net_limit <= 0) + /* In case of speed limit on receiving: if this loop already got + * data, break out. If not, limit the amount of bytes to receive. + * The overall, timed, speed limiting is done in multi.c */ + if(total_received) break; - if((size_t)net_limit < bytestoread) - bytestoread = (size_t)net_limit; + if((size_t)data->set.max_recv_speed < bytestoread) + bytestoread = (size_t)data->set.max_recv_speed; } nread = Curl_xfer_recv_resp(data, buf, bytestoread, @@ -523,15 +271,14 @@ DEBUGF(infof(data, "nread == 0, stream closed, bailing")); else DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); - if(k->eos_written) { /* already did write this to client, leave */ - k->keepon = 0; /* stop sending as well */ + k->keepon &= ~(KEEP_RECV|KEEP_SEND); /* stop sending as well */ + if(k->eos_written) /* already did write this to client, leave */ break; - } } total_received += blen; - result = Curl_xfer_write_resp(data, buf, blen, is_eos, done); - if(result || *done) + result = Curl_xfer_write_resp(data, buf, blen, is_eos); + if(result || data->req.done) goto out; /* if we are done, we stop receiving. On multiplexed connections, @@ -564,22 +311,12 @@ } out: + Curl_multi_xfer_buf_release(data, xfer_buf); if(result) DEBUGF(infof(data, "readwrite_data() -> %d", result)); return result; } -CURLcode Curl_done_sending(struct Curl_easy *data, - struct SingleRequest *k) -{ - k->keepon &= ~KEEP_SEND; /* we're done writing */ - - /* These functions should be moved into the handler struct! */ - Curl_conn_ev_data_done_send(data); - - return CURLE_OK; -} - #if defined(_WIN32) && defined(USE_WINSOCK) #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B @@ -602,245 +339,42 @@ #endif #define curl_upload_refill_watermark(data) \ - ((ssize_t)((data)->set.upload_buffer_size >> 5)) + ((size_t)((data)->set.upload_buffer_size >> 5)) /* * Send data to upload to the server, when the socket is writable. */ -static CURLcode readwrite_upload(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat) +static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat) { - ssize_t i, si; - ssize_t bytes_written; - CURLcode result; - ssize_t nread; /* number of bytes read */ - bool sending_http_headers = FALSE; - struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; - *didwhat |= KEEP_SEND; + if((data->req.keepon & KEEP_SEND_PAUSE)) + return CURLE_OK; - do { - curl_off_t nbody; - ssize_t offset = 0; + /* 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. */ + DEBUGASSERT(!Curl_req_done_sending(data)); - if(0 != k->upload_present && - k->upload_present < curl_upload_refill_watermark(data) && - !k->upload_chunky &&/*(variable sized chunked header; append not safe)*/ - !k->upload_done && /*!(k->upload_done once k->upload_present sent)*/ - !(k->writebytecount + k->upload_present - k->pendingheader == - data->state.infilesize)) { - offset = k->upload_present; - } - - /* only read more data if there's no upload data already - present in the upload buffer, or if appending to upload buffer */ - if(0 == k->upload_present || offset) { - result = Curl_get_upload_buffer(data); - if(result) - return result; - if(offset && k->upload_fromhere != data->state.ulbuf) - memmove(data->state.ulbuf, k->upload_fromhere, offset); - /* init the "upload from here" pointer */ - k->upload_fromhere = data->state.ulbuf; - - if(!k->upload_done) { - /* HTTP pollution, this should be written nicer to become more - protocol agnostic. */ - size_t fillcount; - struct HTTP *http = k->p.http; - - if((k->exp100 == EXP100_SENDING_REQUEST) && - (http->sending == HTTPSEND_BODY)) { - /* If this call is to send body data, we must take some action: - We have sent off the full HTTP 1.1 request, and we shall now - go into the Expect: 100 state and await such a header */ - k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */ - k->keepon &= ~KEEP_SEND; /* disable writing */ - k->start100 = Curl_now(); /* timeout count starts now */ - *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */ - /* set a timeout for the multi interface */ - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - break; - } - - if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) { - if(http->sending == HTTPSEND_REQUEST) - /* We're sending the HTTP request headers, not the data. - Remember that so we don't change the line endings. */ - sending_http_headers = TRUE; - else - sending_http_headers = FALSE; - } - - k->upload_fromhere += offset; - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size-offset, - &fillcount); - k->upload_fromhere -= offset; - if(result) - return result; - - nread = offset + fillcount; - } - else - nread = 0; /* we're done uploading/reading */ - - if(!nread && (k->keepon & KEEP_SEND_PAUSE)) { - /* this is a paused transfer */ - break; - } - if(nread <= 0) { - result = Curl_done_sending(data, k); - if(result) - return result; - break; - } - - /* store number of bytes available for upload */ - k->upload_present = nread; - - /* convert LF to CRLF if so asked */ - if((!sending_http_headers) && ( -#ifdef CURL_DO_LINEEND_CONV - /* always convert if we're FTPing in ASCII mode */ - (data->state.prefer_ascii) || -#endif - (data->set.crlf))) { - /* Do we need to allocate a scratch buffer? */ - if(!data->state.scratch) { - data->state.scratch = malloc(2 * data->set.upload_buffer_size); - if(!data->state.scratch) { - failf(data, "Failed to alloc scratch buffer"); - - return CURLE_OUT_OF_MEMORY; - } - } - - /* - * ASCII/EBCDIC Note: This is presumably a text (not binary) - * transfer so the data should already be in ASCII. - * That means the hex values for ASCII CR (0x0d) & LF (0x0a) - * must be used instead of the escape sequences \r & \n. - */ - if(offset) - memcpy(data->state.scratch, k->upload_fromhere, offset); - for(i = offset, si = offset; i < nread; i++, si++) { - if(k->upload_fromhere[i] == 0x0a) { - data->state.scratch[si++] = 0x0d; - data->state.scratch[si] = 0x0a; - if(!data->set.crlf) { - /* we're here only because FTP is in ASCII mode... - bump infilesize for the LF we just added */ - if(data->state.infilesize != -1) - data->state.infilesize++; - } - } - else - data->state.scratch[si] = k->upload_fromhere[i]; - } - - if(si != nread) { - /* only perform the special operation if we really did replace - anything */ - nread = si; - - /* upload from the new (replaced) buffer instead */ - k->upload_fromhere = data->state.scratch; - - /* set the new amount too */ - k->upload_present = nread; - } - } - -#ifndef CURL_DISABLE_SMTP - if(conn->handler->protocol & PROTO_FAMILY_SMTP) { - result = Curl_smtp_escape_eob(data, nread, offset); - if(result) - return result; - } -#endif /* CURL_DISABLE_SMTP */ - } /* if 0 == k->upload_present or appended to upload buffer */ - else { - /* We have a partial buffer left from a previous "round". Use - that instead of reading more data */ - } - - /* write to socket (send away data) */ - result = Curl_write(data, - conn->writesockfd, /* socket to send to */ - k->upload_fromhere, /* buffer pointer */ - k->upload_present, /* buffer size */ - &bytes_written); /* actually sent */ + if(!Curl_req_done_sending(data)) { + *didwhat |= KEEP_SEND; + result = Curl_req_send_more(data); if(result) return result; #if defined(_WIN32) && defined(USE_WINSOCK) + /* FIXME: this looks like it would fit better into cf-socket.c + * but then I do not know enough Windows to say... */ { struct curltime n = Curl_now(); - if(Curl_timediff(n, k->last_sndbuf_update) > 1000) { - win_update_buffer_size(conn->writesockfd); - k->last_sndbuf_update = n; + if(Curl_timediff(n, data->conn->last_sndbuf_update) > 1000) { + win_update_buffer_size(data->conn->writesockfd); + data->conn->last_sndbuf_update = n; } } #endif - - if(k->pendingheader) { - /* parts of what was sent was header */ - curl_off_t n = CURLMIN(k->pendingheader, bytes_written); - /* show the data before we change the pointer upload_fromhere */ - Curl_debug(data, CURLINFO_HEADER_OUT, k->upload_fromhere, (size_t)n); - k->pendingheader -= n; - nbody = bytes_written - n; /* size of the written body part */ - } - else - nbody = bytes_written; - - if(nbody) { - /* show the data before we change the pointer upload_fromhere */ - Curl_debug(data, CURLINFO_DATA_OUT, - &k->upload_fromhere[bytes_written - nbody], - (size_t)nbody); - - k->writebytecount += nbody; - Curl_pgrsSetUploadCounter(data, k->writebytecount); - } - - if((!k->upload_chunky || k->forbidchunk) && - (k->writebytecount == data->state.infilesize)) { - /* we have sent all data we were supposed to */ - k->upload_done = TRUE; - infof(data, "We are completely uploaded and fine"); - } - - if(k->upload_present != bytes_written) { - /* we only wrote a part of the buffer (if anything), deal with it! */ - - /* store the amount of bytes left in the buffer to write */ - k->upload_present -= bytes_written; - - /* advance the pointer where to find the buffer when the next send - is to happen */ - k->upload_fromhere += bytes_written; - } - else { - /* we've uploaded that buffer now */ - result = Curl_get_upload_buffer(data); - if(result) - return result; - k->upload_fromhere = data->state.ulbuf; - k->upload_present = 0; /* no more bytes left */ - - if(k->upload_done) { - result = Curl_done_sending(data, k); - if(result) - return result; - } - } - - - } while(0); /* just to break out from! */ - - return CURLE_OK; + } + return result; } static int select_bits_paused(struct Curl_easy *data, int select_bits) @@ -865,8 +399,7 @@ * Curl_readwrite() is the low-level function to be called when data is to * be read and written to/from the connection. */ -CURLcode Curl_readwrite(struct Curl_easy *data, - bool *done) +CURLcode Curl_readwrite(struct Curl_easy *data) { struct connectdata *conn = data->conn; struct SingleRequest *k = &data->req; @@ -875,6 +408,14 @@ int didwhat = 0; int select_bits; + /* Check if client writes had been paused and can resume now. */ + if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) { + Curl_conn_ev_data_pause(data, FALSE); + result = Curl_cwriter_unpause(data); + if(result) + goto out; + } + if(data->state.select_bits) { if(select_bits_paused(data, data->state.select_bits)) { /* leave the bits unchanged, so they'll tell us what to do when @@ -912,8 +453,8 @@ #ifdef USE_HYPER if(conn->datastream) { - result = conn->datastream(data, conn, &didwhat, done, select_bits); - if(result || *done) + result = conn->datastream(data, conn, &didwhat, select_bits); + if(result || data->req.done) goto out; } else { @@ -922,16 +463,17 @@ the stream was rewound (in which case we have data in a buffer) */ if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) { - result = readwrite_data(data, k, &didwhat, done); - if(result || *done) + result = readwrite_data(data, k, &didwhat); + if(result || data->req.done) goto out; } /* If we still have writing to do, we check if we have a writable socket. */ - if((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) { + if(((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) || + (k->keepon & KEEP_SEND_TIMED)) { /* write */ - result = readwrite_upload(data, conn, &didwhat); + result = readwrite_upload(data, &didwhat); if(result) goto out; } @@ -941,31 +483,6 @@ now = Curl_now(); if(!didwhat) { - /* no read no write, this is a timeout? */ - if(k->exp100 == EXP100_AWAITING_CONTINUE) { - /* This should allow some time for the header to arrive, but only a - very short time as otherwise it'll be too much wasted time too - often. */ - - /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status": - - Therefore, when a client sends this header field to an origin server - (possibly via a proxy) from which it has never seen a 100 (Continue) - status, the client SHOULD NOT wait for an indefinite period before - sending the request body. - - */ - - timediff_t ms = Curl_timediff(now, k->start100); - if(ms >= data->set.expect_100_timeout) { - /* we've waited long enough, continue anyway */ - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - infof(data, "Done waiting for 100-continue"); - } - } - result = Curl_conn_ev_data_idle(data); if(result) goto out; @@ -1002,7 +519,6 @@ * The transfer has been performed. Just make some general checks before * returning. */ - if(!(data->req.no_body) && (k->size != -1) && (k->bytecount != k->size) && #ifdef CURL_DO_LINEEND_CONV @@ -1024,8 +540,10 @@ } } - /* Now update the "done" boolean we return */ - *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE; + /* If there is nothing more to send/recv, the request is done */ + if(0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) + data->req.done = TRUE; + out: if(result) DEBUGF(infof(data, "Curl_readwrite() -> %d", result)); @@ -1194,12 +712,14 @@ 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 data->req.headerbytecount = 0; Curl_headers_cleanup(data); @@ -1400,7 +920,7 @@ data->state.url = newurl; data->state.url_alloc = TRUE; - + Curl_req_soft_reset(&data->req, data); infof(data, "Issue another request to this URL: '%s'", data->state.url); /* @@ -1446,6 +966,7 @@ && !(data->set.keep_post & CURL_REDIR_POST_301)) { infof(data, "Switch from POST to GET"); data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); } break; case 302: /* Found */ @@ -1471,6 +992,7 @@ && !(data->set.keep_post & CURL_REDIR_POST_302)) { infof(data, "Switch from POST to GET"); data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); } break; @@ -1573,23 +1095,16 @@ prevent i.e HTTP transfers to return error just because nothing has been transferred! */ - - - if((conn->handler->protocol&PROTO_FAMILY_HTTP) && - data->req.writebytecount) { - data->state.rewindbeforesend = TRUE; - infof(data, "state.rewindbeforesend = TRUE"); - } + Curl_creader_set_rewind(data, TRUE); } return CURLE_OK; } /* - * Curl_setup_transfer() is called to setup some basic properties for the + * Curl_xfer_setup() is called to setup some basic properties for the * upcoming transfer. */ -void -Curl_setup_transfer( +void Curl_xfer_setup( struct Curl_easy *data, /* transfer */ int sockindex, /* socket index to read from or -1 */ curl_off_t size, /* -1 if unknown at this point */ @@ -1600,22 +1115,19 @@ { struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; - struct HTTP *http = data->req.p.http; - bool httpsending; + bool want_send = Curl_req_want_send(data); DEBUGASSERT(conn != NULL); DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); + DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1)); - httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) && - (http->sending == HTTPSEND_REQUEST)); - - if(conn->bits.multiplex || conn->httpversion >= 20 || httpsending) { + if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) { /* when multiplexing, the read/write sockets need to be the same! */ conn->sockfd = sockindex == -1 ? ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : conn->sock[sockindex]; conn->writesockfd = conn->sockfd; - if(httpsending) + if(want_send) /* special and very HTTP-specific */ writesockindex = FIRSTSOCKET; } @@ -1644,51 +1156,22 @@ if(sockindex != -1) k->keepon |= KEEP_RECV; - if(writesockindex != -1) { - /* HTTP 1.1 magic: - - Even if we require a 100-return code before uploading data, we might - need to write data before that since the REQUEST may not have been - finished sent off just yet. - - Thus, we must check if the request has been sent before we set the - state info where we wait for the 100-return code - */ - if((data->state.expect100header) && - (conn->handler->protocol&PROTO_FAMILY_HTTP) && - (http->sending == HTTPSEND_BODY)) { - /* wait with write until we either got 100-continue or a timeout */ - k->exp100 = EXP100_AWAITING_CONTINUE; - k->start100 = Curl_now(); - - /* Set a timeout for the multi interface. Add the inaccuracy margin so - that we don't fire slightly too early and get denied to run. */ - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - } - else { - if(data->state.expect100header) - /* when we've sent off the rest of the headers, we must await a - 100-continue but first finish sending the request */ - k->exp100 = EXP100_SENDING_REQUEST; - - /* enable the write bit when we're not waiting for continue */ - k->keepon |= KEEP_SEND; - } - } /* if(writesockindex != -1) */ + if(writesockindex != -1) + k->keepon |= KEEP_SEND; } /* if(k->getheader || !data->req.no_body) */ } CURLcode Curl_xfer_write_resp(struct Curl_easy *data, - char *buf, size_t blen, - bool is_eos, bool *done) + const char *buf, size_t blen, + bool is_eos) { CURLcode result = CURLE_OK; if(data->conn->handler->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, done); + result = data->conn->handler->write_resp(data, buf, blen, is_eos); } else { /* No special handling by protocol handler, write all received data @@ -1714,5 +1197,82 @@ data->req.eos_written = TRUE; data->req.download_done = TRUE; } + CURL_TRC_WRITE(data, "xfer_write_resp(len=%zu, eos=%d) -> %d", + blen, is_eos, result); return result; } + +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) { + /* 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); + } + /* No special handling by protocol handler, write as response bytes */ + return Curl_xfer_write_resp(data, hd0, hdlen, is_eos); +} + +CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature) +{ + (void)premature; + return Curl_cw_out_done(data); +} + +CURLcode Curl_xfer_send(struct Curl_easy *data, + const void *buf, size_t blen, + size_t *pnwritten) +{ + CURLcode result; + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + /* FIXME: would like to enable this, but some protocols (MQTT) do not + * setup the transfer correctly, it seems + if(data->conn->writesockfd == CURL_SOCKET_BAD) { + failf(data, "transfer not setup for sending"); + DEBUGASSERT(0); + return CURLE_SEND_ERROR; + } */ + sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) && + (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET])); + result = Curl_conn_send(data, sockindex, buf, blen, pnwritten); + if(result == CURLE_AGAIN) { + result = CURLE_OK; + *pnwritten = 0; + } + else if(!result && *pnwritten) + data->info.request_size += *pnwritten; + + return result; +} + +CURLcode Curl_xfer_recv(struct Curl_easy *data, + char *buf, size_t blen, + ssize_t *pnrcvd) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + /* FIXME: would like to enable this, but some protocols (MQTT) do not + * setup the transfer correctly, it seems + if(data->conn->sockfd == CURL_SOCKET_BAD) { + failf(data, "transfer not setup for receiving"); + DEBUGASSERT(0); + return CURLE_RECV_ERROR; + } */ + sockindex = ((data->conn->sockfd != CURL_SOCKET_BAD) && + (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET])); + if(data->set.buffer_size > 0 && (size_t)data->set.buffer_size < blen) + blen = (size_t)data->set.buffer_size; + return Curl_conn_recv(data, sockindex, buf, blen, pnrcvd); +} + +CURLcode Curl_xfer_send_close(struct Curl_easy *data) +{ + Curl_conn_ev_data_done_send(data); + return CURLE_OK; +}
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index 0507f1a..ad0f3a2 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h
@@ -45,17 +45,11 @@ CURLcode Curl_follow(struct Curl_easy *data, char *newurl, followtype type); -CURLcode Curl_readwrite(struct Curl_easy *data, bool *done); +CURLcode Curl_readwrite(struct Curl_easy *data); int Curl_single_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *socks); -CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, - size_t *nreadp); CURLcode Curl_retry_request(struct Curl_easy *data, char **url); bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc); -CURLcode Curl_get_upload_buffer(struct Curl_easy *data); - -CURLcode Curl_done_sending(struct Curl_easy *data, - struct SingleRequest *k); /** * Write the transfer raw response bytes, as received from the connection. @@ -68,15 +62,22 @@ * @param blen the amount of bytes in `buf` * @param is_eos TRUE iff the connection indicates this to be the last * bytes of the response - * @param done on returnm, TRUE iff the response is complete */ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, - char *buf, size_t blen, - bool is_eos, bool *done); + const char *buf, size_t blen, + bool is_eos); + +/** + * Write a single "header" line from a server response. + * @param hd0 the 0-terminated, single header line + * @param hdlen the length of the header line + * @param is_eos TRUE iff this is the end of the response + */ +CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data, + const char *hd0, size_t hdlen, bool is_eos); /* This sets up a forthcoming transfer */ -void -Curl_setup_transfer (struct Curl_easy *data, +void Curl_xfer_setup(struct Curl_easy *data, int sockindex, /* socket index to read from or -1 */ curl_off_t size, /* -1 if unknown at this point */ bool getheader, /* TRUE if header parsing is wanted */ @@ -85,4 +86,30 @@ disables */ ); +/** + * Multi has set transfer to DONE. Last chance to trigger + * missing response things like writing an EOS to the client. + */ +CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature); + +/** + * Send data on the socket/connection filter designated + * for transfer's outgoing data. + * Will return CURLE_OK on blocking with (*pnwritten == 0). + */ +CURLcode Curl_xfer_send(struct Curl_easy *data, + const void *buf, size_t blen, + size_t *pnwritten); + +/** + * Receive data on the socket/connection filter designated + * for transfer's incoming data. + * Will return CURLE_AGAIN on blocking with (*pnrcvd == 0). + */ +CURLcode Curl_xfer_recv(struct Curl_easy *data, + char *buf, size_t blen, + ssize_t *pnrcvd); + +CURLcode Curl_xfer_send_close(struct Curl_easy *data); + #endif /* HEADER_CURL_TRANSFER_H */
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 36395a1..2814d31 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c
@@ -261,7 +261,7 @@ free(data->state.range); /* freed here just in case DONE wasn't called */ - Curl_free_request_state(data); + Curl_req_free(&data->req, data); /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); @@ -269,10 +269,6 @@ Curl_safefree(data->state.scratch); Curl_ssl_free_certinfo(data); - /* Cleanup possible redirect junk */ - free(data->req.newurl); - data->req.newurl = NULL; - if(data->state.referer_alloc) { Curl_safefree(data->state.referer); data->state.referer_alloc = FALSE; @@ -280,14 +276,14 @@ data->state.referer = NULL; up_free(data); - Curl_safefree(data->state.buffer); Curl_dyn_free(&data->state.headerb); - Curl_safefree(data->state.ulbuf); Curl_flush_cookies(data, TRUE); +#ifndef CURL_DISABLE_ALTSVC Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(&data->asi); - Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); +#endif #ifndef CURL_DISABLE_HSTS + Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); if(!data->share || !data->share->hsts) Curl_hsts_cleanup(&data->hsts); curl_slist_free_all(data->state.hstslist); /* clean up list */ @@ -311,7 +307,9 @@ Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } +#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); @@ -319,23 +317,20 @@ Curl_safefree(data->state.aptr.rangeline); Curl_safefree(data->state.aptr.ref); Curl_safefree(data->state.aptr.host); +#ifndef CURL_DISABLE_COOKIES Curl_safefree(data->state.aptr.cookiehost); +#endif +#ifndef CURL_DISABLE_RTSP Curl_safefree(data->state.aptr.rtsp_transport); +#endif Curl_safefree(data->state.aptr.user); Curl_safefree(data->state.aptr.passwd); +#ifndef CURL_DISABLE_PROXY Curl_safefree(data->state.aptr.proxyuser); Curl_safefree(data->state.aptr.proxypasswd); - -#ifndef CURL_DISABLE_DOH - if(data->req.doh) { - Curl_dyn_free(&data->req.doh->probe[0].serverdoh); - Curl_dyn_free(&data->req.doh->probe[1].serverdoh); - curl_slist_free_all(data->req.doh->headers); - Curl_safefree(data->req.doh); - } #endif -#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); Curl_safefree(data->state.formp); #endif @@ -368,7 +363,6 @@ set->fread_func_set = (curl_read_callback)fread; set->is_fread_set = 0; - set->seek_func = ZERO_NULL; set->seek_client = ZERO_NULL; set->filesize = -1; /* we don't know the size */ @@ -445,21 +439,23 @@ result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); if(result) return result; - +#ifndef CURL_DISABLE_PROXY result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], CURL_CA_BUNDLE); if(result) return result; #endif +#endif #if defined(CURL_CA_PATH) result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); if(result) return result; - +#ifndef CURL_DISABLE_PROXY result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); if(result) return result; #endif +#endif } #ifndef CURL_DISABLE_FTP @@ -520,9 +516,12 @@ data->magic = CURLEASY_MAGIC_NUMBER; + Curl_req_init(&data->req); + result = Curl_resolver_init(data, &data->state.async.resolver); if(result) { DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); + Curl_req_free(&data->req, data); free(data); return result; } @@ -546,6 +545,7 @@ Curl_resolver_cleanup(data->state.async.resolver); Curl_dyn_free(&data->state.headerb); Curl_freeset(data); + Curl_req_free(&data->req, data); free(data); data = NULL; } @@ -774,16 +774,16 @@ } /* - * This function checks if the given connection is dead and extracts it from + * This function checks if the given connection is dead and prunes it from * the connection cache if so. * * When this is called as a Curl_conncache_foreach() callback, the connection * cache lock is held! * - * Returns TRUE if the connection was dead and extracted. + * Returns TRUE if the connection was dead and pruned. */ -static bool extract_if_dead(struct connectdata *conn, - struct Curl_easy *data) +static bool prune_if_dead(struct connectdata *conn, + struct Curl_easy *data) { if(!CONN_INUSE(conn)) { /* The check for a dead socket makes sense only if the connection isn't in @@ -810,7 +810,7 @@ } else { - bool input_pending; + bool input_pending = FALSE; Curl_attach_connection(data, conn); dead = !Curl_conn_is_alive(data, conn, &input_pending); @@ -829,6 +829,7 @@ } if(dead) { + /* remove connection from cache */ infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead", conn->connection_id); Curl_conncache_remove_conn(data, conn, FALSE); @@ -838,22 +839,17 @@ return FALSE; } -struct prunedead { - struct Curl_easy *data; - struct connectdata *extracted; -}; - /* - * Wrapper to use extract_if_dead() function in Curl_conncache_foreach() + * Wrapper to use prune_if_dead() function in Curl_conncache_foreach() * */ -static int call_extract_if_dead(struct Curl_easy *data, - struct connectdata *conn, void *param) +static int call_prune_if_dead(struct Curl_easy *data, + struct connectdata *conn, void *param) { - struct prunedead *p = (struct prunedead *)param; - if(extract_if_dead(conn, data)) { - /* stop the iteration here, pass back the connection that was extracted */ - p->extracted = conn; + struct connectdata **pruned = (struct connectdata **)param; + if(prune_if_dead(conn, data)) { + /* stop the iteration here, pass back the connection that was pruned */ + *pruned = conn; return 1; } return 0; /* continue iteration */ @@ -877,18 +873,15 @@ CONNCACHE_UNLOCK(data); if(elapsed >= 1000L) { - struct prunedead prune; - prune.data = data; - prune.extracted = NULL; - while(Curl_conncache_foreach(data, data->state.conn_cache, &prune, - call_extract_if_dead)) { + struct connectdata *pruned = NULL; + while(Curl_conncache_foreach(data, data->state.conn_cache, &pruned, + call_prune_if_dead)) { /* unlocked */ - /* remove connection from cache */ - Curl_conncache_remove_conn(data, prune.extracted, TRUE); + /* connection previously removed from cache in prune_if_dead() */ /* disconnect it */ - Curl_disconnect(data, prune.extracted, TRUE); + Curl_disconnect(data, pruned, TRUE); } CONNCACHE_LOCK(data); data->state.conn_cache->last_cleanup = now; @@ -932,13 +925,12 @@ struct Curl_llist_element *curr; #ifdef USE_NTLM - bool wantNTLMhttp = ((data->state.authhost.want & - (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) && (needle->handler->protocol & PROTO_FAMILY_HTTP)); #ifndef CURL_DISABLE_PROXY bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd && ((data->state.authproxy.want & - (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + CURLAUTH_NTLM) && (needle->handler->protocol & PROTO_FAMILY_HTTP))); #else bool wantProxyNTLMhttp = FALSE; @@ -1009,9 +1001,9 @@ if(!canmultiplex) { if(Curl_resolver_asynch() && - /* primary_ip[0] is NUL only if the resolving of the name hasn't + /* remote_ip[0] is NUL only if the resolving of the name hasn't completed yet and until then we don't reuse this connection */ - !check->primary_ip[0]) + !check->primary.remote_ip[0]) continue; } @@ -1300,7 +1292,7 @@ /* When not multiplexed, we have a match here! */ infof(data, "Multiplexed connection found"); } - else if(extract_if_dead(check, data)) { + else if(prune_if_dead(check, data)) { /* disconnect it */ Curl_disconnect(data, check, TRUE); continue; @@ -1334,11 +1326,15 @@ */ #ifndef CURL_DISABLE_VERBOSE_STRINGS void Curl_verboseconnect(struct Curl_easy *data, - struct connectdata *conn) + struct connectdata *conn, int sockindex) { - if(data->set.verbose) + if(data->set.verbose && sockindex == SECONDARYSOCKET) + infof(data, "Connected 2nd connection to %s port %u", + conn->secondary.remote_ip, conn->secondary.remote_port); + else infof(data, "Connected to %s (%s) port %u", - CURL_CONN_HOST_DISPNAME(conn), conn->primary_ip, conn->port); + CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip, + conn->primary.remote_port); } #endif @@ -1358,7 +1354,7 @@ conn->sockfd = CURL_SOCKET_BAD; conn->writesockfd = CURL_SOCKET_BAD; conn->connection_id = -1; /* no ID */ - conn->port = -1; /* unknown at this point */ + conn->primary.remote_port = -1; /* unknown at this point */ conn->remote_port = -1; /* unknown at this point */ /* Default protocol-independent behavior doesn't support persistent @@ -1406,12 +1402,6 @@ conn->connect_only = data->set.connect_only; conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ -#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - conn->ntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; - conn->proxyntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; -#endif - /* Initialize the easy handle list */ Curl_llist_init(&conn->easyq, NULL); @@ -1713,7 +1703,7 @@ } } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 /* * If the URL was set with an IPv6 numerical address with a zone id part, set * the scope_id based on that! @@ -1971,14 +1961,14 @@ } else { unsigned long port = strtoul(data->state.up.port, NULL, 10); - conn->port = conn->remote_port = + conn->primary.remote_port = conn->remote_port = (data->set.use_port && data->state.allow_port) ? data->set.use_port : curlx_ultous(port); } (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 if(data->set.scope_id) /* Override any scope that was set above. */ conn->scope_id = data->set.scope_id; @@ -2047,32 +2037,14 @@ p = conn->handler; /* May have changed. */ } - if(conn->port < 0) + if(conn->primary.remote_port < 0) /* we check for -1 here since if proxy was detected already, this was very likely already set to the proxy port */ - conn->port = p->defport; + conn->primary.remote_port = p->defport; return CURLE_OK; } -/* - * Curl_free_request_state() should free temp data that was allocated in the - * Curl_easy for this single request. - */ - -void Curl_free_request_state(struct Curl_easy *data) -{ - Curl_safefree(data->req.p.http); - Curl_safefree(data->req.newurl); -#ifndef CURL_DISABLE_DOH - if(data->req.doh) { - Curl_close(&data->req.doh->probe[0].easy); - Curl_close(&data->req.doh->probe[1].easy); - } -#endif - Curl_client_cleanup(data); -} - #ifndef CURL_DISABLE_PROXY @@ -2104,19 +2076,13 @@ * For compatibility, the all-uppercase versions of these variables are * checked if the lowercase versions don't exist. */ - char proxy_env[128]; - const char *protop = conn->handler->scheme; + char proxy_env[20]; char *envp = proxy_env; #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)data; #endif - /* Now, build <protocol>_proxy and check for such a one to use */ - while(*protop) - *envp++ = Curl_raw_tolower(*protop++); - - /* append _proxy */ - strcpy(envp, "_proxy"); + msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", conn->handler->scheme); /* read the protocol proxy: */ proxy = curl_getenv(proxy_env); @@ -2139,7 +2105,6 @@ proxy = curl_getenv(proxy_env); } - envp = proxy_env; if(!proxy) { #ifdef USE_WEBSOCKETS /* websocket proxy fallbacks */ @@ -2314,8 +2279,9 @@ } if(port >= 0) { proxyinfo->port = port; - if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc) - conn->port = port; + if(conn->primary.remote_port < 0 || sockstype || + !conn->socks_proxy.host.rawalloc) + conn->primary.remote_port = port; } /* now, clone the proxy host name */ @@ -2386,17 +2352,16 @@ data->state.aptr.proxyuser : ""; const char *proxypasswd = data->state.aptr.proxypasswd ? data->state.aptr.proxypasswd : ""; - CURLcode result = Curl_urldecode(proxyuser, 0, &conn->http_proxy.user, NULL, - REJECT_ZERO); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - conn->http_proxy.user); - if(!result) - result = Curl_urldecode(proxypasswd, 0, &conn->http_proxy.passwd, - NULL, REJECT_ZERO); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - conn->http_proxy.passwd); + CURLcode result = CURLE_OUT_OF_MEMORY; + + conn->http_proxy.user = strdup(proxyuser); + if(conn->http_proxy.user) { + conn->http_proxy.passwd = strdup(proxypasswd); + if(conn->http_proxy.passwd) + result = CURLE_OK; + else + Curl_safefree(conn->http_proxy.user); + } return result; } @@ -2596,14 +2561,15 @@ * * Parameters: * - * login [in] - The login string. - * len [in] - The length of the login string. - * userp [in/out] - The address where a pointer to newly allocated memory + * login [in] - login string. + * len [in] - length of the login string. + * userp [in/out] - address where a pointer to newly allocated memory * holding the user will be stored upon completion. - * passwdp [in/out] - The address where a pointer to newly allocated memory + * passwdp [in/out] - address where a pointer to newly allocated memory * holding the password will be stored upon completion. - * optionsp [in/out] - The address where a pointer to newly allocated memory - * holding the options will be stored upon completion. + * optionsp [in/out] - OPTIONAL address where a pointer to newly allocated + * memory holding the options will be stored upon + * completion. * * Returns CURLE_OK on success. */ @@ -2611,19 +2577,19 @@ char **userp, char **passwdp, char **optionsp) { - CURLcode result = CURLE_OK; char *ubuf = NULL; char *pbuf = NULL; - char *obuf = NULL; const char *psep = NULL; const char *osep = NULL; size_t ulen; size_t plen; size_t olen; + DEBUGASSERT(userp); + DEBUGASSERT(passwdp); + /* Attempt to find the password separator */ - if(passwdp) - psep = memchr(login, ':', len); + psep = memchr(login, ':', len); /* Attempt to find the options separator */ if(optionsp) @@ -2635,64 +2601,40 @@ (osep ? (size_t)(osep - login) : len)); plen = (psep ? (osep && osep > psep ? (size_t)(osep - psep) : - (size_t)(login + len - psep)) - 1 : 0); + (size_t)(login + len - psep)) - 1 : 0); olen = (osep ? (psep && psep > osep ? (size_t)(psep - osep) : - (size_t)(login + len - osep)) - 1 : 0); + (size_t)(login + len - osep)) - 1 : 0); - /* Allocate the user portion buffer, which can be zero length */ - if(userp) { - ubuf = malloc(ulen + 1); - if(!ubuf) - result = CURLE_OUT_OF_MEMORY; - } + /* Clone the user portion buffer, which can be zero length */ + ubuf = Curl_memdup0(login, ulen); + if(!ubuf) + goto error; - /* Allocate the password portion buffer */ - if(!result && passwdp && psep) { - pbuf = malloc(plen + 1); - if(!pbuf) { - free(ubuf); - result = CURLE_OUT_OF_MEMORY; - } + /* Clone the password portion buffer */ + if(psep) { + pbuf = Curl_memdup0(&psep[1], plen); + if(!pbuf) + goto error; } /* Allocate the options portion buffer */ - if(!result && optionsp && olen) { - obuf = malloc(olen + 1); - if(!obuf) { - free(pbuf); - free(ubuf); - result = CURLE_OUT_OF_MEMORY; + if(optionsp) { + char *obuf = NULL; + if(olen) { + obuf = Curl_memdup0(&osep[1], olen); + if(!obuf) + goto error; } + *optionsp = obuf; } - - if(!result) { - /* Store the user portion if necessary */ - if(ubuf) { - memcpy(ubuf, login, ulen); - ubuf[ulen] = '\0'; - Curl_safefree(*userp); - *userp = ubuf; - } - - /* Store the password portion if necessary */ - if(pbuf) { - memcpy(pbuf, psep + 1, plen); - pbuf[plen] = '\0'; - Curl_safefree(*passwdp); - *passwdp = pbuf; - } - - /* Store the options portion if necessary */ - if(obuf) { - memcpy(obuf, osep + 1, olen); - obuf[olen] = '\0'; - Curl_safefree(*optionsp); - *optionsp = obuf; - } - } - - return result; + *userp = ubuf; + *passwdp = pbuf; + return CURLE_OK; +error: + free(ubuf); + free(pbuf); + return CURLE_OUT_OF_MEMORY; } /************************************************************* @@ -2903,7 +2845,7 @@ /* detect and extract RFC6874-style IPv6-addresses */ if(*hostptr == '[') { -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 char *ptr = ++hostptr; /* advance beyond the initial bracket */ while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) ptr++; @@ -3101,7 +3043,7 @@ #ifdef USE_HTTP2 | ALPN_h2 #endif -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 | ALPN_h3 #endif ) & data->asi->flags; @@ -3213,8 +3155,8 @@ if(!conn->hostname_resolve) return CURLE_OUT_OF_MEMORY; - rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port, - &hostaddr, timeout_ms); + rc = Curl_resolv_timeout(data, conn->hostname_resolve, + conn->primary.remote_port, &hostaddr, timeout_ms); conn->dns_entry = hostaddr; if(rc == CURLRESOLV_PENDING) *async = TRUE; @@ -3244,7 +3186,7 @@ /* If not connecting via a proxy, extract the port from the URL, if it is * there, thus overriding any defaults that might have been set above. */ - conn->port = conn->bits.conn_to_port ? conn->conn_to_port : + conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port; /* Resolve target host right on */ @@ -3252,8 +3194,8 @@ if(!conn->hostname_resolve) return CURLE_OUT_OF_MEMORY; - rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port, - &hostaddr, timeout_ms); + rc = Curl_resolv_timeout(data, conn->hostname_resolve, + conn->primary.remote_port, &hostaddr, timeout_ms); conn->dns_entry = hostaddr; if(rc == CURLRESOLV_PENDING) *async = TRUE; @@ -3590,7 +3532,7 @@ /* this is supposed to be the connect function so we better at least check that the file is present here! */ DEBUGASSERT(conn->handler->connect_it); - Curl_persistconninfo(data, conn, NULL, -1); + Curl_persistconninfo(data, conn, NULL); result = conn->handler->connect_it(data, &done); /* Setup a "faked" transfer that'll do nothing */ @@ -3610,7 +3552,7 @@ (void)conn->handler->done(data, result, FALSE); goto out; } - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); } /* since we skip do_init() */ @@ -3621,10 +3563,10 @@ #endif /* Setup filter for network connections */ - conn->recv[FIRSTSOCKET] = Curl_conn_recv; - conn->send[FIRSTSOCKET] = Curl_conn_send; - conn->recv[SECONDARYSOCKET] = Curl_conn_recv; - conn->send[SECONDARYSOCKET] = Curl_conn_send; + 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 */ @@ -3761,14 +3703,14 @@ /* If NTLM is requested in a part of this connection, make sure we don't assume the state is fine as this is a fresh connection and NTLM is connection based. */ - if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + if((data->state.authhost.picked & CURLAUTH_NTLM) && data->state.authhost.done) { infof(data, "NTLM picked AND auth done set, clear picked"); data->state.authhost.picked = CURLAUTH_NONE; data->state.authhost.done = FALSE; } - if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + if((data->state.authproxy.picked & CURLAUTH_NTLM) && data->state.authproxy.done) { infof(data, "NTLM-proxy picked AND auth done set, clear picked"); data->state.authproxy.picked = CURLAUTH_NONE; @@ -3789,13 +3731,6 @@ /* Continue connectdata initialization here. */ - /* - * Inherit the proper values from the urldata struct AFTER we have arranged - * the persistent connection stuff - */ - conn->seek_func = data->set.seek_func; - conn->seek_client = data->set.seek_client; - /************************************************************* * Resolve the address of the server or proxy *************************************************************/ @@ -3849,6 +3784,9 @@ if(!conn->bits.reuse) result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry, 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; @@ -3863,11 +3801,8 @@ *asyncp = FALSE; /* assume synchronous resolves by default */ - /* init the single-transfer specific data */ - Curl_free_request_state(data); - memset(&data->req, 0, sizeof(struct SingleRequest)); - data->req.size = data->req.maxdownload = -1; - data->req.no_body = data->set.opt_no_body; + /* 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, asyncp); @@ -3910,12 +3845,8 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) { - struct SingleRequest *k = &data->req; - /* if this is a pushed stream, we need this: */ - CURLcode result = Curl_preconnect(data); - if(result) - return result; + CURLcode result; if(conn) { conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to @@ -3927,23 +3858,18 @@ } data->state.done = FALSE; /* *_done() is not called yet */ - data->state.expect100header = FALSE; if(data->req.no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->state.httpreq = HTTPREQ_HEAD; - k->start = Curl_now(); /* start time */ - k->header = TRUE; /* assume header */ - k->bytecount = 0; - k->ignorebody = FALSE; - - Curl_client_cleanup(data); - Curl_speedinit(data); - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - - return CURLE_OK; + result = Curl_req_start(&data->req, data); + if(!result) { + Curl_speedinit(data); + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + } + return result; } #if defined(USE_HTTP2) || defined(USE_HTTP3)
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index 7c1a29b..198a00a 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h
@@ -41,7 +41,6 @@ struct connectdata *, bool dead_connection); CURLcode Curl_setup_conn(struct Curl_easy *data, bool *protocol_done); -void Curl_free_request_state(struct Curl_easy *data); CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); @@ -59,9 +58,10 @@ specified */ #ifdef CURL_DISABLE_VERBOSE_STRINGS -#define Curl_verboseconnect(x,y) Curl_nop_stmt +#define Curl_verboseconnect(x,y,z) Curl_nop_stmt #else -void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn); +void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn, + int sockindex); #endif #if defined(USE_HTTP2) || defined(USE_HTTP3)
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index d6e240a..c40281a 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -28,8 +28,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme); -CURLUcode Curl_url_set_authority(CURLU *u, const char *authority, - unsigned int flags); +CURLUcode Curl_url_set_authority(CURLU *u, const char *authority); #ifdef DEBUGBUILD CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index 3cd0362..eb03966 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c
@@ -59,11 +59,11 @@ #define MAX_SCHEME_LEN 40 /* - * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make + * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make * sure we have _some_ value for AF_INET6 without polluting our fake value * everywhere. */ -#if !defined(ENABLE_IPV6) && !defined(AF_INET6) +#if !defined(USE_IPV6) && !defined(AF_INET6) #define AF_INET6 (AF_INET + 1) #endif @@ -79,7 +79,9 @@ char *path; char *query; char *fragment; - long portnum; /* the numerical version */ + unsigned short portnum; /* the numerical version (if 'port' is set) */ + BIT(query_present); /* to support blank */ + BIT(fragment_present); /* to support blank */ }; #define DEFAULT_SCHEME "https" @@ -232,10 +234,8 @@ /* the length of the scheme is the name part only */ size_t len = i; if(buf) { + Curl_strntolower(buf, url, i); buf[i] = 0; - while(i--) { - buf[i] = Curl_raw_tolower(url[i]); - } } return len; } @@ -264,6 +264,7 @@ const char *useurl = relurl; CURLcode result = CURLE_OK; CURLUcode uc; + bool skip_slash = FALSE; *newurl = NULL; /* protsep points to the start of the host name */ @@ -283,48 +284,50 @@ *pathsep = 0; /* we have a relative path to append to the last slash if there's one - available, or if the new URL is just a query string (starts with a - '?') we append the new one at the end of the entire currently worked - out URL */ - if(useurl[0] != '?') { + available, or the new URL is just a query string (starts with a '?') or + a fragment (starts with '#') we append the new one at the end of the + current URL */ + if((useurl[0] != '?') && (useurl[0] != '#')) { pathsep = strrchr(protsep, '/'); if(pathsep) *pathsep = 0; - } - /* Check if there's any slash after the host name, and if so, remember - that position instead */ - pathsep = strchr(protsep, '/'); - if(pathsep) - protsep = pathsep + 1; - else - protsep = NULL; + /* Check if there's any slash after the host name, and if so, remember + that position instead */ + pathsep = strchr(protsep, '/'); + if(pathsep) + protsep = pathsep + 1; + else + protsep = NULL; - /* now deal with one "./" or any amount of "../" in the newurl - and act accordingly */ + /* now deal with one "./" or any amount of "../" in the newurl + and act accordingly */ - if((useurl[0] == '.') && (useurl[1] == '/')) - useurl += 2; /* just skip the "./" */ + if((useurl[0] == '.') && (useurl[1] == '/')) + useurl += 2; /* just skip the "./" */ - while((useurl[0] == '.') && - (useurl[1] == '.') && - (useurl[2] == '/')) { - level++; - useurl += 3; /* pass the "../" */ - } + while((useurl[0] == '.') && + (useurl[1] == '.') && + (useurl[2] == '/')) { + level++; + useurl += 3; /* pass the "../" */ + } - if(protsep) { - while(level--) { - /* cut off one more level from the right of the original URL */ - pathsep = strrchr(protsep, '/'); - if(pathsep) - *pathsep = 0; - else { - *protsep = 0; - break; + if(protsep) { + while(level--) { + /* cut off one more level from the right of the original URL */ + pathsep = strrchr(protsep, '/'); + if(pathsep) + *pathsep = 0; + else { + *protsep = 0; + break; + } } } } + else + skip_slash = TRUE; } else { /* We got a new absolute path for this server */ @@ -370,7 +373,7 @@ return result; /* check if we need to append a slash */ - if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) + if(('/' == useurl[0]) || (protsep && !*protsep) || skip_slash) ; else { result = Curl_dyn_addn(&newest, "/", 1); @@ -531,8 +534,8 @@ portptr = strchr(hostname, ':'); if(portptr) { - char *rest; - long port; + char *rest = NULL; + unsigned long port; size_t keep = portptr - hostname; /* Browser behavior adaptation. If there's a colon with no digits after, @@ -550,15 +553,13 @@ if(!ISDIGIT(*portptr)) return CURLUE_BAD_PORT_NUMBER; - port = strtol(portptr, &rest, 10); /* Port number must be decimal */ + errno = 0; + port = strtoul(portptr, &rest, 10); /* Port number must be decimal */ - if(port > 0xffff) + if(errno || (port > 0xffff) || *rest) return CURLUE_BAD_PORT_NUMBER; - if(rest[0]) - return CURLUE_BAD_PORT_NUMBER; - - u->portnum = port; + u->portnum = (unsigned short) port; /* generate a new port number string to get rid of leading zeroes etc */ free(u->port); u->port = aprintf("%ld", port); @@ -680,13 +681,21 @@ if(*c == '[') return HOST_IPV6; + errno = 0; /* for strtoul */ while(!done) { - char *endp; + char *endp = NULL; unsigned long l; if(!ISDIGIT(*c)) /* most importantly this doesn't allow a leading plus or minus */ return HOST_NAME; l = strtoul(c, &endp, 0); + if(errno) + return HOST_NAME; +#if SIZEOF_LONG > 4 + /* a value larger than 32 bits */ + if(l > UINT_MAX) + return HOST_NAME; +#endif parts[n] = l; c = endp; @@ -706,16 +715,6 @@ default: return HOST_NAME; } - - /* overflow */ - if((l == ULONG_MAX) && (errno == ERANGE)) - return HOST_NAME; - -#if SIZEOF_LONG > 4 - /* a value larger than 32 bits */ - if(l > UINT_MAX) - return HOST_NAME; -#endif } switch(n) { @@ -846,8 +845,8 @@ return uc; } -CURLUcode Curl_url_set_authority(CURLU *u, const char *authority, - unsigned int flags) +/* used for HTTP/2 server push */ +CURLUcode Curl_url_set_authority(CURLU *u, const char *authority) { CURLUcode result; struct dynbuf host; @@ -855,8 +854,8 @@ DEBUGASSERT(authority); Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = parse_authority(u, authority, strlen(authority), flags, - &host, !!u->scheme); + result = parse_authority(u, authority, strlen(authority), + CURLU_DISALLOW_USER, &host, !!u->scheme); if(result) Curl_dyn_free(&host); else { @@ -1242,6 +1241,7 @@ 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) { @@ -1269,6 +1269,7 @@ 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; @@ -1404,6 +1405,8 @@ DUP(u, in, fragment); DUP(u, in, zoneid); u->portnum = in->portnum; + u->fragment_present = in->fragment_present; + u->query_present = in->query_present; } return u; fail: @@ -1488,10 +1491,16 @@ ptr = u->query; ifmissing = CURLUE_NO_QUERY; plusdecode = urldecode; + if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY)) + /* there was a blank query and the user do not ask for it */ + ptr = NULL; break; case CURLUPART_FRAGMENT: ptr = u->fragment; ifmissing = CURLUE_NO_FRAGMENT; + if(!ptr && u->fragment_present && flags & CURLU_GET_EMPTY) + /* there was a blank fragment and the user asks for it */ + ptr = ""; break; case CURLUPART_URL: { char *url; @@ -1499,13 +1508,18 @@ 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); punycode = (flags & CURLU_PUNYCODE)?1:0; depunyfy = (flags & CURLU_PUNY2IDN)?1:0; if(u->scheme && strcasecompare("file", u->scheme)) { url = aprintf("file://%s%s%s", u->path, - u->fragment? "#": "", - u->fragment? u->fragment : ""); + show_fragment ? "#": "", + u->fragment ? u->fragment : ""); } else if(!u->host) return CURLUE_NO_HOST; @@ -1593,9 +1607,9 @@ port ? ":": "", port ? port : "", u->path ? u->path : "/", - (u->query && u->query[0]) ? "?": "", - (u->query && u->query[0]) ? u->query : "", - u->fragment? "#": "", + show_query ? "?": "", + u->query ? u->query : "", + show_fragment ? "#": "", u->fragment? u->fragment : ""); free(allochost); } @@ -1687,7 +1701,6 @@ const char *part, unsigned int flags) { char **storep = NULL; - long port = 0; bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; bool plusencode = FALSE; bool urlskipslash = FALSE; @@ -1730,9 +1743,11 @@ break; case CURLUPART_QUERY: storep = &u->query; + u->query_present = FALSE; break; case CURLUPART_FRAGMENT: storep = &u->fragment; + u->fragment_present = FALSE; break; default: return CURLUE_UNKNOWN_PART; @@ -1794,18 +1809,26 @@ storep = &u->zoneid; break; case CURLUPART_PORT: - { - char *endp; - urlencode = FALSE; /* never */ - port = strtol(part, &endp, 10); /* Port number must be decimal */ - if((port <= 0) || (port > 0xffff)) + if(!ISDIGIT(part[0])) + /* not a number */ return CURLUE_BAD_PORT_NUMBER; - if(*endp) - /* weirdly provided number, not good! */ - return CURLUE_BAD_PORT_NUMBER; - storep = &u->port; - } - break; + else { + char *tmp; + char *endp; + unsigned long port; + errno = 0; + port = strtoul(part, &endp, 10); /* must be decimal */ + if(errno || (port > 0xffff) || *endp) + /* weirdly provided number, not good! */ + return CURLUE_BAD_PORT_NUMBER; + tmp = strdup(part); + if(!tmp) + return CURLUE_OUT_OF_MEMORY; + free(u->port); + u->port = tmp; + u->portnum = (unsigned short)port; + return CURLUE_OK; + } case CURLUPART_PATH: urlskipslash = TRUE; leadingslash = TRUE; /* enforce */ @@ -1816,9 +1839,11 @@ appendquery = (flags & CURLU_APPENDQUERY)?1:0; equalsencode = appendquery; storep = &u->query; + u->query_present = TRUE; break; case CURLUPART_FRAGMENT: storep = &u->fragment; + u->fragment_present = TRUE; break; case CURLUPART_URL: { /* @@ -1966,9 +1991,5 @@ free(*storep); *storep = (char *)newp; } - /* set after the string, to make it not assigned if the allocation above - fails */ - if(port) - u->portnum = port; return CURLUE_OK; }
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 9dcccc7..8b1bd65 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h
@@ -53,6 +53,17 @@ #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 + #ifdef USE_WEBSOCKETS /* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, * the rest are internal information. If we use higher bits we only do this on @@ -102,7 +113,7 @@ #define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ - !defined(CURL_DISABLE_POP3) + !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_FILE) /* these protocols support CURLOPT_DIRLISTONLY */ #define CURL_LIST_ONLY_PROTOCOL 1 #endif @@ -141,6 +152,7 @@ #include "splay.h" #include "dynbuf.h" #include "dynhds.h" +#include "request.h" /* return the count of bytes sent, or -1 on error */ typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ @@ -160,7 +172,6 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, struct connectdata *conn, int *didwhat, - bool *done, int select_res); #endif @@ -230,8 +241,7 @@ #ifdef HAVE_GSSAPI /* Types needed for krb5-ftp connections */ struct krb5buffer { - void *data; - size_t size; + struct dynbuf buf; size_t index; BIT(eof_flag); }; @@ -266,11 +276,19 @@ /* SSL backend-specific data; declared differently by each SSL backend */ struct ssl_backend_data; +typedef enum { + CURL_SSL_PEER_DNS, + CURL_SSL_PEER_IPV4, + CURL_SSL_PEER_IPV6 +} ssl_peer_type; + struct ssl_peer { char *hostname; /* hostname for verification */ char *dispname; /* display version of hostname */ char *sni; /* SNI version of hostname or NULL if not usable */ - BIT(is_ip_address); /* if hostname is an IPv4|6 address */ + ssl_peer_type type; /* type of the peer information */ + int port; /* port we are talking to */ + int transport; /* TCP or QUIC */ }; struct ssl_primary_config { @@ -326,6 +344,8 @@ int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ }; +typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize); + /* information stored about one single SSL session */ struct Curl_ssl_session { char *name; /* host name for which this ID was used */ @@ -333,9 +353,11 @@ const char *scheme; /* protocol scheme used */ void *sessionid; /* as returned from the SSL layer */ size_t idsize; /* if known, otherwise 0 */ + Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */ long age; /* just a number, the higher the more recent */ int remote_port; /* remote port */ int conn_to_port; /* remote port for the connection (may be -1) */ + int transport; /* TCP or QUIC */ struct ssl_primary_config ssl_config; /* setup for this session */ }; @@ -436,14 +458,6 @@ unsigned char nonce[8]; unsigned int target_info_len; void *target_info; /* TargetInfo received in the ntlm type-2 message */ - -#if defined(NTLM_WB_ENABLED) - /* used for communication with Samba's winbind daemon helper ntlm_auth */ - curl_socket_t ntlm_auth_hlpr_socket; - pid_t ntlm_auth_hlpr_pid; - char *challenge; /* The received base64 encoded ntlm type-2 message */ - char *response; /* The generated base64 ntlm type-1/type-3 message */ -#endif #endif }; #endif @@ -519,10 +533,6 @@ the TCP layer connect */ BIT(retry); /* this connection is about to get closed and then re-attempted at another connection. */ - BIT(authneg); /* TRUE when the auth phase has started, which means - that we are creating a request with an auth header, - but it is not the final request in the auth - negotiation. */ #ifndef CURL_DISABLE_FTP BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out EPSV doesn't work we disable it for the forthcoming @@ -575,6 +585,14 @@ #define KEEP_RECV_PAUSE (1<<4) /* reading is paused */ #define KEEP_SEND_PAUSE (1<<5) /* writing is paused */ +/* KEEP_SEND_TIMED is set when the transfer should attempt sending + * at timer (or other) events. A transfer waiting on a timer will + * remove KEEP_SEND to suppress POLLOUTs of the connection. + * Adding KEEP_SEND_TIMED will then attempt to send whenever the transfer + * enters the "readwrite" loop, e.g. when a timer fires. + * This is used in HTTP for 'Expect: 100-continue' waiting. */ +#define KEEP_SEND_TIMED (1<<6) + #define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE) #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) @@ -612,22 +630,6 @@ unsigned char actions[MAX_SOCKSPEREASYHANDLE]; }; -enum expect100 { - EXP100_SEND_DATA, /* enough waiting, just 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 */ - EXP100_FAILED /* used on 417 Expectation Failed */ -}; - -enum upgrade101 { - UPGR101_INIT, /* default state */ - UPGR101_WS, /* upgrade to WebSockets requested */ - UPGR101_H2, /* upgrade to HTTP/2 requested */ - UPGR101_RECEIVED, /* 101 response received */ - UPGR101_WORKING /* talking upgraded protocol */ -}; - enum doh_slots { /* Explicit values for first two symbols so as to match hard-coded * constants in existing code @@ -636,6 +638,9 @@ DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */ /* Space here for (possibly build-specific) additional slot definitions */ +#ifdef USE_HTTPSRR + DOH_PROBE_SLOT_HTTPS = 2, /* for HTTPS RR */ +#endif /* for example */ /* #ifdef WANT_DOH_FOOBAR_TXT */ @@ -647,116 +652,11 @@ }; /* - * Request specific data in the easy handle (Curl_easy). Previously, - * these members were on the connectdata struct but since a conn struct may - * now be shared between different Curl_easys, we store connection-specific - * data here. This struct only keeps stuff that's interesting for *this* - * request, as it will be cleared between multiple ones - */ -struct SingleRequest { - curl_off_t size; /* -1 if unknown at this point */ - curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, - -1 means unlimited */ - curl_off_t bytecount; /* total number of bytes read */ - curl_off_t writebytecount; /* number of bytes written */ - - curl_off_t pendingheader; /* this many bytes left to send is actually - header and not body */ - struct curltime start; /* transfer started at this time */ - unsigned int headerbytecount; /* received server headers (not CONNECT - headers) */ - unsigned int allheadercount; /* all received headers (server + CONNECT) */ - unsigned int deductheadercount; /* this amount of bytes doesn't count when - we check if anything has been transferred - at the end of a connection. We use this - counter to make only a 100 reply (without - a following second response code) result - in a CURLE_GOT_NOTHING error code */ - 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; - struct curltime start100; /* time stamp to wait for the 100 code from */ - enum expect100 exp100; /* expect 100 continue state */ - enum upgrade101 upgr101; /* 101 upgrade state */ - - /* Client Writer stack, handles trasnfer- and content-encodings, protocol - * checks, pausing by client callbacks. */ - struct Curl_cwriter *writer_stack; - time_t timeofdoc; - long bodywrites; - char *location; /* This points to an allocated version of the Location: - header data */ - char *newurl; /* Set to the new URL to use when a redirect or a retry is - wanted */ - - /* 'upload_present' is used to keep a byte counter of how much data there is - still left in the buffer, aimed for upload. */ - ssize_t upload_present; - - /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a - buffer, so the next read should read from where this pointer points to, - and the 'upload_present' contains the number of bytes available at this - position */ - char *upload_fromhere; - - /* Allocated protocol-specific data. Each protocol handler makes sure this - points to data it needs. */ - union { - struct FILEPROTO *file; - struct FTP *ftp; - struct HTTP *http; - struct IMAP *imap; - struct ldapreqinfo *ldap; - struct MQTT *mqtt; - struct POP3 *pop3; - struct RTSP *rtsp; - struct smb_request *smb; - struct SMTP *smtp; - struct SSHPROTO *ssh; - struct TELNET *telnet; - } p; -#ifndef CURL_DISABLE_DOH - struct dohdata *doh; /* DoH specific data for this request */ -#endif -#if defined(_WIN32) && defined(USE_WINSOCK) - struct curltime last_sndbuf_update; /* last time readwrite_upload called - win_update_buffer_size */ -#endif - char fread_eof[2]; /* the body read callback (index 0) returned EOF or - the trailer read callback (index 1) returned EOF */ -#ifndef CURL_DISABLE_COOKIES - unsigned char setcookies; -#endif - BIT(header); /* incoming data has HTTP header */ - BIT(content_range); /* set TRUE if Content-Range: was found */ - BIT(download_done); /* set to TRUE when download is complete */ - BIT(eos_written); /* iff EOS has been written to client */ - BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding - upload and we're uploading the last chunk */ - 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 */ - BIT(chunk); /* if set, this is a chunked transfer-encoding */ - BIT(ignore_cl); /* ignore content-length */ - BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding - on upload */ - BIT(getheader); /* TRUE if header parsing is wanted */ - BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for - specific upload buffers. See readmoredata() in http.c - for details. */ - BIT(no_body); /* the response has no body */ -}; - -/* * Specific protocol handler. */ struct Curl_handler { - const char *scheme; /* URL scheme name. */ + const char *scheme; /* URL scheme name in lowercase */ /* Complement to setup_connection_internals(). This is done before the transfer "owns" the connection. */ @@ -815,11 +715,17 @@ CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, bool dead_connection); - /* If used, this function gets called from transfer.c:readwrite_data() to + /* 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, bool *done); + 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, @@ -875,6 +781,13 @@ #define CONNRESULT_NONE 0 /* No extra information. */ #define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ +struct ip_quadruple { + char remote_ip[MAX_IPADR_LEN]; + char local_ip[MAX_IPADR_LEN]; + int remote_port; + int local_port; +}; + struct proxy_info { struct hostname host; int port; @@ -930,14 +843,13 @@ struct proxy_info socks_proxy; struct proxy_info http_proxy; #endif - /* 'primary_ip' and 'primary_port' get filled with peer's numerical - ip address and port number whenever an outgoing connection is - *attempted* from the primary socket to a remote address. When more - than one address is tried for a connection these will hold data + /* 'primary' and 'secondary' get filled with IP quadruple + (local/remote numerical ip address and port) whenever a is *attempted*. + When more than one address is tried for a connection these will hold data for the last attempt. When the connection is actually established these are updated with data which comes directly from the socket. */ - - char primary_ip[MAX_IPADR_LEN]; + struct ip_quadruple primary; + struct ip_quadruple secondary; char *user; /* user name string, allocated */ char *passwd; /* password string, allocated */ char *options; /* options string, allocated */ @@ -990,14 +902,17 @@ #endif /* however, some of them are ftp specific. */ struct Curl_llist easyq; /* List of easy handles using this connection */ - curl_seek_callback seek_func; /* function that seeks the input */ - void *seek_client; /* pointer to pass to the seek() above */ /*************** Request - specific items ************/ #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) CtxtHandle *sslContext; #endif +#if defined(_WIN32) && defined(USE_WINSOCK) + struct curltime last_sndbuf_update; /* last time readwrite_upload called + win_update_buffer_size */ +#endif + #ifdef USE_GSASL struct gsasldata gsasl; #endif @@ -1080,11 +995,10 @@ int socks5_gssapi_enctype; #endif /* The field below gets set in connect.c:connecthost() */ - int port; /* which port to use locally - to connect to */ int remote_port; /* the remote port, not the proxy port! */ int conn_to_port; /* the remote port to connect to. valid only if bits.conn_to_port is set */ -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int scope_id; /* Scope id for IPv6 */ #endif unsigned short localport; @@ -1135,22 +1049,16 @@ curl_off_t retry_after; /* info from Retry-After: header */ unsigned int header_size; /* size of read header(s) in bytes */ - /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip' - and, 'conn_local_port' are copied over from the connectdata struct in - order to allow curl_easy_getinfo() to return this information even when - the session handle is no longer associated with a connection, and also - allow curl_easy_reset() to clear this information from the session handle - without disturbing information which is still alive, and that might be - reused, in the connection cache. */ - - char conn_primary_ip[MAX_IPADR_LEN]; - int conn_primary_port; /* this is the destination port to the connection, - which might have been a proxy */ + /* PureInfo primary ip_quadruple is copied over from the connectdata + struct in order to allow curl_easy_getinfo() to return this information + even when the session handle is no longer associated with a connection, + and also allow curl_easy_reset() to clear this information from the + session handle without disturbing information which is still alive, and + that might be reused, in the connection cache. */ + struct ip_quadruple primary; int conn_remote_port; /* this is the "remote port", which is the port number of the used URL, independent of proxy or not */ - char conn_local_ip[MAX_IPADR_LEN]; - int conn_local_port; const char *conn_scheme; unsigned int conn_protocol; struct curl_certinfo certs; /* info about the certs. Asked for with @@ -1158,6 +1066,7 @@ CURLproxycode pxcode; BIT(timecond); /* set to TRUE if the time condition didn't match, which thus made the document NOT get fetched */ + BIT(used_proxy); /* the transfer used a proxy */ }; @@ -1263,18 +1172,6 @@ #endif }; -/* - * This struct is for holding data that was attempted to get sent to the user's - * callback but is held due to pausing. One instance per type (BOTH, HEADER, - * BODY). - */ -struct tempbuf { - struct dynbuf b; - int type; /* type of the 'tempwrite' buffer as a bitmask that is used with - Curl_client_write() */ - BIT(paused_body); /* if PAUSE happened before/during BODY write */ -}; - /* Timers */ typedef enum { EXPIRE_100_TIMEOUT, @@ -1337,8 +1234,6 @@ struct dynbuf headerb; /* buffer to store headers in */ struct curl_slist *hstslist; /* list of HSTS files set by curl_easy_setopt(HSTS) calls */ - char *buffer; /* download buffer */ - char *ulbuf; /* allocated upload buffer or NULL */ curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ @@ -1353,8 +1248,6 @@ int retrycount; /* number of retries on a new connection */ struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */ long sessionage; /* number of the most recent session */ - struct tempbuf tempwrite[3]; /* BOTH, HEADER, BODY */ - unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */ int os_errno; /* filled in with errno whenever an error occurs */ char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */ long followlocation; /* redirect counter */ @@ -1387,8 +1280,6 @@ #if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) /* do FTP line-end conversions on most platforms */ #define CURL_DO_LINEEND_CONV - /* for FTP downloads: track CRLF sequences that span blocks */ - BIT(prev_block_had_trailing_cr); /* for FTP downloads: how many CRLFs did we converted to LFs? */ curl_off_t crlf_conversions; #endif @@ -1422,8 +1313,10 @@ this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP curl_mimepart *mimepost; +#ifndef CURL_DISABLE_FORM_API curl_mimepart *formp; /* storage for old API form-posting, allocated on demand */ +#endif size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing headers */ @@ -1442,25 +1335,35 @@ CURLcode hresult; /* used to pass return codes back from hyper callbacks */ #endif +#ifndef CURL_DISABLE_VERBOSE_STRINGS + struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ +#endif + /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { - char *proxyuserpwd; 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 char *te; /* TE: request header */ /* transfer credentials */ char *user; char *passwd; +#ifndef CURL_DISABLE_PROXY + char *proxyuserpwd; char *proxyuser; char *proxypasswd; +#endif } aptr; unsigned char httpwant; /* when non-zero, a specific HTTP version requested @@ -1490,7 +1393,6 @@ BIT(authproblem); /* TRUE if there's some problem authenticating */ /* set after initial USER failure, to prevent an authentication loop */ BIT(wildcardmatch); /* enable wildcard matching */ - BIT(expect100header); /* TRUE if we added Expect: 100-continue */ BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous 417 response */ BIT(use_range); @@ -1498,7 +1400,6 @@ BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE when multi_done() is called, to prevent multi_done() to get invoked twice when the multi interface is used. */ - BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ #ifndef CURL_DISABLE_COOKIES BIT(cookie_engine); #endif @@ -1509,9 +1410,6 @@ BIT(url_alloc); /* URL string is malloc()'ed */ BIT(referer_alloc); /* referer string is malloc()ed */ BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ - BIT(rewindbeforesend);/* TRUE when the sending couldn't be stopped even - though it will be discarded. We must call the data - rewind callback before trying to send again. */ BIT(upload); /* upload request */ BIT(internal); /* internal: true if this easy handle was created for internal use and the user does not have ownership of the @@ -1529,95 +1427,127 @@ struct Curl_multi; /* declared in multihandle.c */ -/* - * This enumeration MUST not use conditional directives (#ifdefs), new - * null terminated strings MUST be added to the enumeration immediately - * before STRING_LASTZEROTERMINATED, binary fields immediately before - * STRING_LAST. When doing so, ensure that the packages/OS400/chkstring.c - * test is updated and applicable changes for EBCDIC to ASCII conversion - * are catered for in curl_easy_setopt_ccsid() - */ enum dupstring { STRING_CERT, /* client certificate file name */ - STRING_CERT_PROXY, /* client certificate file name */ STRING_CERT_TYPE, /* format for certificate (default: PEM)*/ + STRING_KEY, /* private key file name */ + STRING_KEY_PASSWD, /* plain text private key password */ + STRING_KEY_TYPE, /* format for private key (default: PEM) */ + STRING_SSL_CAPATH, /* CA directory name (doesn't work on windows) */ + STRING_SSL_CAFILE, /* certificate file to verify peer against */ + STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ + STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ + STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */ + STRING_SSL_CRLFILE, /* crl file to check certificate */ + STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ + STRING_SERVICE_NAME, /* Service name */ +#ifndef CURL_DISABLE_PROXY + STRING_CERT_PROXY, /* client certificate file name */ STRING_CERT_TYPE_PROXY, /* format for certificate (default: PEM)*/ + STRING_KEY_PROXY, /* private key file name */ + STRING_KEY_PASSWD_PROXY, /* plain text private key password */ + STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */ + STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */ + STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */ + STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */ + STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */ + STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */ + STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */ + STRING_SSL_ISSUERCERT_PROXY, /* issuer cert file to check certificate */ + STRING_PROXY_SERVICE_NAME, /* Proxy service name */ +#endif +#ifndef CURL_DISABLE_COOKIES STRING_COOKIE, /* HTTP cookie string to send */ STRING_COOKIEJAR, /* dump all cookies to this file */ +#endif STRING_CUSTOMREQUEST, /* HTTP/FTP/RTSP request/method to use */ STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL doesn't specify */ STRING_DEVICE, /* local network interface/address to use */ STRING_ENCODING, /* Accept-Encoding string */ +#ifndef CURL_DISABLE_FTP STRING_FTP_ACCOUNT, /* ftp account data */ STRING_FTP_ALTERNATIVE_TO_USER, /* command to send if USER/PASS fails */ STRING_FTPPORT, /* port to send with the FTP PORT command */ - STRING_KEY, /* private key file name */ - STRING_KEY_PROXY, /* private key file name */ - STRING_KEY_PASSWD, /* plain text private key password */ - STRING_KEY_PASSWD_PROXY, /* plain text private key password */ - STRING_KEY_TYPE, /* format for private key (default: PEM) */ - STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */ +#endif +#if defined(HAVE_GSSAPI) STRING_KRB_LEVEL, /* krb security level */ +#endif +#ifndef CURL_DISABLE_NETRC STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find $HOME/.netrc */ +#endif +#ifndef CURL_DISABLE_PROXY STRING_PROXY, /* proxy to use */ STRING_PRE_PROXY, /* pre socks proxy to use */ +#endif STRING_SET_RANGE, /* range, if used */ STRING_SET_REFERER, /* custom string for the HTTP referer field */ STRING_SET_URL, /* what original URL to work on */ - STRING_SSL_CAPATH, /* CA directory name (doesn't work on windows) */ - STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */ - STRING_SSL_CAFILE, /* certificate file to verify peer against */ - STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */ - STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ - STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */ - STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ - STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */ - STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */ - STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */ STRING_USERAGENT, /* User-Agent string */ - STRING_SSL_CRLFILE, /* crl file to check certificate */ - STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */ - STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ - STRING_SSL_ISSUERCERT_PROXY, /* issuer cert file to check certificate */ STRING_SSL_ENGINE, /* name of ssl engine */ STRING_USERNAME, /* <username>, if used */ STRING_PASSWORD, /* <password>, if used */ STRING_OPTIONS, /* <options>, if used */ +#ifndef CURL_DISABLE_PROXY STRING_PROXYUSERNAME, /* Proxy <username>, if used */ STRING_PROXYPASSWORD, /* Proxy <password>, if used */ STRING_NOPROXY, /* List of hosts which should not use the proxy, if used */ +#endif +#ifndef CURL_DISABLE_RTSP STRING_RTSP_SESSION_ID, /* Session ID to use */ STRING_RTSP_STREAM_URI, /* Stream URI for this request */ STRING_RTSP_TRANSPORT, /* Transport for this session */ +#endif +#ifdef USE_SSH STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */ STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */ - STRING_PROXY_SERVICE_NAME, /* Proxy service name */ - STRING_SERVICE_NAME, /* Service name */ +#endif +#ifndef CURL_DISABLE_SMTP STRING_MAIL_FROM, STRING_MAIL_AUTH, +#endif +#ifdef USE_TLS_SRP STRING_TLSAUTH_USERNAME, /* TLS auth <username> */ - STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth <username> */ STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */ +#ifndef CURL_DISABLE_PROXY + STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth <username> */ STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth <password> */ +#endif +#endif STRING_BEARER, /* <bearer>, if used */ +#ifdef USE_UNIX_SOCKETS STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ +#endif STRING_TARGET, /* CURLOPT_REQUEST_TARGET */ +#ifndef CURL_DISABLE_DOH STRING_DOH, /* CURLOPT_DOH_URL */ +#endif +#ifndef CURL_DISABLE_ALTSVC STRING_ALTSVC, /* CURLOPT_ALTSVC */ +#endif +#ifndef CURL_DISABLE_HSTS STRING_HSTS, /* CURLOPT_HSTS */ +#endif STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */ +#ifdef USE_ARES STRING_DNS_SERVERS, STRING_DNS_INTERFACE, STRING_DNS_LOCAL_IP4, STRING_DNS_LOCAL_IP6, +#endif STRING_SSL_EC_CURVES, +#ifndef CURL_DISABLE_AWS STRING_AWS_SIGV4, /* Parameters for V4 signature */ +#endif +#ifndef CURL_DISABLE_PROXY STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */ +#endif + STRING_ECH_CONFIG, /* CURLOPT_ECH_CONFIG */ + STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */ /* -- end of null-terminated strings -- */ @@ -1632,13 +1562,15 @@ enum dupblob { BLOB_CERT, - BLOB_CERT_PROXY, BLOB_KEY, - BLOB_KEY_PROXY, BLOB_SSL_ISSUERCERT, - BLOB_SSL_ISSUERCERT_PROXY, BLOB_CAINFO, +#ifndef CURL_DISABLE_PROXY + BLOB_CERT_PROXY, + BLOB_KEY_PROXY, + BLOB_SSL_ISSUERCERT_PROXY, BLOB_CAINFO_PROXY, +#endif BLOB_LAST }; @@ -1720,7 +1652,9 @@ curl_off_t set_resume_from; /* continue [ftp] transfer from here */ 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. */ +#endif #ifndef CURL_DISABLE_TELNET struct curl_slist *telnet_options; /* linked list of telnet options */ #endif @@ -1787,7 +1721,7 @@ unsigned int 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 ENABLE_IPV6 +#ifdef USE_IPV6 unsigned int scope_id; /* Scope id for IPv6 */ #endif curl_prot_t allowed_protocols; @@ -1855,7 +1789,9 @@ BIT(cookiesession); /* new cookie session? */ #endif BIT(crlf); /* convert crlf on ftp upload(?) */ +#ifdef USE_SSH BIT(ssh_compression); /* enable SSH compression */ +#endif /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially @@ -1865,7 +1801,9 @@ don't want lengthy cleanups to delay termination, e.g. after a DNS timeout */ BIT(get_filetime); /* get the time and get of the remote file */ +#ifndef CURL_DISABLE_PROXY BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */ +#endif BIT(prefer_ascii); /* ASCII rather than binary */ BIT(remote_append); /* append, not overwrite, on upload */ #ifdef CURL_LIST_ONLY_PROTOCOL @@ -1892,7 +1830,9 @@ location: */ BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ BIT(verbose); /* output verbosity */ +#if defined(HAVE_GSSAPI) BIT(krb); /* Kerberos connection requested */ +#endif BIT(reuse_forbid); /* forbidden to be reused, close after use */ BIT(reuse_fresh); /* do not reuse an existing connection */ BIT(no_signal); /* do not use any signal/alarm handler */ @@ -1917,9 +1857,13 @@ BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers from user callbacks */ BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */ +#ifndef CURL_DISABLE_PROXY BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1 header */ +#endif +#ifdef USE_UNIX_SOCKETS BIT(abstract_unix_socket); +#endif BIT(disallow_username_in_url); /* disallow username in url */ #ifndef CURL_DISABLE_DOH BIT(doh); /* DNS-over-HTTPS enabled */ @@ -1931,8 +1875,17 @@ #ifdef USE_WEBSOCKETS BIT(ws_raw_mode); #endif +#ifdef USE_ECH + int tls_ech; /* TLS ECH configuration */ +#endif }; +#ifndef CURL_DISABLE_MIME +#define IS_MIME_POST(a) ((a)->set.mimepost.kind != MIMEKIND_NONE) +#else +#define IS_MIME_POST(a) FALSE +#endif + struct Names { struct Curl_hash *hostcache; enum {
diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c index 972a874..29389c2 100644 --- a/Utilities/cmcurl/lib/vauth/cleartext.c +++ b/Utilities/cmcurl/lib/vauth/cleartext.c
@@ -107,12 +107,11 @@ * valuep [in] - The user name or user's password. * out [out] - The result storage. * - * Returns CURLE_OK on success. + * Returns void. */ -CURLcode Curl_auth_create_login_message(const char *valuep, struct bufref *out) +void Curl_auth_create_login_message(const char *valuep, struct bufref *out) { Curl_bufref_set(out, valuep, strlen(valuep), NULL); - return CURLE_OK; } /* @@ -126,13 +125,13 @@ * user [in] - The user name. * out [out] - The result storage. * - * Returns CURLE_OK on success. + * Returns void. */ -CURLcode Curl_auth_create_external_message(const char *user, +void Curl_auth_create_external_message(const char *user, struct bufref *out) { /* This is the same formatting as the login message */ - return Curl_auth_create_login_message(user, out); + Curl_auth_create_login_message(user, out); } #endif /* if no users */
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index 416da0f..a742cce 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -38,6 +38,7 @@ #include "curl_hmac.h" #include "curl_md5.h" #include "curl_sha256.h" +#include "curl_sha512_256.h" #include "vtls/vtls.h" #include "warnless.h" #include "strtok.h" @@ -150,7 +151,7 @@ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); } -/* Convert sha256 chunk to RFC7616 -suitable ascii string */ +/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ascii string */ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ unsigned char *dest) /* 65 bytes */ { @@ -287,7 +288,7 @@ /* Retrieve realm string from the challenge */ if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) { /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ - strcpy(realm, ""); + *realm = '\0'; } /* Retrieve algorithm string from the challenge */ @@ -601,10 +602,20 @@ digest->algo = ALGO_SHA256; else if(strcasecompare(content, "SHA-256-SESS")) digest->algo = ALGO_SHA256SESS; - else if(strcasecompare(content, "SHA-512-256")) + else if(strcasecompare(content, "SHA-512-256")) { +#ifdef CURL_HAVE_SHA512_256 digest->algo = ALGO_SHA512_256; - else if(strcasecompare(content, "SHA-512-256-SESS")) +#else /* ! CURL_HAVE_SHA512_256 */ + return CURLE_NOT_BUILT_IN; +#endif /* ! CURL_HAVE_SHA512_256 */ + } + else if(strcasecompare(content, "SHA-512-256-SESS")) { +#ifdef CURL_HAVE_SHA512_256 digest->algo = ALGO_SHA512_256SESS; +#else /* ! CURL_HAVE_SHA512_256 */ + return CURLE_NOT_BUILT_IN; +#endif /* ! CURL_HAVE_SHA512_256 */ + } else return CURLE_BAD_CONTENT_ENCODING; } @@ -717,8 +728,10 @@ if(!hashthis) return CURLE_OUT_OF_MEMORY; - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); + if(result) + return result; convert_to_ascii(hashbuf, (unsigned char *)userh); } @@ -738,8 +751,10 @@ if(!hashthis) return CURLE_OUT_OF_MEMORY; - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); + if(result) + return result; convert_to_ascii(hashbuf, ha1); if(digest->algo & SESSION_ALGO) { @@ -748,8 +763,10 @@ if(!tmp) return CURLE_OUT_OF_MEMORY; - hash(hashbuf, (unsigned char *) tmp, strlen(tmp)); + result = hash(hashbuf, (unsigned char *) tmp, strlen(tmp)); free(tmp); + if(result) + return result; convert_to_ascii(hashbuf, ha1); } @@ -775,7 +792,11 @@ char hashed[65]; char *hashthis2; - hash(hashbuf, (const unsigned char *)"", 0); + result = hash(hashbuf, (const unsigned char *)"", 0); + if(result) { + free(hashthis); + return result; + } convert_to_ascii(hashbuf, (unsigned char *)hashed); hashthis2 = aprintf("%s:%s", hashthis, hashed); @@ -786,8 +807,10 @@ if(!hashthis) return CURLE_OUT_OF_MEMORY; - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); + if(result) + return result; convert_to_ascii(hashbuf, ha2); if(digest->qop) { @@ -801,8 +824,10 @@ if(!hashthis) return CURLE_OUT_OF_MEMORY; - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); + result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); free(hashthis); + if(result) + return result; convert_to_ascii(hashbuf, request_digest); /* For test case 64 (snooped from a Mozilla 1.3a request) @@ -957,12 +982,24 @@ outptr, outlen, auth_digest_md5_to_ascii, Curl_md5it); - DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS); - return auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, - outptr, outlen, - auth_digest_sha256_to_ascii, - Curl_sha256it); + + if(digest->algo <= ALGO_SHA256SESS) + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_sha256_to_ascii, + Curl_sha256it); +#ifdef CURL_HAVE_SHA512_256 + if(digest->algo <= ALGO_SHA512_256SESS) + return auth_create_digest_http_message(data, userp, passwdp, + request, uripath, digest, + outptr, outlen, + auth_digest_sha256_to_ascii, + Curl_sha512_256it); +#endif /* CURL_HAVE_SHA512_256 */ + + /* Should be unreachable */ + return CURLE_BAD_CONTENT_ENCODING; } /*
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h index 9da0540..7e82348 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.h +++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -79,12 +79,10 @@ struct bufref *out); /* This is used to generate a LOGIN cleartext message */ -CURLcode Curl_auth_create_login_message(const char *value, - struct bufref *out); +void Curl_auth_create_login_message(const char *value, struct bufref *out); /* This is used to generate an EXTERNAL cleartext message */ -CURLcode Curl_auth_create_external_message(const char *user, - struct bufref *out); +void Curl_auth_create_external_message(const char *user, struct bufref *out); #ifndef CURL_DISABLE_DIGEST_AUTH /* This is used to generate a CRAM-MD5 response message */
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index 34d4d7c..a1e333b 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c
@@ -55,6 +55,7 @@ #ifdef USE_LIBRTMP #include <librtmp/rtmp.h> +#include "curl_rtmp.h" #endif #ifdef HAVE_LIBZ @@ -152,7 +153,7 @@ #ifdef USE_NGHTTP2 char h2_version[40]; #endif -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 char h3_version[40]; #endif #ifdef USE_LIBRTMP @@ -174,8 +175,7 @@ /* Override version string when environment variable CURL_VERSION is set */ const char *debugversion = getenv("CURL_VERSION"); if(debugversion) { - strncpy(out, debugversion, sizeof(out)-1); - out[sizeof(out)-1] = '\0'; + msnprintf(out, sizeof(out), "%s", debugversion); return out; } #endif @@ -208,13 +208,21 @@ src[i++] = idn_version; #elif defined(USE_WIN32_IDN) src[i++] = (char *)"WinIDN"; +#elif defined(USE_APPLE_IDN) + src[i++] = (char *)"AppleIDN"; #endif #ifdef USE_LIBPSL { +#if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \ + PSL_VERSION_MINOR >= 11) int num = psl_check_version_number(0); msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d", num >> 16, (num >> 8) & 0xff, num & 0xff); +#else + msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", + psl_get_version()); +#endif src[i++] = psl_version; } #endif @@ -227,25 +235,13 @@ Curl_http2_ver(h2_version, sizeof(h2_version)); src[i++] = h2_version; #endif -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 Curl_quic_ver(h3_version, sizeof(h3_version)); src[i++] = h3_version; #endif #ifdef USE_LIBRTMP - { - char suff[2]; - if(RTMP_LIB_VERSION & 0xff) { - suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1; - suff[1] = '\0'; - } - else - suff[0] = '\0'; - - msnprintf(rtmp_version, sizeof(rtmp_version), "librtmp/%d.%d%s", - RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, - suff); - src[i++] = rtmp_version; - } + Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); + src[i++] = rtmp_version; #endif #ifdef USE_HYPER msnprintf(hyper_buf, sizeof(hyper_buf), "Hyper/%s", hyper_version()); @@ -422,6 +418,14 @@ } #endif +#if defined(USE_SSL) && defined(USE_ECH) +static int ech_present(curl_version_info_data *info) +{ + (void) info; + return Curl_ssl_supports(NULL, SSLSUPP_ECH); +} +#endif + /* * Features table. * @@ -450,6 +454,9 @@ #ifdef DEBUGBUILD FEATURE("Debug", NULL, CURL_VERSION_DEBUG), #endif +#if defined(USE_SSL) && defined(USE_ECH) + FEATURE("ECH", ech_present, 0), +#endif #ifdef USE_GSASL FEATURE("gsasl", NULL, CURL_VERSION_GSASL), #endif @@ -462,17 +469,17 @@ #if defined(USE_NGHTTP2) FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2), #endif -#if defined(ENABLE_QUIC) +#if defined(USE_HTTP3) FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3), #endif #if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \ !defined(CURL_DISABLE_HTTP) FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), #endif -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) FEATURE("IDN", idn_present, CURL_VERSION_IDN), #endif -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 FEATURE("IPv6", NULL, CURL_VERSION_IPV6), #endif #ifdef USE_KERBEROS5 @@ -491,10 +498,6 @@ #ifdef USE_NTLM FEATURE("NTLM", NULL, CURL_VERSION_NTLM), #endif -#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - FEATURE("NTLM_WB", NULL, CURL_VERSION_NTLM_WB), -#endif #if defined(USE_LIBPSL) FEATURE("PSL", NULL, CURL_VERSION_PSL), #endif @@ -566,7 +569,8 @@ NULL, /* zstd version */ NULL, /* Hyper version */ NULL, /* gsasl version */ - feature_names + feature_names, + NULL /* rtmp version */ }; curl_version_info_data *curl_version_info(CURLversion stamp) @@ -641,7 +645,7 @@ } #endif -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 { static char quicbuffer[80]; Curl_quic_ver(quicbuffer, sizeof(quicbuffer)); @@ -674,5 +678,13 @@ 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_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c index 7674bc1..d49af6e 100644 --- a/Utilities/cmcurl/lib/vquic/curl_msh3.c +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c
@@ -27,6 +27,7 @@ #ifdef USE_MSH3 #include "urldata.h" +#include "hash.h" #include "timeval.h" #include "multiif.h" #include "sendf.h" @@ -118,6 +119,7 @@ struct cf_call_data call_data; struct curltime connect_started; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ + struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ /* Flags written by msh3/msquic thread */ bool handshake_complete; bool handshake_succeeded; @@ -127,6 +129,8 @@ BIT(active); }; +static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data); + /* How to access `call_data` from a cf_msh3 filter */ #undef CF_CTX_CALL_DATA #define CF_CTX_CALL_DATA(cf) \ @@ -153,18 +157,26 @@ bool recv_header_complete; }; -#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) +#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \ + Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) +static void h3_stream_ctx_free(struct stream_ctx *stream) +{ + Curl_bufq_free(&stream->recvbuf); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h3_stream_ctx_free((struct stream_ctx *)stream); +} static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream) return CURLE_OK; @@ -173,25 +185,29 @@ if(!stream) return CURLE_OUT_OF_MEMORY; - H3_STREAM_LCTX(data) = stream; stream->req = ZERO_NULL; msh3_lock_initialize(&stream->recv_lock); Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); CURL_TRC_CF(data, cf, "data setup"); + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + 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 stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; if(stream) { CURL_TRC_CF(data, cf, "easy handle is done"); - Curl_bufq_free(&stream->recvbuf); - free(stream); - H3_STREAM_LCTX(data) = NULL; + Curl_hash_offt_remove(&ctx->streams, data->id); } } @@ -213,7 +229,8 @@ static void drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); unsigned char bits; (void)cf; @@ -311,7 +328,8 @@ static CURLcode write_resp_raw(struct Curl_easy *data, const void *mem, size_t memlen) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; ssize_t nwritten; @@ -337,10 +355,12 @@ const MSH3_HEADER *hd) { struct Curl_easy *data = userp; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result; (void)Request; + DEBUGF(infof(data, "[MSH3] header received, stream=%d", !!stream)); if(!stream || stream->recv_header_complete) { return; } @@ -386,7 +406,8 @@ const uint8_t *buf) { struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result; bool rv = FALSE; @@ -425,7 +446,8 @@ bool aborted, uint64_t error) { struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)Request; if(!stream) @@ -444,7 +466,8 @@ void *IfContext) { struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(!stream) return; @@ -456,7 +479,8 @@ void *IfContext, void *SendContext) { struct Curl_easy *data = IfContext; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(!stream) return; (void)Request; @@ -468,7 +492,8 @@ struct Curl_easy *data, CURLcode *err) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nread = -1; if(!stream) { @@ -501,7 +526,8 @@ static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); /* we have no indication from msh3 when it would be a good time * to juggle the connection again. So, we compromise by calling @@ -518,17 +544,17 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nread = -1; struct cf_call_data save; - (void)cf; + CURL_TRC_CF(data, cf, "cf_recv(len=%zu), stream=%d", len, !!stream); if(!stream) { *err = CURLE_RECV_ERROR; return -1; } CF_DATA_SAVE(save, cf, data); - CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len); msh3_lock_acquire(&stream->recv_lock); @@ -570,7 +596,7 @@ const void *buf, size_t len, CURLcode *err) { struct cf_msh3_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct h1_req_parser h1; struct dynhds h2_headers; MSH3_HEADER *nva = NULL; @@ -682,7 +708,7 @@ struct easy_pollset *ps) { struct cf_msh3_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; CF_DATA_SAVE(save, cf, data); @@ -701,7 +727,8 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; bool pending = FALSE; @@ -722,23 +749,6 @@ return pending; } -static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_msh3_ctx *ctx = cf->ctx; - - /* use this socket from now on */ - cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL]; - /* the first socket info gets set at conn and data */ - if(cf->sockindex == FIRSTSOCKET) { - cf->conn->remote_addr = &ctx->addr; - #ifdef ENABLE_IPV6 - cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; - #endif - Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); - } - ctx->active = TRUE; -} - static CURLcode h3_data_pause(struct Curl_cfilter *cf, struct Curl_easy *data, bool pause) @@ -754,7 +764,8 @@ struct Curl_easy *data, int event, int arg1, void *arg2) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_msh3_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; CURLcode result = CURLE_OK; @@ -785,10 +796,6 @@ } } break; - case CF_CTRL_CONN_INFO_UPDATE: - CURL_TRC_CF(data, cf, "req: update info"); - cf_msh3_active(cf, data); - break; default: break; } @@ -806,6 +813,7 @@ CURLcode result; bool verify; + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); conn_config = Curl_ssl_cf_get_primary_config(cf); if(!conn_config) return CURLE_FAILED_INIT; @@ -932,6 +940,7 @@ MsH3ApiClose(ctx->api); ctx->api = NULL; } + Curl_hash_destroy(&ctx->streams); if(ctx->active) { /* We share our socket at cf->conn->sock[cf->sockindex] when active. @@ -1040,6 +1049,20 @@ cf_msh3_query, }; +static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data) +{ + if(data && data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[FIRSTSOCKET]; + while(cf) { + if(cf->cft == &Curl_cft_http3) + return cf->ctx; + cf = cf->next; + } + } + DEBUGF(infof(data, "no filter context found")); + return NULL; +} + CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn,
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index a26b3e4..0d9d87f 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -44,6 +44,7 @@ #endif #include "urldata.h" +#include "hash.h" #include "sendf.h" #include "strdup.h" #include "rand.h" @@ -58,6 +59,7 @@ #include "http1.h" #include "select.h" #include "inet_pton.h" +#include "transfer.h" #include "vquic.h" #include "vquic_int.h" #include "vquic-tls.h" @@ -112,7 +114,7 @@ struct cf_ngtcp2_ctx { struct cf_quic_ctx q; struct ssl_peer peer; - struct quic_tls_ctx tls; + struct curl_tls_ctx tls; ngtcp2_path connected_path; ngtcp2_conn *qconn; ngtcp2_cid dcid; @@ -129,9 +131,14 @@ struct curltime handshake_at; /* time connect handshake finished */ struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct dynbuf scratch; /* temp buffer for header construction */ + struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ size_t max_stream_window; /* max flow window for one stream */ uint64_t max_idle_ms; /* max idle time for QUIC connection */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ int qlogfd; + BIT(conn_closed); /* connection is closed */ }; /* How to access `call_data` from a cf_ngtcp2 filter */ @@ -139,20 +146,27 @@ #define CF_CTX_CALL_DATA(cf) \ ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data +struct pkt_io_ctx; +static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx); +static CURLcode cf_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct pkt_io_ctx *pktx); + /** * All about the H3 internals of a stream */ struct h3_stream_ctx { - int64_t id; /* HTTP/3 protocol identifier */ + curl_int64_t id; /* HTTP/3 protocol identifier */ struct bufq sendbuf; /* h3 request body */ - struct bufq recvbuf; /* h3 response body */ struct h1_req_parser h1; /* h1 request parsing */ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ size_t upload_blocked_len; /* the amount written last and EGAINed */ - size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ - uint64_t error3; /* HTTP/3 stream error code */ + curl_uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ int status_code; /* HTTP status code */ + CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ bool resp_hds_complete; /* we have a complete, final response */ bool closed; /* TRUE on stream close */ bool reset; /* TRUE on stream reset */ @@ -160,18 +174,29 @@ BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; -#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) +#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) +#define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\ + Curl_hash_offt_get(&(ctx)->streams, (id)))) + +static void h3_stream_ctx_free(struct h3_stream_ctx *stream) +{ + Curl_bufq_free(&stream->sendbuf); + Curl_h1_req_parse_free(&stream->h1); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + 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_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(!data || !data->req.p.http) { failf(data, "initialization failure, transfer not http initialized"); @@ -190,67 +215,87 @@ 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); - H3_STREAM_LCTX(data) = stream; + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; } +static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + DEBUGASSERT(data); + DEBUGASSERT(stream); + if(!stream->closed && ctx->qconn && ctx->h3conn) { + CURLcode result; + + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + result = cf_progress_egress(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cancel stream -> %d", + stream->id, result); + } +} + static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); - if(ctx->h3conn && !stream->closed) { - nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream->id); - nghttp3_conn_close_stream(ctx->h3conn, stream->id, - NGHTTP3_H3_REQUEST_CANCELLED); - nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); - ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); - stream->closed = TRUE; - } - - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - Curl_h1_req_parse_free(&stream->h1); - free(stream); - H3_STREAM_LCTX(data) = NULL; + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] easy handle is done", + stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + Curl_hash_offt_remove(&ctx->streams, data->id); } } static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, struct Curl_easy *data, - int64_t stream_id) + int64_t stream_id, + struct h3_stream_ctx **pstream) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *sdata; + struct h3_stream_ctx *stream; (void)cf; - if(H3_STREAM_ID(data) == stream_id) { + stream = H3_STREAM_CTX(ctx, data); + if(stream && stream->id == stream_id) { + *pstream = stream; return data; } else { DEBUGASSERT(data->multi); for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { - if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) { + if(sdata->conn != data->conn) + continue; + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->id == stream_id) { + *pstream = stream; return sdata; } } } + *pstream = NULL; return NULL; } static void h3_drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); unsigned char bits; (void)cf; @@ -296,12 +341,6 @@ pktx_update_time(pktx, cf); } -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, uint64_t datalen, void *user_data, void *stream_user_data); @@ -387,43 +426,60 @@ return 0; } -static void report_consumed_data(struct Curl_cfilter *cf, - struct Curl_easy *data, - size_t consumed) -{ - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - struct cf_ngtcp2_ctx *ctx = cf->ctx; +static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data); - if(!stream) - return; - /* the HTTP/1.1 response headers are written to the buffer, but - * consuming those does not count against flow control. */ - if(stream->recv_buf_nonflow) { - if(consumed >= stream->recv_buf_nonflow) { - consumed -= stream->recv_buf_nonflow; - stream->recv_buf_nonflow = 0; +static bool cf_ngtcp2_err_is_fatal(int code) +{ + return (NGTCP2_ERR_FATAL >= code) || + (NGTCP2_ERR_DROP_CONN == code) || + (NGTCP2_ERR_IDLE_CLOSE == code); +} + +static void cf_ngtcp2_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + if(NGTCP2_ERR_CRYPTO == code) { + ngtcp2_ccerr_set_tls_alert(&ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), + NULL, 0); } else { - stream->recv_buf_nonflow -= consumed; - consumed = 0; + ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); } } - if(consumed > 0) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA", - stream->id, consumed); - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, - consumed); - ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + if(cf_ngtcp2_err_is_fatal(code)) + cf_ngtcp2_conn_close(cf, data); +} + +static bool cf_ngtcp2_h3_err_is_fatal(int code) +{ + return (NGHTTP3_ERR_FATAL >= code) || + (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); +} + +static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + ngtcp2_ccerr_set_application_error(&ctx->last_error, + nghttp3_err_infer_quic_app_error_code(code), NULL, 0); } + if(cf_ngtcp2_h3_err_is_fatal(code)) + cf_ngtcp2_conn_close(cf, data); } static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, + int64_t sid, uint64_t offset, const uint8_t *buf, size_t buflen, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; nghttp3_ssize nconsumed; int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; struct Curl_easy *data = stream_user_data; @@ -432,18 +488,18 @@ nconsumed = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed); + if(!data) + data = CF_DATA_CURRENT(cf); + if(data) + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read_stream(len=%zu) -> %zd", + stream_id, buflen, nconsumed); if(nconsumed < 0) { - if(!data) { - struct Curl_easy *cdata = CF_DATA_CURRENT(cf); - CURL_TRC_CF(cdata, cf, "[%" PRId64 "] nghttp3 error on stream not " - "used by us, ignored", stream_id); - return 0; + struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id); + if(data && stream) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] error on known stream, " + "reset=%d, closed=%d", + stream_id, stream->reset, stream->closed); } - ngtcp2_ccerr_set_application_error( - &ctx->last_error, - nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -479,41 +535,45 @@ } static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream3_id, uint64_t app_error_code, + int64_t sid, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; - struct Curl_easy *data = stream_user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + curl_int64_t stream_id = (curl_int64_t)sid; int rv; (void)tconn; - (void)data; /* stream is closed... */ + if(!data) + data = CF_DATA_CURRENT(cf); + if(!data) + return NGTCP2_ERR_CALLBACK_FAILURE; if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { app_error_code = NGHTTP3_H3_NO_ERROR; } - rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id, - app_error_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" - PRIu64 ") -> %d", stream3_id, app_error_code, rv); + rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] quic close(app_error=%" + CURL_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, + rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - ngtcp2_ccerr_set_application_error( - &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); + cf_ngtcp2_h3_err_set(cf, data, rv); return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } -static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid, uint64_t final_size, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; struct Curl_easy *data = stream_user_data; int rv; (void)tconn; @@ -522,7 +582,7 @@ (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -553,19 +613,26 @@ uint64_t max_streams, void *user_data) { - (void)tconn; - (void)max_streams; - (void)user_data; + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + (void)tconn; + ctx->max_bidi_streams = max_streams; + if(data) + CURL_TRC_CF(data, cf, "max bidi streams now %" CURL_PRIu64 + ", used %" CURL_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, + (curl_uint64_t)ctx->used_bidi_streams); return 0; } -static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, +static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid, uint64_t max_data, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; struct Curl_easy *data = CF_DATA_CURRENT(cf); struct Curl_easy *s_data; struct h3_stream_ctx *stream; @@ -578,12 +645,12 @@ if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } - s_data = get_stream_easy(cf, data, stream_id); - stream = H3_STREAM_CTX(s_data); - if(stream && stream->quic_flow_blocked) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] unblock quic flow", stream_id); + s_data = get_stream_easy(cf, data, stream_id, &stream); + if(s_data && stream && stream->quic_flow_blocked) { + CURL_TRC_CF(s_data, cf, "[%" CURL_PRId64 "] unblock quic flow", + stream_id); stream->quic_flow_blocked = FALSE; - h3_drain_stream(cf, data); + h3_drain_stream(cf, s_data); } return 0; } @@ -713,7 +780,7 @@ if(rv) { failf(data, "ngtcp2_conn_handle_expiry returned error: %s", ngtcp2_strerror(rv)); - ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); + cf_ngtcp2_err_set(cf, data, rv); return CURLE_SEND_ERROR; } result = cf_progress_ingress(cf, data, pktx); @@ -749,7 +816,7 @@ Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(want_recv || want_send) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; bool c_exhaust, s_exhaust; @@ -767,13 +834,15 @@ } } -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + curl_int64_t stream_id = (curl_int64_t)sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; (void)stream_id; @@ -782,63 +851,60 @@ return 0; stream->closed = TRUE; - stream->error3 = app_error_code; + stream->error3 = (curl_uint64_t)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 %" PRId64, + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, stream->id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->id); } h3_drain_stream(cf, data); return 0; } -/* - * write_resp_raw() copies response data in raw format to the `data`'s - * receive buffer. If not enough space is available, it appends to the - * `data`'s overflow buffer. - */ -static CURLcode write_resp_raw(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t memlen, - bool flow) +static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t blen, bool eos) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; - ssize_t nwritten; - (void)cf; - if(!stream) { - return CURLE_RECV_ERROR; + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); + if(stream->xfer_result) + CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu " + "bytes of headers", stream->id, stream->xfer_result, blen); } - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - if(nwritten < 0) { - return result; - } +} - if(!flow) - stream->recv_buf_nonflow += (size_t)nwritten; +static void h3_xfer_write_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t blen, bool eos) +{ - if((size_t)nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this very reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; + /* If we already encountered an error, skip further writes */ + if(!stream->xfer_result) { + stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); + /* If the transfer write is errored, we do not want any more data */ + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu bytes " + "of data", stream->id, stream->xfer_result, blen); + } } - return result; } static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t buflen, + const uint8_t *buf, size_t blen, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; (void)stream3_id; @@ -846,14 +912,14 @@ if(!stream) return NGHTTP3_ERR_CALLBACK_FAILURE; - result = write_resp_raw(cf, data, buf, buflen, TRUE); - if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d", - stream->id, buflen, result); - return NGHTTP3_ERR_CALLBACK_FAILURE; + h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE); + if(blen) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] ACK %zu bytes of DATA", + stream->id, blen); + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen); + ngtcp2_conn_extend_max_offset(ctx->qconn, blen); } - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen); - h3_drain_stream(cf, data); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu", stream->id, blen); return 0; } @@ -873,13 +939,14 @@ return 0; } -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, int fin, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); - CURLcode result = CURLE_OK; + curl_int64_t stream_id = (curl_int64_t)sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; (void)stream_id; (void)fin; @@ -888,12 +955,9 @@ if(!stream) return 0; /* add a CRLF only if we've received some headers */ - result = write_resp_raw(cf, data, "\r\n", 2, FALSE); - if(result) { - return -1; - } + h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); - CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -902,16 +966,18 @@ return 0; } -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, 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_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; (void)conn; (void)stream_id; @@ -924,42 +990,45 @@ 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 = 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); + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", + stream_id, Curl_dyn_ptr(&ctx->scratch)); if(result) { return -1; } } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" CURL_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; - } + Curl_dyn_reset(&ctx->scratch); + result = Curl_dyn_addn(&ctx->scratch, + (const char *)h3name.base, h3name.len); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST(": ")); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch), + Curl_dyn_len(&ctx->scratch), FALSE); } return 0; } @@ -983,11 +1052,12 @@ return 0; } -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; + curl_int64_t stream_id = (curl_int64_t)sid; struct Curl_easy *data = stream_user_data; int rv; (void)conn; @@ -995,7 +1065,7 @@ rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1091,14 +1161,14 @@ (void)cf; if(stream->reset) { failf(data, - "HTTP/3 stream %" PRId64 " reset by server", stream->id); - *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; + "HTTP/3 stream %" CURL_PRId64 " reset by server", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" - " all response header fields, treated as error", + "HTTP/3 stream %" CURL_PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", stream->id); *err = CURLE_HTTP3; goto out; @@ -1112,15 +1182,16 @@ /* incoming data frames on the h3 stream */ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) + char *buf, size_t blen, CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nread = -1; struct cf_call_data save; struct pkt_io_ctx pktx; (void)ctx; + (void)buf; CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); @@ -1131,51 +1202,30 @@ pktx_init(&pktx, cf, data); - if(!stream) { + if(!stream || ctx->conn_closed) { *err = CURLE_RECV_ERROR; goto out; } - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err); - goto out; - } - report_consumed_data(cf, data, nread); - } - if(cf_progress_ingress(cf, data, &pktx)) { *err = CURLE_RECV_ERROR; nread = -1; goto out; } - /* recvbuf had nothing before, maybe after progressing ingress? */ - if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err); - goto out; - } - report_consumed_data(cf, data, nread); - } - - if(nread > 0) { - h3_drain_stream(cf, data); - } - else { - if(stream->closed) { - nread = recv_closed_stream(cf, data, stream, err); - goto out; - } - *err = CURLE_AGAIN; + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + *err = stream->xfer_result; nread = -1; + goto out; } + else if(stream->closed) { + nread = recv_closed_stream(cf, data, stream, err); + goto out; + } + *err = CURLE_AGAIN; + nread = -1; out: if(cf_progress_egress(cf, data, &pktx)) { @@ -1189,8 +1239,8 @@ nread = -1; } } - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nread, *err); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", + stream? stream->id : -1, blen, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } @@ -1200,8 +1250,9 @@ void *stream_user_data) { 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); size_t skiplen; (void)cf; @@ -1234,8 +1285,9 @@ void *stream_user_data) { 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nwritten = 0; size_t nvecs = 0; (void)cf; @@ -1278,12 +1330,12 @@ } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", stream->id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " "%d vecs%s with %zu (buffered=%zu, left=%" CURL_FORMAT_CURL_OFF_T ")", stream->id, (int)nvecs, @@ -1304,6 +1356,7 @@ { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = NULL; + int64_t sid; struct dynhds h2_headers; size_t nheader; nghttp3_nv *nva = NULL; @@ -1318,7 +1371,7 @@ *err = h3_data_setup(cf, data); if(*err) goto out; - stream = H3_STREAM_CTX(data); + stream = H3_STREAM_CTX(ctx, data); DEBUGASSERT(stream); if(!stream) { *err = CURLE_FAILED_INIT; @@ -1359,12 +1412,15 @@ nva[i].flags = NGHTTP3_NV_FLAG_NONE; } - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, data); + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); if(rc) { failf(data, "can get bidi streams"); *err = CURLE_SEND_ERROR; + nwritten = -1; goto out; } + stream->id = (curl_int64_t)sid; + ++ctx->used_bidi_streams; switch(data->state.httpreq) { case HTTPREQ_POST: @@ -1395,12 +1451,12 @@ if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send, " "connection is closing", stream->id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", - stream->id, rc, ngtcp2_strerror(rc)); + CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send -> " + "%d (%s)", stream->id, rc, ngtcp2_strerror(rc)); break; } *err = CURLE_SEND_ERROR; @@ -1409,10 +1465,10 @@ } if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", stream->id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", stream->id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); } @@ -1428,7 +1484,7 @@ const void *buf, size_t len, CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t sent = 0; struct cf_call_data save; struct pkt_io_ctx pktx; @@ -1448,12 +1504,25 @@ } if(!stream || stream->id < 0) { + if(ctx->conn_closed) { + CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); + *err = CURLE_SEND_ERROR; + sent = -1; + goto out; + } sent = h3_stream_open(cf, data, buf, len, err); if(sent < 0) { CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); goto out; } - stream = H3_STREAM_CTX(data); + stream = H3_STREAM_CTX(ctx, data); + } + else if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + cf_ngtcp2_stream_close(cf, data, stream); + *err = stream->xfer_result; + sent = -1; + goto out; } else if(stream->upload_blocked_len) { /* the data in `buf` has already been submitted or added to the @@ -1477,21 +1546,27 @@ * 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" + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" "on closed stream with response", stream->id); *err = CURLE_OK; sent = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); *err = CURLE_HTTP3; sent = -1; goto out; } + else if(ctx->conn_closed) { + CURL_TRC_CF(data, cf, "cannot send on closed connection"); + *err = CURLE_SEND_ERROR; + sent = -1; + goto out; + } else { sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %zd, %d", stream->id, len, sent, *err); if(sent < 0) { @@ -1512,7 +1587,7 @@ * caller. Instead we EAGAIN and remember how much we have already * "written" into our various internal connection buffers. */ stream->upload_blocked_len = sent; - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu), " "%zu bytes in flight -> EGAIN", stream->id, len, stream->sendbuf_len_in_flight); *err = CURLE_AGAIN; @@ -1525,7 +1600,7 @@ *err = result; sent = -1; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->id : -1, len, sent, *err); CF_DATA_RESTORE(cf, save); return sent; @@ -1565,16 +1640,7 @@ if(rv) { CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", ngtcp2_strerror(rv), rv); - if(!ctx->last_error.error_code) { - if(rv == NGTCP2_ERR_CRYPTO) { - ngtcp2_ccerr_set_tls_alert(&ctx->last_error, - ngtcp2_conn_get_tls_alert(ctx->qconn), - NULL, 0); - } - else { - ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); - } - } + cf_ngtcp2_err_set(pktx->cf, pktx->data, rv); if(rv == NGTCP2_ERR_CRYPTO) /* this is a "TLS problem", but a failed certificate verification @@ -1593,7 +1659,6 @@ struct cf_ngtcp2_ctx *ctx = cf->ctx; struct pkt_io_ctx local_pktx; size_t pkts_chunk = 128, i; - size_t pkts_max = 10 * pkts_chunk; CURLcode result = CURLE_OK; if(!pktx) { @@ -1608,17 +1673,13 @@ if(result) return result; - for(i = 0; i < pkts_max; i += pkts_chunk) { + for(i = 0; i < 4; ++i) { + if(i) + pktx_update_time(pktx, cf); pktx->pkt_count = 0; result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, recv_pkt, pktx); - if(result) /* error */ - break; - if(pktx->pkt_count < pkts_chunk) /* got less than we could */ - break; - /* give egress a chance before we receive more */ - result = cf_progress_egress(cf, data, pktx); - if(result) /* error */ + if(result || !pktx->pkt_count) /* error or got nothing */ break; } return result; @@ -1662,9 +1723,7 @@ if(veccnt < 0) { failf(x->data, "nghttp3_conn_writev_stream returned error: %s", nghttp3_strerror((int)veccnt)); - ngtcp2_ccerr_set_application_error( - &ctx->last_error, - nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); + cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); *err = CURLE_SEND_ERROR; return -1; } @@ -1685,11 +1744,11 @@ else if(n < 0) { switch(n) { case NGTCP2_ERR_STREAM_DATA_BLOCKED: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(x->data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", - stream_id); + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] block quic flow", + (curl_int64_t)stream_id); DEBUGASSERT(stream); if(stream) stream->quic_flow_blocked = TRUE; @@ -1711,7 +1770,7 @@ DEBUGASSERT(ndatalen == -1); failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", ngtcp2_strerror((int)n)); - ngtcp2_ccerr_set_liberr(&ctx->last_error, (int)n, NULL, 0); + cf_ngtcp2_err_set(x->cf, x->data, (int)n); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; @@ -1769,7 +1828,7 @@ } /* In UDP, there is a maximum theoretical packet paload length and - * a minimum payload length that is "guarantueed" to work. + * a minimum payload length that is "guaranteed" to work. * To detect if this minimum payload can be increased, ngtcp2 sends * now and then a packet payload larger than the minimum. It that * is ACKed by the peer, both parties know that it works and @@ -1857,9 +1916,9 @@ static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - const struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)cf; - return stream && !Curl_bufq_is_empty(&stream->recvbuf); + (void)data; + return FALSE; } static CURLcode h3_data_pause(struct Curl_cfilter *cf, @@ -1899,7 +1958,7 @@ h3_data_done(cf, data); break; case CF_CTRL_DATA_DONE_SEND: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + 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); @@ -1908,7 +1967,7 @@ break; } case CF_CTRL_DATA_IDLE: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURL_TRC_CF(data, cf, "data idle"); if(stream && !stream->closed) { result = check_and_set_expiry(cf, data, NULL); @@ -1938,6 +1997,9 @@ if(ctx->qconn) ngtcp2_conn_del(ctx->qconn); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_dyn_free(&ctx->scratch); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); Curl_ssl_peer_cleanup(&ctx->peer); memset(ctx, 0, sizeof(*ctx)); @@ -1945,6 +2007,31 @@ ctx->call_data = save; } +static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(ctx && ctx->qconn && !ctx->conn_closed) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + struct pkt_io_ctx pktx; + ngtcp2_ssize rc; + + ctx->conn_closed = TRUE; + pktx_init(&pktx, cf, data); + rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "closing connection(err_type=%d, err_code=%" + CURL_PRIu64 ") -> %d", ctx->last_error.type, + (curl_uint64_t)ctx->last_error.error_code, (int)rc); + if(rc > 0) { + while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && + SOCKERRNO == EINTR); + } + } +} + static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; @@ -1952,24 +2039,10 @@ CF_DATA_SAVE(save, cf, data); if(ctx && ctx->qconn) { - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - struct pkt_io_ctx pktx; - ngtcp2_ssize rc; - - CURL_TRC_CF(data, cf, "close"); - pktx_init(&pktx, cf, data); - rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, pktx.ts); - if(rc > 0) { - while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && - SOCKERRNO == EINTR); - } - + cf_ngtcp2_conn_close(cf, data); cf_ngtcp2_ctx_clear(ctx); + CURL_TRC_CF(data, cf, "close"); } - cf->connected = FALSE; CF_DATA_RESTORE(cf, save); } @@ -1990,25 +2063,59 @@ (void)save; } -static CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data) +#ifdef USE_OPENSSL +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { + struct Curl_cfilter *cf; + struct cf_ngtcp2_ctx *ctx; + struct Curl_easy *data; + ngtcp2_crypto_conn_ref *cref; + + cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); + cf = cref? cref->user_data : NULL; + ctx = cf? cf->ctx : NULL; + data = cf? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid); + return 1; + } + return 0; +} +#endif /* USE_OPENSSL */ + +static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) +{ + struct curl_tls_ctx *ctx = user_data; (void)cf; #ifdef USE_OPENSSL #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) - if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) { + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); return CURLE_FAILED_INIT; } #else - if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) { + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); return CURLE_FAILED_INIT; } #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ + /* Enable the session cache because it's a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); + #elif defined(USE_GNUTLS) - if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); return CURLE_FAILED_INIT; } @@ -2040,18 +2147,24 @@ ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); + Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); - result = Curl_ssl_peer_init(&ctx->peer, cf); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) return result; #define H3_ALPN "\x2h3\x5h3-29" result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, H3_ALPN, sizeof(H3_ALPN) - 1, - tls_ctx_setup, &ctx->conn_ref); + tls_ctx_setup, &ctx->tls, &ctx->conn_ref); if(result) return result; +#ifdef USE_OPENSSL + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); +#endif + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); if(result) @@ -2070,8 +2183,7 @@ if(result) return result; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, - &sockaddr, NULL, NULL, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); if(!sockaddr) return CURLE_QUIC_CONNECT_ERROR; ctx->q.local_addrlen = sizeof(ctx->q.local_addr); @@ -2094,8 +2206,10 @@ if(rc) return CURLE_QUIC_CONNECT_ERROR; -#ifdef USE_GNUTLS - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session); +#ifdef USE_OPENSSL + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#elif defined(USE_GNUTLS) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); #else ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl); #endif @@ -2186,13 +2300,11 @@ #ifndef CURL_DISABLE_VERBOSE_STRINGS if(result) { - const char *r_ip = NULL; - int r_port = 0; + struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); infof(data, "QUIC connect to %s port %u failed: %s", - r_ip, r_port, curl_easy_strerror(result)); + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); } #endif if(!result && ctx->qconn) { @@ -2213,17 +2325,28 @@ switch(query) { case CF_QUERY_MAX_CONCURRENT: { - const ngtcp2_transport_params *rp; DEBUGASSERT(pres1); - CF_DATA_SAVE(save, cf, data); - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(rp) - *pres1 = (rp->initial_max_streams_bidi > INT_MAX)? - INT_MAX : (int)rp->initial_max_streams_bidi; - else /* not arrived yet? */ + /* Set after transport params arrived and continually updated + * by callback. QUIC counts the number over the lifetime of the + * connection, ever increasing. + * We count the *open* transfers plus the budget for new ones. */ + if(!ctx->qconn || ctx->conn_closed) { + *pres1 = 0; + } + else if(ctx->max_bidi_streams) { + uint64_t avail_bidi_streams = 0; + uint64_t max_streams = CONN_INUSE(cf->conn); + if(ctx->max_bidi_streams > ctx->used_bidi_streams) + avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; + max_streams += avail_bidi_streams; + *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; + } + else /* transport params not arrived yet? take our default. */ *pres1 = Curl_multi_max_concurrent_streams(data->multi); - CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); + CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + "MAX_CONCURRENT -> %d (%zu in use)", + cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); CF_DATA_RESTORE(cf, save); return CURLE_OK; } @@ -2264,9 +2387,9 @@ const ngtcp2_transport_params *rp; struct cf_call_data save; - CF_DATA_SAVE(save, cf, data); + CF_DATA_SAVE(save, cf, data); *input_pending = FALSE; - if(!ctx->qconn) + if(!ctx->qconn || ctx->conn_closed) goto out; /* Both sides of the QUIC connection announce they max idle times in
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.c b/Utilities/cmcurl/lib/vquic/curl_osslq.c index c499a00..8b9e889 100644 --- a/Utilities/cmcurl/lib/vquic/curl_osslq.c +++ b/Utilities/cmcurl/lib/vquic/curl_osslq.c
@@ -32,6 +32,7 @@ #include <nghttp3/nghttp3.h> #include "urldata.h" +#include "hash.h" #include "sendf.h" #include "strdup.h" #include "rand.h" @@ -67,7 +68,7 @@ * 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 windows +/* The pool keeps spares around and half of a full stream window * seems good. More does not seem to improve performance. * The benefit of the pool is that stream buffer to not keep * spares. So memory consumption goes down when streams run empty, @@ -100,7 +101,7 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, struct Curl_easy *data); -static const char *SSL_ERROR_to_str(int err) +static const char *osslq_SSL_ERROR_to_str(int err) { switch(err) { case SSL_ERROR_NONE: @@ -139,7 +140,7 @@ } /* Return error string for last OpenSSL error */ -static char *ossl_strerror(unsigned long error, char *buf, size_t size) +static char *osslq_strerror(unsigned long error, char *buf, size_t size) { DEBUGASSERT(size); *buf = '\0'; @@ -182,7 +183,7 @@ result = CURLE_OK; break; } -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 case AF_INET6: { struct sockaddr_in6 * const sin = (struct sockaddr_in6 * const)(void *)&addr->sa_addr; @@ -192,7 +193,7 @@ result = CURLE_OK; break; } -#endif /* ENABLE_IPV6 */ +#endif /* USE_IPV6 */ default: /* sunsupported */ DEBUGASSERT(0); @@ -210,7 +211,7 @@ /* QUIC stream (not necessarily H3) */ struct cf_osslq_stream { - int64_t id; + curl_int64_t id; SSL *ssl; struct bufq recvbuf; /* QUIC war data recv buffer */ BIT(recvd_eos); @@ -281,7 +282,7 @@ struct cf_osslq_ctx { struct cf_quic_ctx q; struct ssl_peer peer; - struct quic_tls_ctx tls; + 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 */ @@ -289,6 +290,7 @@ struct curltime first_byte_at; /* when first byte was recvd */ struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ size_t max_stream_window; /* max flow window for one stream */ uint64_t max_idle_ms; /* max idle time for QUIC connection */ BIT(got_first_byte); /* if first byte was received */ @@ -306,6 +308,8 @@ Curl_vquic_tls_cleanup(&ctx->tls); vquic_ctx_free(&ctx->q); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); Curl_ssl_peer_cleanup(&ctx->peer); memset(ctx, 0, sizeof(*ctx)); @@ -318,7 +322,7 @@ struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->tls.ssl) { + if(ctx && ctx->tls.ossl.ssl) { /* TODO: send connection close */ CURL_TRC_CF(data, cf, "cf_osslq_close()"); cf_osslq_ctx_clear(ctx); @@ -355,8 +359,8 @@ if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) { /* rejected, we are full */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting additional remote stream", - stream_id); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] rejecting remote stream", + (curl_int64_t)stream_id); SSL_free(stream_ssl); return CURLE_FAILED_INIT; } @@ -366,13 +370,13 @@ 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 new remote uni stream", - stream_id); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] accepted remote uni stream", + (curl_int64_t)stream_id); break; } default: - CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting remote non-uni-read" - " stream", stream_id); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reject remote non-uni-read" + " stream", (curl_int64_t)stream_id); SSL_free(stream_ssl); return CURLE_FAILED_INIT; } @@ -381,8 +385,8 @@ } static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, - struct Curl_easy *data, - int detail, CURLcode def_result) + struct Curl_easy *data, + int detail, CURLcode def_result) { struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = def_result; @@ -403,7 +407,7 @@ (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { result = CURLE_PEER_FAILED_VERIFICATION; - lerr = SSL_get_verify_result(ctx->tls.ssl); + lerr = SSL_get_verify_result(ctx->tls.ossl.ssl); if(lerr != X509_V_OK) { ssl_config->certverifyresult = lerr; msnprintf(ebuf, sizeof(ebuf), @@ -421,17 +425,17 @@ /* If client certificate is required, communicate the error to client */ result = CURLE_SSL_CLIENTCERT; - ossl_strerror(errdetail, ebuf, sizeof(ebuf)); + 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 connectin has been shut down"; + err_descr = "QUIC connection has been shut down"; result = def_result; } else { result = def_result; - ossl_strerror(errdetail, ebuf, sizeof(ebuf)); + osslq_strerror(errdetail, ebuf, sizeof(ebuf)); } /* detail is already set to the SSL error above */ @@ -443,16 +447,14 @@ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { char extramsg[80]=""; int sockerr = SOCKERRNO; - const char *r_ip = NULL; - int r_port = 0; + struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); if(sockerr && detail == SSL_ERROR_SYSCALL) Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, "QUIC connect: %s in connection to %s:%d (%s)", - extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - ctx->peer.dispname, r_port, r_ip); + extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail), + ctx->peer.dispname, ip.remote_port, ip.remote_ip); } else { /* Could be a CERT problem */ @@ -484,7 +486,7 @@ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ size_t upload_blocked_len; /* the amount written last and EGAINed */ size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ - uint64_t error3; /* HTTP/3 stream error code */ + curl_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 */ @@ -495,18 +497,29 @@ BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; -#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->s.id : -2) +#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +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); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(!data || !data->req.p.http) { failf(data, "initialization failure, transfer not http initialized"); @@ -532,18 +545,23 @@ stream->recv_buf_nonflow = 0; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - H3_STREAM_LCTX(data) = stream; + if(!Curl_hash_offt_set(&ctx->streams, data->id, 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->s.id); + CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] easy handle is done", + stream->s.id); if(ctx->h3.conn && !stream->closed) { nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id, @@ -552,12 +570,7 @@ stream->closed = TRUE; } - cf_osslq_stream_cleanup(&stream->s); - Curl_bufq_free(&stream->sendbuf); - Curl_bufq_free(&stream->recvbuf); - Curl_h1_req_parse_free(&stream->h1); - free(stream); - H3_STREAM_LCTX(data) = NULL; + Curl_hash_offt_remove(&ctx->streams, data->id); } } @@ -566,7 +579,7 @@ int64_t stream_id) { struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct Curl_easy *sdata; if(stream && stream->s.id == stream_id) { @@ -584,9 +597,11 @@ else { DEBUGASSERT(data->multi); for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { - if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) { - stream = H3_STREAM_CTX(sdata); - return stream? &stream->s : NULL; + if(sdata->conn != data->conn) + continue; + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->s.id == stream_id) { + return &stream->s; } } } @@ -596,7 +611,8 @@ static void h3_drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); unsigned char bits; (void)cf; @@ -626,8 +642,9 @@ 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; (void)stream_id; @@ -640,11 +657,11 @@ if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64, + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, stream->s.id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->s.id); } h3_drain_stream(cf, data); return 0; @@ -660,7 +677,8 @@ const void *mem, size_t memlen, bool flow) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_osslq_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; ssize_t nwritten; @@ -690,8 +708,9 @@ 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result; (void)conn; @@ -702,12 +721,12 @@ result = write_resp_raw(cf, data, buf, buflen, TRUE); if(result) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d", + CURL_TRC_CF(data, cf, "[%" CURL_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=%zd", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, total=%zd", stream->s.id, buflen, stream->download_recvd); h3_drain_stream(cf, data); return 0; @@ -718,27 +737,30 @@ 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(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", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] deferred consume %zu bytes", stream->s.id, consumed); return 0; } -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, 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; + curl_int64_t stream_id = sid; + 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; (void)conn; (void)stream_id; @@ -760,7 +782,7 @@ return -1; ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", stream->status_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line); result = write_resp_raw(cf, data, line, ncopy, FALSE); if(result) { return -1; @@ -768,7 +790,7 @@ } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" CURL_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); @@ -791,12 +813,14 @@ return 0; } -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, 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(data); + curl_int64_t stream_id = sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; (void)conn; (void)stream_id; @@ -811,7 +835,7 @@ return -1; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -820,30 +844,34 @@ return 0; } -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, +static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t sid, 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(data); + curl_int64_t stream_id = sid; + 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); + CURL_TRC_CF(data, cf, "[%" CURL_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, +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, 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(data); + curl_int64_t stream_id = sid; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); int rv; (void)conn; @@ -851,7 +879,7 @@ 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); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); if(!rv) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -866,8 +894,9 @@ 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nwritten = 0; size_t nvecs = 0; (void)cf; @@ -910,12 +939,12 @@ } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", stream->s.id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " "%d vecs%s with %zu (buffered=%zu, left=%" CURL_FORMAT_CURL_OFF_T ")", stream->s.id, (int)nvecs, @@ -930,8 +959,9 @@ 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(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); size_t skiplen; (void)cf; @@ -976,7 +1006,7 @@ }; static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn, - void *user_data) + void *user_data) { struct cf_osslq_h3conn *h3 = &ctx->h3; CURLcode result; @@ -1039,20 +1069,20 @@ CURLcode result; int rv; const struct Curl_sockaddr_ex *peer_addr = NULL; - int peer_port; BIO *bio = NULL; BIO_ADDR *baddr = NULL; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); - result = Curl_ssl_peer_init(&ctx->peer, cf); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) goto out; #define H3_ALPN "\x2h3" result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, H3_ALPN, sizeof(H3_ALPN) - 1, - NULL, NULL); + NULL, NULL, NULL); if(result) goto out; @@ -1061,8 +1091,7 @@ goto out; result = CURLE_QUIC_CONNECT_ERROR; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, - &peer_addr, NULL, &peer_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL); if(!peer_addr) goto out; @@ -1078,30 +1107,54 @@ 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.ssl, baddr)) { + 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.ssl, 0)) { + 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.ssl, bio, bio); +#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT + /* Added in OpenSSL v3.3.x */ + if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl, + SSL_VALUE_QUIC_IDLE_TIMEOUT, + CURL_QUIC_MAX_IDLE_MS)) { + CURL_TRC_CF(data, cf, "error setting idle timeout, "); + result = CURLE_FAILED_INIT; + goto out; + } +#endif + + SSL_set_bio(ctx->tls.ossl.ssl, bio, bio); bio = NULL; - SSL_set_connect_state(ctx->tls.ssl); - SSL_set_incoming_stream_policy(ctx->tls.ssl, + SSL_set_connect_state(ctx->tls.ossl.ssl); + SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl, SSL_INCOMING_STREAM_POLICY_ACCEPT, 0); /* setup the H3 things on top of the QUIC connection */ - result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf); + result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf); out: if(bio) @@ -1135,7 +1188,7 @@ return -1; } else if(detail == SSL_ERROR_ZERO_RETURN) { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS", + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> EOS", x->s->id); x->s->recvd_eos = TRUE; return 0; @@ -1144,9 +1197,9 @@ SSL_STREAM_STATE_RESET_REMOTE) { uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, " - "rv=%d, app_err=%" PRIu64, - x->s->id, rv, app_error_code); + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> RESET, " + "rv=%d, app_err=%" CURL_PRIu64, + x->s->id, rv, (curl_uint64_t)app_error_code); if(app_error_code != NGHTTP3_H3_NO_ERROR) { x->s->reset = TRUE; } @@ -1158,10 +1211,6 @@ return -1; } } - else { - /* CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> %zu bytes", - x->s->id, nread); */ - } return (ssize_t)nread; } @@ -1205,7 +1254,7 @@ 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 " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forward %zu bytes " "to nghttp3 -> %zd", s->id, blen, nread); if(nread < 0) { failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", @@ -1244,7 +1293,7 @@ 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", + CURL_TRC_CF(data, cf, "[%" CURL_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", @@ -1257,7 +1306,7 @@ } out: if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_osslq_stream_recv -> %d", s->id, result); return result; } @@ -1268,22 +1317,23 @@ struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - if(!ctx->tls.ssl) + 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.ssl, SSL_ACCEPT_STREAM_NO_BLOCK); + SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl, + SSL_ACCEPT_STREAM_NO_BLOCK); if(!snew) break; (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); } - if(!SSL_handle_events(ctx->tls.ssl)) { - int detail = SSL_get_error(ctx->tls.ssl, 0); + 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); } @@ -1303,7 +1353,7 @@ DEBUGASSERT(data->multi); for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) { - stream = H3_STREAM_CTX(sdata); + stream = H3_STREAM_CTX(ctx, sdata); if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) { result = cf_osslq_stream_recv(&stream->s, cf, sdata); @@ -1330,7 +1380,7 @@ if(ctx->h3.conn) { for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { if(sdata->conn == data->conn) { - stream = H3_STREAM_CTX(sdata); + stream = H3_STREAM_CTX(ctx, sdata); if(stream && stream->s.ssl && stream->s.send_blocked && !SSL_want_write(stream->s.ssl)) { nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); @@ -1350,7 +1400,7 @@ struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - if(!ctx->tls.ssl || !ctx->h3.conn) + if(!ctx->tls.ossl.ssl || !ctx->h3.conn) goto out; for(;;) { @@ -1361,7 +1411,7 @@ size_t written; int eos, ok, rv; size_t total_len, acked_len = 0; - bool blocked = FALSE; + bool blocked = FALSE, eos_written = FALSE; n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos, vec, ARRAYSIZE(vec)); @@ -1379,8 +1429,8 @@ /* 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); + failf(data, "nghttp3_conn_writev_stream gave unknown stream %" + CURL_PRId64, (curl_int64_t)stream_id); result = CURLE_SEND_ERROR; goto out; } @@ -1392,14 +1442,24 @@ for(i = 0; (i < n) && !blocked; ++i) { /* Without stream->s.ssl, we closed that already, so * pretend the write did succeed. */ +#ifdef SSL_WRITE_FLAG_CONCLUDE + /* Since OpenSSL v3.3.x, on last chunk set EOS if needed */ + uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0; + written = vec[i].len; + ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags, + &written); + if(ok && flags & SSL_WRITE_FLAG_CONCLUDE) + eos_written = TRUE; +#else written = vec[i].len; ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len, &written); +#endif if(ok) { /* As OpenSSL buffers the data, we count this as acknowledged * from nghttp3's point of view */ - CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC ok", - s->id, vec[i].len); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send %zu bytes to QUIC ok", + s->id, vec[i].len); acked_len += vec[i].len; } else { @@ -1408,16 +1468,16 @@ 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); + CURL_TRC_CF(data, cf, "[%"CURL_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", + failf(data, "[%"CURL_PRId64 "] send %zu bytes to QUIC, SSL error %d", s->id, vec[i].len, detail); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); + result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); goto out; } } @@ -1426,6 +1486,7 @@ 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_now(); 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\n", @@ -1440,13 +1501,13 @@ result = CURLE_SEND_ERROR; goto out; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forwarded %zu/%zu h3 bytes " "to QUIC, eos=%d", s->id, acked_len, total_len, eos); } - if(eos && !s->send_blocked) { + 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); + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] closing QUIC stream", s->id); SSL_stream_conclude(s->ssl, 0); } } @@ -1462,7 +1523,7 @@ struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - if(!ctx->tls.ssl) + if(!ctx->tls.ossl.ssl) goto out; ERR_clear_error(); @@ -1470,8 +1531,8 @@ if(result) goto out; - if(!SSL_handle_events(ctx->tls.ssl)) { - int detail = SSL_get_error(ctx->tls.ssl, 0); + 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); } @@ -1491,8 +1552,8 @@ timediff_t timeoutms; int is_infinite = TRUE; - if(ctx->tls.ssl && - SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) && + 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 */ @@ -1503,7 +1564,7 @@ result = cf_progress_egress(cf, data); if(result) goto out; - if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) { + if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) { timeoutms = curlx_tvtoms(&tv); } } @@ -1548,7 +1609,7 @@ goto out; } - if(!ctx->tls.ssl) { + if(!ctx->tls.ossl.ssl) { ctx->started_at = now; result = cf_osslq_ctx_start(cf, data); if(result) @@ -1564,11 +1625,12 @@ } ERR_clear_error(); - err = SSL_do_handshake(ctx->tls.ssl); + err = SSL_do_handshake(ctx->tls.ossl.ssl); if(err == 1) { /* connected */ ctx->handshake_at = now; + ctx->q.last_io = now; CURL_TRC_CF(data, cf, "handshake complete after %dms", (int)Curl_timediff(now, ctx->started_at)); result = cf_osslq_verify_peer(cf, data); @@ -1581,18 +1643,21 @@ } } else { - int detail = SSL_get_error(ctx->tls.ssl, err); + int detail = SSL_get_error(ctx->tls.ossl.ssl, err); switch(detail) { case SSL_ERROR_WANT_READ: + ctx->q.last_io = now; CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); goto out; case SSL_ERROR_WANT_WRITE: + ctx->q.last_io = now; 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 = now; CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC"); result = CURLE_OK; goto out; @@ -1609,7 +1674,8 @@ } out: - if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) { + 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 @@ -1619,13 +1685,11 @@ #ifndef CURL_DISABLE_VERBOSE_STRINGS if(result) { - const char *r_ip = NULL; - int r_port = 0; + struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); infof(data, "QUIC connect to %s port %u failed: %s", - r_ip, r_port, curl_easy_strerror(result)); + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); } #endif if(!result) @@ -1657,7 +1721,7 @@ *err = h3_data_setup(cf, data); if(*err) goto out; - stream = H3_STREAM_CTX(data); + stream = H3_STREAM_CTX(ctx, data); DEBUGASSERT(stream); if(!stream) { *err = CURLE_FAILED_INIT; @@ -1699,7 +1763,7 @@ } DEBUGASSERT(stream->s.id == -1); - *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0, + *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, &ctx->stream_bufcp, data); if(*err) { failf(data, "can't get bidi streams"); @@ -1736,11 +1800,11 @@ if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64"] failed to send, " "connection is closing", stream->s.id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", + CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64 "] failed to send -> %d (%s)", stream->s.id, rc, nghttp3_strerror(rc)); break; } @@ -1750,10 +1814,11 @@ } if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", stream->s.id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->s.id, + infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", + stream->s.id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); } @@ -1769,14 +1834,14 @@ const void *buf, size_t len, CURLcode *err) { struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; ssize_t nwritten; CURLcode result; CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->tls.ssl); + DEBUGASSERT(ctx->tls.ossl.ssl); DEBUGASSERT(ctx->h3.conn); *err = CURLE_OK; @@ -1800,7 +1865,7 @@ CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); goto out; } - stream = H3_STREAM_CTX(data); + stream = H3_STREAM_CTX(ctx, data); } else if(stream->upload_blocked_len) { /* the data in `buf` has already been submitted or added to the @@ -1824,13 +1889,13 @@ * 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" + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" "on closed stream with response", stream->s.id); *err = CURLE_OK; nwritten = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->s.id, len); *err = CURLE_HTTP3; nwritten = -1; @@ -1838,7 +1903,7 @@ } else { nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %zd, %d", stream->s.id, len, nwritten, *err); if(nwritten < 0) { @@ -1859,7 +1924,7 @@ * caller. Instead we EAGAIN and remember how much we have already * "written" into our various internal connection buffers. */ stream->upload_blocked_len = nwritten; - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu), " "%zu bytes in flight -> EGAIN", stream->s.id, len, stream->sendbuf_len_in_flight); *err = CURLE_AGAIN; @@ -1868,7 +1933,7 @@ out: result = check_and_set_expiry(cf, data); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->s.id : -1, len, nwritten, *err); CF_DATA_RESTORE(cf, save); return nwritten; @@ -1884,13 +1949,15 @@ (void)cf; if(stream->reset) { failf(data, - "HTTP/3 stream %" PRId64 " reset by server", stream->s.id); - *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; + "HTTP/3 stream %" CURL_PRId64 " reset by server", + stream->s.id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" + "HTTP/3 stream %" CURL_PRId64 + " was closed cleanly, but before getting" " all response header fields, treated as error", stream->s.id); *err = CURLE_HTTP3; @@ -1907,7 +1974,7 @@ char *buf, size_t len, CURLcode *err) { struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nread = -1; struct cf_call_data save; CURLcode result; @@ -1916,7 +1983,7 @@ CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx); - DEBUGASSERT(ctx->tls.ssl); + DEBUGASSERT(ctx->tls.ossl.ssl); DEBUGASSERT(ctx->h3.conn); *err = CURLE_OK; @@ -1929,7 +1996,7 @@ nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->s.id, len, nread, *err); goto out; } @@ -1947,7 +2014,7 @@ nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->s.id, len, nread, *err); goto out; } @@ -1977,7 +2044,7 @@ nread = -1; } } - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(len=%zu) -> %zd, %d", stream? stream->s.id : -1, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; @@ -1990,7 +2057,8 @@ static bool cf_osslq_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - const struct h3_stream_ctx *stream = H3_STREAM_CTX(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); } @@ -2019,7 +2087,7 @@ h3_data_done(cf, data); break; case CF_CTRL_DATA_DONE_SEND: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + 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); @@ -2028,7 +2096,7 @@ break; } case CF_CTRL_DATA_IDLE: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURL_TRC_CF(data, cf, "data idle"); if(stream && !stream->closed) { result = check_and_set_expiry(cf, data); @@ -2052,10 +2120,28 @@ CF_DATA_SAVE(save, cf, data); *input_pending = FALSE; - if(!ctx->tls.ssl) + if(!ctx->tls.ossl.ssl) goto out; - /* TODO: how to check negotiated connection idle time? */ +#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT + /* Added in OpenSSL v3.3.x */ + { + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + 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: %zums", (size_t)idle_ms); + idletime = Curl_timediff(Curl_now(), ctx->q.last_io); + if(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; @@ -2083,15 +2169,15 @@ { struct cf_osslq_ctx *ctx = cf->ctx; - if(!ctx->tls.ssl) { + 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 */ Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ssl), - SSL_net_write_desired(ctx->tls.ssl)); + 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. @@ -2100,8 +2186,8 @@ Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(want_recv || want_send) { Curl_pollset_set(data, ps, ctx->q.sockfd, - SSL_net_read_desired(ctx->tls.ssl), - SSL_net_write_desired(ctx->tls.ssl)); + SSL_net_read_desired(ctx->tls.ossl.ssl), + SSL_net_write_desired(ctx->tls.ossl.ssl)); } } } @@ -2111,15 +2197,24 @@ int query, int *pres1, void *pres2) { struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; switch(query) { case CF_QUERY_MAX_CONCURRENT: { - /* TODO: how to get this? */ - CF_DATA_SAVE(save, cf, data); +#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL + /* Added in OpenSSL v3.3.x */ + uint64_t v; + if(!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 += CONN_INUSE(cf->conn); + *pres1 = (v > INT_MAX)? INT_MAX : (int)v; +#else *pres1 = 100; +#endif CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); - CF_DATA_RESTORE(cf, save); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS:
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index fcb0eb8..a68fc64 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -29,6 +29,7 @@ #include <openssl/err.h> #include <openssl/ssl.h> #include "bufq.h" +#include "hash.h" #include "urldata.h" #include "cfilters.h" #include "cf-socket.h" @@ -88,7 +89,7 @@ struct cf_quiche_ctx { struct cf_quic_ctx q; struct ssl_peer peer; - struct quic_tls_ctx tls; + struct curl_tls_ctx tls; quiche_conn *qconn; quiche_config *cfg; quiche_h3_conn *h3c; @@ -98,8 +99,8 @@ struct curltime handshake_at; /* time connect handshake finished */ struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ + struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ curl_off_t data_recvd; - uint64_t max_idle_ms; /* max idle time for QUIC conn */ BIT(goaway); /* got GOAWAY from server */ BIT(x509_store_setup); /* if x509 store has been set up */ }; @@ -123,55 +124,71 @@ quiche_conn_free(ctx->qconn); if(ctx->cfg) quiche_config_free(ctx->cfg); - /* quiche just freed ctx->tls.ssl */ - ctx->tls.ssl = NULL; + /* quiche just freed it */ + ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); Curl_ssl_peer_cleanup(&ctx->peer); vquic_ctx_free(&ctx->q); Curl_bufcp_free(&ctx->stream_bufcp); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); memset(ctx, 0, sizeof(*ctx)); } } +static CURLcode cf_flush_egress(struct Curl_cfilter *cf, + struct Curl_easy *data); + /** * All about the H3 internals of a stream */ struct stream_ctx { - int64_t id; /* HTTP/3 protocol stream identifier */ + curl_uint64_t id; /* HTTP/3 protocol stream identifier */ struct bufq recvbuf; /* h3 response */ struct h1_req_parser h1; /* h1 request parsing */ - uint64_t error3; /* HTTP/3 stream error code */ + curl_uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ - bool closed; /* TRUE on stream close */ - bool reset; /* TRUE on stream reset */ - bool send_closed; /* stream is locally closed */ - bool resp_hds_complete; /* complete, final response has been received */ - bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */ + BIT(opened); /* TRUE after stream has been opened */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(send_closed); /* stream is locally closed */ + BIT(resp_hds_complete); /* final response has been received */ + BIT(resp_got_header); /* TRUE when h3 stream has recvd some HEADER */ BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; -#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ - ((struct HTTP *)(d)->req.p.http)->h3_ctx \ - : NULL)) -#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx -#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ - H3_STREAM_CTX(d)->id : -2) +#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\ + data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + +static void h3_stream_ctx_free(struct stream_ctx *stream) +{ + Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); + free(stream); +} + +static void h3_stream_hash_free(void *stream) +{ + DEBUGASSERT(stream); + h3_stream_ctx_free((struct stream_ctx *)stream); +} static void check_resumes(struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_quiche_ctx *ctx = cf->ctx; struct Curl_easy *sdata; struct stream_ctx *stream; DEBUGASSERT(data->multi); for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { if(sdata->conn == data->conn) { - stream = H3_STREAM_CTX(sdata); + stream = H3_STREAM_CTX(ctx, sdata); if(stream && stream->quic_flow_blocked) { stream->quic_flow_blocked = FALSE; Curl_expire(data, 0, EXPIRE_RUN_NOW); - CURL_TRC_CF(data, cf, "[%"PRId64"] unblock", stream->id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] unblock", stream->id); } } } @@ -181,7 +198,7 @@ struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream) return CURLE_OK; @@ -190,22 +207,28 @@ if(!stream) return CURLE_OUT_OF_MEMORY; - H3_STREAM_LCTX(data) = stream; stream->id = -1; Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); + + if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + 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_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); + CURLcode result; (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] easy handle is done", stream->id); if(ctx->qconn && !stream->closed) { quiche_conn_stream_shutdown(ctx->qconn, stream->id, QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); @@ -215,18 +238,19 @@ stream->send_closed = TRUE; } stream->closed = TRUE; + result = cf_flush_egress(cf, data); + if(result) + CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result); } - Curl_bufq_free(&stream->recvbuf); - Curl_h1_req_parse_free(&stream->h1); - free(stream); - H3_STREAM_LCTX(data) = NULL; + Curl_hash_offt_remove(&ctx->streams, data->id); } } static void drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); unsigned char bits; (void)cf; @@ -241,25 +265,50 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, struct Curl_easy *data, - int64_t stream3_id) + curl_uint64_t stream_id, + struct stream_ctx **pstream) { + struct cf_quiche_ctx *ctx = cf->ctx; struct Curl_easy *sdata; + struct stream_ctx *stream; (void)cf; - if(H3_STREAM_ID(data) == stream3_id) { + stream = H3_STREAM_CTX(ctx, data); + if(stream && stream->id == stream_id) { + *pstream = stream; return data; } else { DEBUGASSERT(data->multi); for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { - if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) { + if(sdata->conn != data->conn) + continue; + stream = H3_STREAM_CTX(ctx, sdata); + if(stream && stream->id == stream_id) { + *pstream = stream; return sdata; } } } + *pstream = NULL; return NULL; } +static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct Curl_easy *sdata; + + DEBUGASSERT(data->multi); + CURL_TRC_CF(data, cf, "conn closed, expire all transfers"); + for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + if(sdata == data || sdata->conn != data->conn) + continue; + CURL_TRC_CF(sdata, cf, "conn closed, expire transfer"); + Curl_expire(sdata, 0, EXPIRE_RUN_NOW); + } +} + /* * 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 @@ -269,7 +318,8 @@ struct Curl_easy *data, const void *mem, size_t memlen) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; ssize_t nwritten; @@ -299,14 +349,15 @@ void *argp) { struct cb_ctx *x = argp; - struct stream_ctx *stream = H3_STREAM_CTX(x->data); + struct cf_quiche_ctx *ctx = x->cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); CURLcode result; if(!stream) return CURLE_OK; if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] status: %.*s", + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] status: %.*s", stream->id, (int)value_len, value); result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); if(!result) @@ -315,7 +366,7 @@ result = write_resp_raw(x->cf, x->data, " \r\n", 3); } else { - CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] header: %.*s: %.*s", stream->id, (int)name_len, name, (int)value_len, value); result = write_resp_raw(x->cf, x->data, name, name_len); @@ -327,7 +378,7 @@ result = write_resp_raw(x->cf, x->data, "\r\n", 2); } if(result) { - CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d", + CURL_TRC_CF(x->data, x->cf, "[%"CURL_PRIu64"] on header error %d", stream->id, result); } return result; @@ -339,7 +390,7 @@ { struct cb_ctx *x = reader_ctx; struct cf_quiche_ctx *ctx = x->cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(x->data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); ssize_t nread; if(!stream) { @@ -362,7 +413,8 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nwritten; struct cb_ctx cb_ctx; CURLcode result = CURLE_OK; @@ -383,9 +435,9 @@ stream_resp_read, &cb_ctx, &result); if(nwritten < 0 && result != CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd", + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv_body error %zd", stream->id, nwritten); - failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]", + failf(data, "Error %d in HTTP/3 response body for stream[%"CURL_PRIu64"]", result, stream->id); stream->closed = TRUE; stream->reset = TRUE; @@ -420,17 +472,15 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, struct Curl_easy *data, - int64_t stream3_id, + struct stream_ctx *stream, quiche_h3_event *ev) { - struct stream_ctx *stream = H3_STREAM_CTX(data); struct cb_ctx cb_ctx; CURLcode result = CURLE_OK; int rc; if(!stream) return CURLE_OK; - DEBUGASSERT(stream3_id == stream->id); switch(quiche_h3_event_type(ev)) { case QUICHE_H3_EVENT_HEADERS: stream->resp_got_header = TRUE; @@ -438,11 +488,11 @@ cb_ctx.data = data; rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); if(rc) { - failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]", - rc, stream3_id); + failf(data, "Error %d in HTTP/3 response header for stream[%" + CURL_PRIu64"]", rc, stream->id); return CURLE_RECV_ERROR; } - CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [HEADERS]", stream->id); break; case QUICHE_H3_EVENT_DATA: @@ -452,7 +502,7 @@ break; case QUICHE_H3_EVENT_RESET: - CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] RESET", stream->id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -460,7 +510,7 @@ break; case QUICHE_H3_EVENT_FINISHED: - CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] CLOSED", stream->id); if(!stream->resp_hds_complete) { result = write_resp_raw(cf, data, "\r\n", 2); if(result) @@ -472,12 +522,12 @@ break; case QUICHE_H3_EVENT_GOAWAY: - CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [GOAWAY]", stream->id); break; default: - CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d", - stream3_id, quiche_h3_event_type(ev)); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv, unhandled event %d", + stream->id, quiche_h3_event_type(ev)); break; } return result; @@ -487,36 +537,33 @@ struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = NULL; struct Curl_easy *sdata; quiche_h3_event *ev; CURLcode result; /* Take in the events and distribute them to the transfers. */ while(ctx->h3c) { - int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); + curl_int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); if(stream3_id == QUICHE_H3_ERR_DONE) { break; } else if(stream3_id < 0) { - CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64, - stream? stream->id : -1, stream3_id); + CURL_TRC_CF(data, cf, "error poll: %"CURL_PRId64, stream3_id); return CURLE_HTTP3; } - sdata = get_stream_easy(cf, data, stream3_id); - if(!sdata) { - CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for " - "unknown [%"PRId64"]", - stream? stream->id : -1, cf_ev_name(ev), stream3_id); + sdata = get_stream_easy(cf, data, stream3_id, &stream); + if(!sdata || !stream) { + CURL_TRC_CF(data, cf, "discard event %s for unknown [%"CURL_PRId64"]", + cf_ev_name(ev), stream3_id); } else { - result = h3_process_event(cf, sdata, stream3_id, ev); + result = h3_process_event(cf, sdata, stream, ev); drain_stream(cf, sdata); if(result) { - CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s " - "for [%"PRId64"] -> %d", - stream? stream->id : -1, cf_ev_name(ev), + CURL_TRC_CF(data, cf, "error processing event %s " + "for [%"CURL_PRIu64"] -> %d", cf_ev_name(ev), stream3_id, result); if(data == sdata) { /* Only report this error to the caller if it is about the @@ -560,11 +607,19 @@ &recv_info); if(nread < 0) { if(QUICHE_ERR_DONE == nread) { + if(quiche_conn_is_draining(ctx->qconn)) { + CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining"); + return CURLE_RECV_ERROR; + } + if(quiche_conn_is_closed(ctx->qconn)) { + CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed"); + return CURLE_RECV_ERROR; + } CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); return CURLE_OK; } else if(QUICHE_ERR_TLS_FAIL == nread) { - long verify_ok = SSL_get_verify_result(ctx->tls.ssl); + long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl); if(verify_ok != X509_V_OK) { failf(r->data, "SSL certificate problem: %s", X509_verify_cert_error_string(verify_ok)); @@ -651,8 +706,8 @@ struct cf_quiche_ctx *ctx = cf->ctx; ssize_t nread; CURLcode result; - int64_t expiry_ns; - int64_t timeout_ns; + curl_int64_t expiry_ns; + curl_int64_t timeout_ns; struct read_ctx readx; size_t pkt_count, gsolen; @@ -660,7 +715,13 @@ if(!expiry_ns) { quiche_conn_on_timeout(ctx->qconn); if(quiche_conn_is_closed(ctx->qconn)) { - failf(data, "quiche_conn_on_timeout closed the connection"); + if(quiche_conn_is_timed_out(ctx->qconn)) + failf(data, "connection closed by idle timeout"); + 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. */ + cf_quiche_expire_conn_closed(cf, data); return CURLE_SEND_ERROR; } } @@ -725,25 +786,26 @@ struct Curl_easy *data, CURLcode *err) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_quiche_ctx *ctx = cf->ctx; + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nread = -1; DEBUGASSERT(stream); if(stream->reset) { failf(data, - "HTTP/3 stream %" PRId64 " reset by server", stream->id); - *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d", + "HTTP/3 stream %" CURL_PRIu64 " reset by server", stream->id); + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, was reset -> %d", stream->id, *err); } else if(!stream->resp_got_header) { failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" - " all response header fields, treated as error", + "HTTP/3 stream %" CURL_PRIu64 " was closed cleanly, but before " + "getting all response header fields, treated as error", stream->id); /* *err = CURLE_PARTIAL_FILE; */ *err = CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete" + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, closed incomplete" " -> %d", stream->id, *err); } else { @@ -757,7 +819,7 @@ char *buf, size_t len, CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); ssize_t nread = -1; CURLcode result; @@ -771,7 +833,7 @@ if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; @@ -788,7 +850,7 @@ if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; @@ -822,7 +884,7 @@ } if(nread > 0) ctx->data_recvd += nread; - CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%" + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] cf_recv(total=%" CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", stream->id, ctx->data_recvd, nread, *err); return nread; @@ -838,9 +900,9 @@ CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); size_t nheader, i; - int64_t stream3_id; + curl_int64_t stream3_id; struct dynhds h2_headers; quiche_h3_header *nva = NULL; ssize_t nwritten; @@ -850,7 +912,7 @@ if(*err) { return -1; } - stream = H3_STREAM_CTX(data); + stream = H3_STREAM_CTX(ctx, data); DEBUGASSERT(stream); } @@ -915,14 +977,14 @@ if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - CURL_TRC_CF(data, cf, "[%"PRId64"] blocked", stream->id); + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] blocked", stream->id); stream->quic_flow_blocked = TRUE; *err = CURLE_AGAIN; nwritten = -1; goto out; } else { - CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64, + CURL_TRC_CF(data, cf, "send_request(%s) -> %" CURL_PRIu64, data->state.url, stream3_id); } *err = CURLE_SEND_ERROR; @@ -930,17 +992,18 @@ goto out; } - DEBUGASSERT(stream->id == -1); + DEBUGASSERT(!stream->opened); *err = CURLE_OK; stream->id = stream3_id; + stream->opened = TRUE; stream->closed = FALSE; stream->reset = FALSE; if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" CURL_PRIu64 "] OPENED stream for %s", stream->id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" CURL_PRIu64 "] [%.*s: %.*s]", stream->id, (int)nva[i].name_len, nva[i].name, (int)nva[i].value_len, nva[i].value); } @@ -956,7 +1019,7 @@ const void *buf, size_t len, CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result; ssize_t nwritten; @@ -968,11 +1031,11 @@ goto out; } - if(!stream || stream->id < 0) { + if(!stream || !stream->opened) { nwritten = h3_open_stream(cf, data, buf, len, err); if(nwritten < 0) goto out; - stream = H3_STREAM_CTX(data); + stream = H3_STREAM_CTX(ctx, data); } else if(stream->closed) { if(stream->resp_hds_complete) { @@ -984,13 +1047,13 @@ * sending the 30x response. * This is sort of a race: had the transfer loop called recv first, * it would see the response and stop/discard sending on its own- */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] discarding data" "on closed stream with response", stream->id); *err = CURLE_OK; nwritten = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); *err = CURLE_HTTP3; nwritten = -1; @@ -1005,7 +1068,7 @@ /* TODO: we seem to be blocked on flow control and should HOLD * sending. But when do we open again? */ if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " "-> window exhausted", stream->id, len); stream->quic_flow_blocked = TRUE; } @@ -1014,21 +1077,21 @@ goto out; } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " "-> invalid stream state", stream->id, len); *err = CURLE_HTTP3; nwritten = -1; goto out; } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " "-> exceeds size", stream->id, len); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } else if(nwritten < 0) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " "-> quiche err %zd", stream->id, len, nwritten); *err = CURLE_SEND_ERROR; nwritten = -1; @@ -1043,7 +1106,7 @@ if(stream->upload_left == 0) stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, " + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send body(len=%zu, " "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", stream->id, len, stream->upload_left, nwritten); *err = CURLE_OK; @@ -1056,7 +1119,7 @@ *err = result; nwritten = -1; } - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->id : -1, len, nwritten, *err); return nwritten; } @@ -1065,10 +1128,10 @@ struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); - return stream && (quiche_conn_stream_writable(ctx->qconn, - (uint64_t)stream->id, 1) > 0); + return stream && (quiche_conn_stream_writable( + ctx->qconn, (curl_uint64_t)stream->id, 1) > 0); } static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, @@ -1083,12 +1146,12 @@ Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); if(want_recv || want_send) { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); bool c_exhaust, s_exhaust; 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->id >= 0 && + s_exhaust = want_send && stream && stream->opened && (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || @@ -1105,7 +1168,8 @@ static bool cf_quiche_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - const struct stream_ctx *stream = H3_STREAM_CTX(data); + struct cf_quiche_ctx *ctx = cf->ctx; + const struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; return stream && !Curl_bufq_is_empty(&stream->recvbuf); } @@ -1127,6 +1191,7 @@ struct Curl_easy *data, int event, int arg1, void *arg2) { + struct cf_quiche_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; (void)arg1; @@ -1144,7 +1209,7 @@ h3_data_done(cf, data); break; case CF_CTRL_DATA_DONE_SEND: { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream && !stream->send_closed) { unsigned char body[1]; ssize_t sent; @@ -1153,13 +1218,13 @@ stream->upload_left = 0; body[0] = 'X'; sent = cf_quiche_send(cf, data, body, 0, &result); - CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d", + CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] DONE_SEND -> %zd, %d", stream->id, sent, result); } break; } case CF_CTRL_DATA_IDLE: { - struct stream_ctx *stream = H3_STREAM_CTX(data); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream && !stream->closed) { result = cf_flush_egress(cf, data); if(result) @@ -1191,16 +1256,16 @@ debug_log_init = 1; } #endif - ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); ctx->data_recvd = 0; result = vquic_ctx_init(&ctx->q); if(result) return result; - result = Curl_ssl_peer_init(&ctx->peer, cf); + result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) return result; @@ -1210,7 +1275,7 @@ return CURLE_FAILED_INIT; } quiche_config_enable_pacing(ctx->cfg, false); - quiche_config_set_max_idle_timeout(ctx->cfg, ctx->max_idle_ms * 1000); + quiche_config_set_max_idle_timeout(ctx->cfg, CURL_QUIC_MAX_IDLE_MS); quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024) /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */); quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); @@ -1235,7 +1300,7 @@ result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, QUICHE_H3_APPLICATION_PROTOCOL, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1, - NULL, cf); + NULL, NULL, cf); if(result) return result; @@ -1243,8 +1308,7 @@ if(result) return result; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, - &sockaddr, NULL, NULL, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); ctx->q.local_addrlen = sizeof(ctx->q.local_addr); rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, &ctx->q.local_addrlen); @@ -1256,7 +1320,7 @@ (struct sockaddr *)&ctx->q.local_addr, ctx->q.local_addrlen, &sockaddr->sa_addr, sockaddr->addrlen, - ctx->cfg, ctx->tls.ssl, false); + ctx->cfg, ctx->tls.ossl.ssl, false); if(!ctx->qconn) { failf(data, "can't create quiche connection"); return CURLE_OUT_OF_MEMORY; @@ -1390,13 +1454,11 @@ out: #ifndef CURL_DISABLE_VERBOSE_STRINGS if(result && result != CURLE_AGAIN) { - const char *r_ip; - int r_port; + struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); infof(data, "connect to %s port %u failed: %s", - r_ip, r_port, curl_easy_strerror(result)); + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); } #endif return result; @@ -1436,12 +1498,14 @@ switch(query) { case CF_QUERY_MAX_CONCURRENT: { - uint64_t max_streams = CONN_INUSE(cf->conn); + curl_uint64_t max_streams = CONN_INUSE(cf->conn); if(!ctx->goaway) { max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); } *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; - CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1); + CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + "MAX_CONCURRENT -> %d (%zu in use)", + cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS: @@ -1483,23 +1547,12 @@ if(!ctx->qconn) return FALSE; - /* Both sides of the QUIC connection announce they max idle times in - * the transport parameters. Look at the minimum of both and if - * we exceed this, regard the connection as dead. The other side - * may have completely purged it and will no longer respond - * to any packets from us. */ - { - quiche_transport_params qpeerparams; - timediff_t idletime; - uint64_t idle_ms = ctx->max_idle_ms; - - if(quiche_conn_peer_transport_params(ctx->qconn, &qpeerparams) && - qpeerparams.peer_max_idle_timeout && - qpeerparams.peer_max_idle_timeout < idle_ms) - idle_ms = qpeerparams.peer_max_idle_timeout; - idletime = Curl_timediff(Curl_now(), cf->conn->lastused); - if(idletime > 0 && (uint64_t)idletime > idle_ms) - return FALSE; + if(quiche_conn_is_closed(ctx->qconn)) { + if(quiche_conn_is_timed_out(ctx->qconn)) + CURL_TRC_CF(data, cf, "connection was closed due to idle timeout"); + else + CURL_TRC_CF(data, cf, "connection is closed"); + return FALSE; } if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c index cc7794e..aca18b4 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.c +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c
@@ -24,7 +24,7 @@ #include "curl_setup.h" -#if defined(ENABLE_QUIC) && \ +#if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) #ifdef USE_OPENSSL @@ -61,277 +61,12 @@ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) #endif -#ifdef USE_OPENSSL -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:X25519:P-384:P-521" -#elif defined(USE_GNUTLS) -#define QUIC_PRIORITY \ - "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ - "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ - "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ - "%DISABLE_TLS13_COMPAT_MODE" -#elif defined(USE_WOLFSSL) +#if defined(USE_WOLFSSL) + #define QUIC_CIPHERS \ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" #define QUIC_GROUPS "P-256:P-384:P-521" -#endif - - -#ifdef USE_OPENSSL - -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} - -static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - Curl_vquic_tls_ctx_setup *ctx_setup) -{ - struct ssl_primary_config *conn_config; - CURLcode result = CURLE_FAILED_INIT; - - DEBUGASSERT(!ctx->ssl_ctx); -#ifdef USE_OPENSSL_QUIC - ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method()); -#else - ctx->ssl_ctx = SSL_CTX_new(TLS_method()); -#endif - if(!ctx->ssl_ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!conn_config) { - result = CURLE_FAILED_INIT; - goto out; - } - - if(ctx_setup) { - result = ctx_setup(ctx, cf, data); - if(result) - goto out; - } - - SSL_CTX_set_default_verify_paths(ctx->ssl_ctx); - - { - const char *curves = conn_config->curves ? - conn_config->curves : QUIC_GROUPS; - if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) { - failf(data, "failed setting curves list for QUIC: '%s'", curves); - return CURLE_SSL_CIPHER; - } - } - -#ifndef OPENSSL_IS_BORINGSSL - { - const char *ciphers13 = conn_config->cipher_list13 ? - conn_config->cipher_list13 : QUIC_CIPHERS; - if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) { - failf(data, "failed setting QUIC cipher suite: %s", ciphers13); - return CURLE_SSL_CIPHER; - } - infof(data, "QUIC cipher selection: %s", ciphers13); - } -#endif - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback); - } - - /* OpenSSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ? - SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - /* When a user callback is installed to modify the SSL_CTX, - * we need to do the full initialization before calling it. - * See: #11800 */ - if(!ctx->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx); - if(result) - goto out; - ctx->x509_store_setup = TRUE; - } - Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); - if(result) { - failf(data, "error signaled by ssl ctx callback"); - goto out; - } - } - result = CURLE_OK; - -out: - if(result && ctx->ssl_ctx) { - SSL_CTX_free(ctx->ssl_ctx); - ctx->ssl_ctx = NULL; - } - return result; -} - -static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = ctx->ssl_ctx; - const struct ssl_config_data *ssl_config; - - ssl_config = Curl_ssl_cf_get_config(cf, data); - DEBUGASSERT(ssl_config); - - if(ssl_config->primary.clientcert || - ssl_config->primary.cert_blob || - ssl_config->cert_type) { - return Curl_ossl_set_client_cert( - data, ssl_ctx, ssl_config->primary.clientcert, - ssl_config->primary.cert_blob, ssl_config->cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd); - } - - return CURLE_OK; -} - -/** SSL callbacks ***/ - -static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx, - struct Curl_easy *data, - struct ssl_peer *peer, - const char *alpn, size_t alpn_len, - void *user_data) -{ - DEBUGASSERT(!ctx->ssl); - ctx->ssl = SSL_new(ctx->ssl_ctx); - - SSL_set_app_data(ctx->ssl, user_data); - SSL_set_connect_state(ctx->ssl); -#ifndef USE_OPENSSL_QUIC - SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); -#endif - - if(alpn) - SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len); - - if(peer->sni) { - if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) { - failf(data, "Failed set SNI"); - SSL_free(ctx->ssl); - ctx->ssl = NULL; - return CURLE_QUIC_CONNECT_ERROR; - } - } - return CURLE_OK; -} - -#elif defined(USE_GNUTLS) -static int keylog_callback(gnutls_session_t session, const char *label, - const gnutls_datum_t *secret) -{ - gnutls_datum_t crandom; - gnutls_datum_t srandom; - - gnutls_session_get_random(session, &crandom, &srandom); - if(crandom.size != 32) { - return -1; - } - - Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); - return 0; -} - -static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const char *alpn, size_t alpn_len, - Curl_vquic_tls_ctx_setup *ctx_setup, - void *user_data) -{ - struct ssl_primary_config *conn_config; - CURLcode result; - gnutls_datum_t alpns[5]; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - long * const pverifyresult = &data->set.ssl.certverifyresult; - int rc; - - conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!conn_config) - return CURLE_FAILED_INIT; - - DEBUGASSERT(ctx->gtls == NULL); - ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); - if(!ctx->gtls) - return CURLE_OUT_OF_MEMORY; - - result = gtls_client_init(data, conn_config, &data->set.ssl, - peer, ctx->gtls, pverifyresult); - if(result) - return result; - - gnutls_session_set_ptr(ctx->gtls->session, user_data); - - if(ctx_setup) { - result = ctx_setup(ctx, cf, data); - if(result) - return result; - } - - rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); - if(rc < 0) { - CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", - gnutls_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback); - } - - /* convert the ALPN string from our arguments to a list of strings - * that gnutls wants and will convert internally back to this very - * string for sending to the server. nice. */ - if(alpn) { - size_t i, alen = alpn_len; - unsigned char *s = (unsigned char *)alpn; - unsigned char slen; - for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) { - slen = s[0]; - if(slen >= alen) - return CURLE_FAILED_INIT; - alpns[i].data = s + 1; - alpns[i].size = slen; - s += slen + 1; - alen -= (size_t)slen + 1; - } - if(alen) /* not all alpn chars used, wrong format or too many */ - return CURLE_FAILED_INIT; - if(i) { - gnutls_alpn_set_protocols(ctx->gtls->session, - alpns, (unsigned int)i, - GNUTLS_ALPN_MANDATORY); - } - } - - return CURLE_OK; -} -#elif defined(USE_WOLFSSL) #if defined(HAVE_SECRET_CALLBACK) static void keylog_callback(const WOLFSSL *ssl, const char *line) @@ -341,10 +76,11 @@ } #endif -static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx, +static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, - Curl_vquic_tls_ctx_setup *ctx_setup) + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data) { struct ssl_primary_config *conn_config; CURLcode result = CURLE_FAILED_INIT; @@ -361,8 +97,8 @@ goto out; } - if(ctx_setup) { - result = ctx_setup(ctx, cf, data); + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); if(result) goto out; } @@ -375,6 +111,7 @@ char error_buffer[256]; ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); failf(data, "wolfSSL failed to set ciphers: %s", error_buffer); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } @@ -382,6 +119,7 @@ conn_config->curves : (char *)QUIC_GROUPS) != 1) { failf(data, "wolfSSL failed to set curves"); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } @@ -392,6 +130,7 @@ wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback); #else failf(data, "wolfSSL was built without keylog callback"); + result = CURLE_NOT_BUILT_IN; goto out; #endif } @@ -414,6 +153,7 @@ " CAfile: %s CApath: %s", ssl_cafile ? ssl_cafile : "none", ssl_capath ? ssl_capath : "none"); + result = CURLE_SSL_CACERT_BADFILE; goto out; } infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); @@ -454,7 +194,7 @@ /** SSL callbacks ***/ -static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx, +static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, @@ -482,57 +222,50 @@ } #endif /* defined(USE_WOLFSSL) */ -CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, - Curl_vquic_tls_ctx_setup *ctx_setup, - void *user_data) + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, void *ssl_user_data) { CURLcode result; #ifdef USE_OPENSSL - result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup); - if(result) - return result; - - result = curl_ossl_set_client_cert(ctx, cf, data); - if(result) - return result; - - return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data); + (void)result; + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC, + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, NULL, ssl_user_data); #elif defined(USE_GNUTLS) (void)result; - return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len, - ctx_setup, user_data); + return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, + (const unsigned char *)alpn, alpn_len, + cb_setup, cb_user_data, ssl_user_data); #elif defined(USE_WOLFSSL) - result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup); + result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); if(result) return result; - return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data); + return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); #else #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; #endif } -void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx) +void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) { #ifdef USE_OPENSSL - if(ctx->ssl) - SSL_free(ctx->ssl); - if(ctx->ssl_ctx) - SSL_CTX_free(ctx->ssl_ctx); + if(ctx->ossl.ssl) + SSL_free(ctx->ossl.ssl); + if(ctx->ossl.ssl_ctx) + SSL_CTX_free(ctx->ossl.ssl_ctx); #elif defined(USE_GNUTLS) - if(ctx->gtls) { - if(ctx->gtls->cred) - gnutls_certificate_free_credentials(ctx->gtls->cred); - if(ctx->gtls->session) - gnutls_deinit(ctx->gtls->session); - free(ctx->gtls); - } + if(ctx->gtls.cred) + gnutls_certificate_free_credentials(ctx->gtls.cred); + if(ctx->gtls.session) + gnutls_deinit(ctx->gtls.session); #elif defined(USE_WOLFSSL) if(ctx->ssl) wolfSSL_free(ctx->ssl); @@ -542,16 +275,22 @@ memset(ctx, 0, sizeof(*ctx)); } -CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { #ifdef USE_OPENSSL - if(!ctx->x509_store_setup) { - CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx); + if(!ctx->ossl.x509_store_setup) { + CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx); if(result) return result; - ctx->x509_store_setup = TRUE; + ctx->ossl.x509_store_setup = TRUE; + } +#elif defined(USE_GNUTLS) + if(!ctx->gtls.trust_setup) { + CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls); + if(result) + return result; } #else (void)ctx; (void)cf; (void)data; @@ -559,7 +298,7 @@ return CURLE_OK; } -CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer) @@ -571,39 +310,33 @@ if(!conn_config) return CURLE_FAILED_INIT; - if(conn_config->verifyhost) { #ifdef USE_OPENSSL - X509 *server_cert; - server_cert = SSL_get1_peer_certificate(ctx->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert); - X509_free(server_cert); - if(result) - return result; + (void)conn_config; + result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer); #elif defined(USE_GNUTLS) - result = Curl_gtls_verifyserver(data, ctx->gtls->session, + if(conn_config->verifyhost) { + result = Curl_gtls_verifyserver(data, ctx->gtls.session, conn_config, &data->set.ssl, peer, data->set.str[STRING_SSL_PINNEDPUBLICKEY]); if(result) return result; -#elif defined(USE_WOLFSSL) - if(!peer->sni || - wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE) - return CURLE_PEER_FAILED_VERIFICATION; -#endif - infof(data, "Verified certificate just fine"); } - else - infof(data, "Skipped certificate verification"); -#ifdef USE_OPENSSL - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, ctx->ssl); +#elif defined(USE_WOLFSSL) + (void)data; + if(conn_config->verifyhost) { + if(peer->sni) { + WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->ssl); + if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL) + == WOLFSSL_FAILURE) { + result = CURLE_PEER_FAILED_VERIFICATION; + } + wolfSSL_X509_free(cert); + } + + } #endif return result; } -#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ +#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.h b/Utilities/cmcurl/lib/vquic/vquic-tls.h index 9c0dfd8..1d35fd0 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.h +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.h
@@ -26,21 +26,20 @@ #include "curl_setup.h" #include "bufq.h" +#include "vtls/openssl.h" -#if defined(ENABLE_QUIC) && \ +#if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) -struct quic_tls_ctx { +struct curl_tls_ctx { #ifdef USE_OPENSSL - SSL_CTX *ssl_ctx; - SSL *ssl; + struct ossl_ctx ossl; #elif defined(USE_GNUTLS) - struct gtls_instance *gtls; + struct gtls_ctx gtls; #elif defined(USE_WOLFSSL) WOLFSSL_CTX *ssl_ctx; WOLFSSL *ssl; #endif - BIT(x509_store_setup); /* if x509 store has been set up */ }; /** @@ -50,9 +49,9 @@ * - openssl/wolfssl: SSL_CTX* has just been created * - gnutls: gtls_client_init() has run */ -typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data); +typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *cb_user_data); /** * Initialize the QUIC TLS instances based of the SSL configurations @@ -64,23 +63,25 @@ * @param alpn the ALPN string in protocol format ((len+bytes+)+), * may be NULL * @param alpn_len the overall number of bytes in `alpn` - * @param ctx_setup optional callback for very early TLS config - * @param user_data optional pointer to set in TLS application context + * @param cb_setup optional callback for very early TLS config + ± @param cb_user_data user_data param for callback + * @param ssl_user_data optional pointer to set in TLS application context */ -CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, - Curl_vquic_tls_ctx_setup *ctx_setup, - void *user_data); + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, + void *ssl_user_data); /** * Cleanup all data that has been initialized. */ -void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx); +void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx); -CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data); @@ -88,11 +89,11 @@ * After the QUIC basic handshake has been, verify that the peer * (and its certificate) fulfill our requirements. */ -CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx, +CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer); -#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ +#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ #endif /* HEADER_CURL_VQUIC_TLS_H */
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 612d25b..9ce1e46 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -59,7 +59,7 @@ #include "memdebug.h" -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 #ifdef O_BINARY #define QLOGMODE O_WRONLY|O_CREAT|O_BINARY @@ -370,12 +370,10 @@ goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip = NULL; - int r_port = 0; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + struct ip_quadruple ip; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); failf(data, "QUIC: connection to %s port %u refused", - r_ip, r_port); + ip.remote_ip, ip.remote_port); result = CURLE_COULDNT_CONNECT; goto out; } @@ -440,12 +438,10 @@ goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip = NULL; - int r_port = 0; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + struct ip_quadruple ip; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); failf(data, "QUIC: connection to %s port %u refused", - r_ip, r_port); + ip.remote_ip, ip.remote_port); result = CURLE_COULDNT_CONNECT; goto out; } @@ -500,12 +496,10 @@ goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip = NULL; - int r_port = 0; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + struct ip_quadruple ip; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); failf(data, "QUIC: connection to %s port %u refused", - r_ip, r_port); + ip.remote_ip, ip.remote_port); result = CURLE_COULDNT_CONNECT; goto out; } @@ -669,7 +663,7 @@ return CURLE_OK; } -#else /* ENABLE_QUIC */ +#else /* USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) @@ -680,4 +674,4 @@ return CURLE_NOT_BUILT_IN; } -#endif /* !ENABLE_QUIC */ +#endif /* !USE_HTTP3 */
diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h index dc73957..c1ca1df 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.h +++ b/Utilities/cmcurl/lib/vquic/vquic.h
@@ -26,7 +26,7 @@ #include "curl_setup.h" -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 struct Curl_cfilter; struct Curl_easy; struct connectdata; @@ -52,11 +52,11 @@ extern struct Curl_cftype Curl_cft_http3; -#else /* ENABLE_QUIC */ +#else /* USE_HTTP3 */ #define Curl_conn_is_http3(a,b,c) FALSE -#endif /* !ENABLE_QUIC */ +#endif /* !USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn);
diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h index c218a94..754e1f5 100644 --- a/Utilities/cmcurl/lib/vquic/vquic_int.h +++ b/Utilities/cmcurl/lib/vquic/vquic_int.h
@@ -27,7 +27,7 @@ #include "curl_setup.h" #include "bufq.h" -#ifdef ENABLE_QUIC +#ifdef USE_HTTP3 #define MAX_PKT_BURST 10 #define MAX_UDP_PAYLOAD_SIZE 1452 @@ -88,6 +88,6 @@ size_t max_pkts, vquic_recv_pkt_cb *recv_cb, void *userp); -#endif /* !ENABLE_QUIC */ +#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 c6dc63a..d6ba987 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -162,6 +162,7 @@ myssh_getsock, /* perform_getsock */ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SSH, /* defport */ @@ -189,6 +190,7 @@ myssh_getsock, /* perform_getsock */ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SSH, /* defport */ @@ -1292,10 +1294,10 @@ position. */ if(data->state.resume_from > 0) { /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { + if(data->set.seek_func) { Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); Curl_set_in_callback(data, false); } @@ -1349,9 +1351,9 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; /* store this original bitmask setup to use later on if we can't @@ -1575,7 +1577,7 @@ sshc->sftp_dir = NULL; /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); state(data, SSH_STOP); break; @@ -1665,6 +1667,8 @@ size = 0; } else { + if((to - from) == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; size = to - from + 1; } @@ -1718,14 +1722,14 @@ /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); infof(data, "File already completely downloaded"); state(data, SSH_STOP); break; } - Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; /* we want to use the _receiving_ function even when the socket turns @@ -1847,9 +1851,9 @@ } /* upload data */ - Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, data->req.size, FALSE, FIRSTSOCKET); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; /* store this original bitmask setup to use later on if we can't @@ -1891,9 +1895,9 @@ /* download data */ bytecount = ssh_scp_request_get_size(sshc->scp_session); data->req.maxdownload = (curl_off_t) bytecount; - Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; /* we want to use the _receiving_ function even when the socket turns
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index e9dfef9..abdf42e 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -139,6 +139,7 @@ ssh_getsock, /* perform_getsock */ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ssh_attach, /* attach */ PORT_SSH, /* defport */ @@ -168,6 +169,7 @@ ssh_getsock, /* perform_getsock */ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ssh_attach, /* attach */ PORT_SSH, /* defport */ @@ -201,7 +203,8 @@ if(num_prompts == 1) { struct connectdata *conn = data->conn; responses[0].text = strdup(conn->passwd); - responses[0].length = curlx_uztoui(strlen(conn->passwd)); + responses[0].length = + responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); } (void)prompts; } /* kbd_callback */ @@ -1083,6 +1086,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; /* If no private key file is specified, try some common paths. */ if(home) { @@ -1090,12 +1094,12 @@ sshc->rsa = aprintf("%s/.ssh/id_rsa", home); if(!sshc->rsa) out_of_memory = TRUE; - else if(access(sshc->rsa, R_OK) != 0) { + else if(stat(sshc->rsa, &sbuf)) { Curl_safefree(sshc->rsa); sshc->rsa = aprintf("%s/.ssh/id_dsa", home); if(!sshc->rsa) out_of_memory = TRUE; - else if(access(sshc->rsa, R_OK) != 0) { + else if(stat(sshc->rsa, &sbuf)) { Curl_safefree(sshc->rsa); } } @@ -1104,10 +1108,10 @@ if(!out_of_memory && !sshc->rsa) { /* Nothing found; try the current dir. */ sshc->rsa = strdup("id_rsa"); - if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { + if(sshc->rsa && stat(sshc->rsa, &sbuf)) { Curl_safefree(sshc->rsa); sshc->rsa = strdup("id_dsa"); - if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { + if(sshc->rsa && stat(sshc->rsa, &sbuf)) { Curl_safefree(sshc->rsa); /* Out of guesses. Set to the empty string to avoid * surprising info messages. */ @@ -2142,10 +2146,10 @@ position. */ if(data->state.resume_from > 0) { /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { + if(data->set.seek_func) { Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); Curl_set_in_callback(data, false); } @@ -2195,9 +2199,9 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; if(result) { @@ -2448,7 +2452,7 @@ Curl_safefree(sshp->readdir_longentry); /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); state(data, SSH_STOP); break; @@ -2544,6 +2548,8 @@ size = 0; } else { + if((to - from) == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; size = to - from + 1; } @@ -2588,14 +2594,14 @@ /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); infof(data, "File already completely downloaded"); state(data, SSH_STOP); break; } - Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; /* we want to use the _receiving_ function even when the socket turns @@ -2739,9 +2745,9 @@ /* upload data */ data->req.size = data->state.infilesize; Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; if(result) { @@ -2810,9 +2816,9 @@ /* download data */ bytecount = (curl_off_t)sb.st_size; data->req.maxdownload = (curl_off_t)sb.st_size; - Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; /* we want to use the _receiving_ function even when the socket turns @@ -3191,12 +3197,13 @@ struct connectdata *conn = data->conn; Curl_recv *backup = conn->recv[0]; struct ssh_conn *ssh = &conn->proto.sshc; + int socknum = Curl_conn_sockindex(data, sock); (void)flags; /* swap in the TLS reader function for this call only, and then swap back the SSH one again */ conn->recv[0] = ssh->tls_recv; - result = Curl_read(data, sock, buffer, length, &nread); + result = Curl_conn_recv(data, socknum, buffer, length, &nread); conn->recv[0] = backup; if(result == CURLE_AGAIN) return -EAGAIN; /* magic return code for libssh2 */ @@ -3210,24 +3217,25 @@ size_t length, int flags, void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; - ssize_t nwrite; + size_t nwrite; CURLcode result; struct connectdata *conn = data->conn; Curl_send *backup = conn->send[0]; struct ssh_conn *ssh = &conn->proto.sshc; + int socknum = Curl_conn_sockindex(data, sock); (void)flags; /* swap in the TLS writer function for this call only, and then swap back the SSH one again */ conn->send[0] = ssh->tls_send; - result = Curl_write(data, sock, buffer, length, &nwrite); + result = Curl_conn_send(data, socknum, buffer, length, &nwrite); conn->send[0] = backup; if(result == CURLE_AGAIN) return -EAGAIN; /* magic return code for libssh2 */ else if(result) return -1; /* error */ - Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, (size_t)nwrite); - return nwrite; + Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, nwrite); + return (ssize_t)nwrite; } #endif @@ -3268,7 +3276,7 @@ #endif /* CURL_LIBSSH2_DEBUG */ /* libcurl MUST to set custom memory functions so that the kbd_callback - funciton's memory allocations can be properled freed */ + function's memory allocations can be properly freed */ sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, my_libssh2_free, my_libssh2_realloc, data); @@ -3278,7 +3286,6 @@ return CURLE_FAILED_INIT; } -#ifdef HAVE_LIBSSH2_VERSION /* Set the packet read timeout if the libssh2 version supports it */ #if LIBSSH2_VERSION_NUM >= 0x010B00 if(data->set.server_response_timeout > 0) { @@ -3286,7 +3293,6 @@ data->set.server_response_timeout / 1000); } #endif -#endif #ifndef CURL_DISABLE_PROXY if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index 7396791c..6a5aed8 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c
@@ -94,6 +94,7 @@ wssh_getsock, /* perform_getsock */ wscp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SSH, /* defport */ @@ -123,6 +124,7 @@ wssh_getsock, /* perform_getsock */ wsftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ + ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_SSH, /* defport */ @@ -625,10 +627,10 @@ if(data->state.resume_from > 0) { /* Let's read off the proper amount of bytes from the input. */ int seekerr = CURL_SEEKFUNC_OK; - if(conn->seek_func) { + if(data->set.seek_func) { Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); Curl_set_in_callback(data, false); } @@ -678,9 +680,9 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; if(result) { @@ -778,14 +780,14 @@ /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + Curl_xfer_setup(data, -1, -1, FALSE, -1); infof(data, "File already completely downloaded"); state(data, SSH_STOP); break; } - Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); + Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1); - /* not set by Curl_setup_transfer to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; /* we want to use the _receiving_ function even when the socket turns
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index 58394ba..a595f54 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c
@@ -28,6 +28,7 @@ #include <bearssl.h> #include "bearssl.h" +#include "cipher_suite.h" #include "urldata.h" #include "sendf.h" #include "inet_pton.h" @@ -37,7 +38,6 @@ #include "select.h" #include "multiif.h" #include "curl_printf.h" -#include "strcase.h" /* The last #include files should be: */ #include "curl_memory.h" @@ -120,9 +120,9 @@ br_x509_pkey *pkey; FILE *fp = 0; unsigned char buf[BUFSIZ]; - const unsigned char *p; + const unsigned char *p = NULL; const char *name; - size_t n, i, pushed; + size_t n = 0, i, pushed; DEBUGASSERT(source->type == CAFILE_SOURCE_PATH || source->type == CAFILE_SOURCE_BLOB); @@ -360,219 +360,121 @@ x509_get_pkey }; -struct st_cipher { - const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */ - const char *alias_name; /* Alias name is the same as OpenSSL cipher name */ - uint16_t num; /* BearSSL cipher suite */ -}; - -/* Macro to initialize st_cipher data structure */ -#define CIPHER_DEF(num, alias) { #num, alias, BR_##num } - -static const struct st_cipher ciphertable[] = { +static const uint16_t ciphertable[] = { /* RFC 2246 TLS 1.0 */ - CIPHER_DEF(TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ - "DES-CBC3-SHA"), + BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ /* RFC 3268 TLS 1.0 AES */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ - "AES128-SHA"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ - "AES256-SHA"), + BR_TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ + BR_TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ /* RFC 5246 TLS 1.2 */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ - "AES128-SHA256"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ - "AES256-SHA256"), + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ /* RFC 5288 TLS 1.2 AES GCM */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ - "AES128-GCM-SHA256"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ - "AES256-GCM-SHA384"), + BR_TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ /* RFC 4492 TLS 1.0 ECC */ - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ - "ECDH-ECDSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ - "ECDH-ECDSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ - "ECDH-ECDSA-AES256-SHA"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ - "ECDHE-ECDSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ - "ECDHE-ECDSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ - "ECDHE-ECDSA-AES256-SHA"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ - "ECDH-RSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ - "ECDH-RSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ - "ECDH-RSA-AES256-SHA"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ - "ECDHE-RSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ - "ECDHE-RSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ - "ECDHE-RSA-AES256-SHA"), + BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ + BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ + BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ + BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ - "ECDHE-ECDSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ - "ECDHE-ECDSA-AES256-SHA384"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ - "ECDH-ECDSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ - "ECDH-ECDSA-AES256-SHA384"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ - "ECDHE-RSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ - "ECDHE-RSA-AES256-SHA384"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ - "ECDH-RSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ - "ECDH-RSA-AES256-SHA384"), + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ /* RFC 5289 TLS 1.2 GCM */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ - "ECDHE-ECDSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ - "ECDHE-ECDSA-AES256-GCM-SHA384"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ - "ECDH-ECDSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ - "ECDH-ECDSA-AES256-GCM-SHA384"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ - "ECDHE-RSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ - "ECDHE-RSA-AES256-GCM-SHA384"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ - "ECDH-RSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ - "ECDH-RSA-AES256-GCM-SHA384"), -#ifdef BR_TLS_RSA_WITH_AES_128_CCM + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ + BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ + BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ +#ifdef BR_TLS_RSA_WITH_AES_128_CCM /* RFC 6655 TLS 1.2 CCM Supported since BearSSL 0.6 */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */ - "AES128-CCM"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */ - "AES256-CCM"), - CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */ - "AES128-CCM8"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */ - "AES256-CCM8"), + BR_TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */ + BR_TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */ + BR_TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */ + BR_TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */ /* RFC 7251 TLS 1.2 ECC CCM Supported since BearSSL 0.6 */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */ - "ECDHE-ECDSA-AES128-CCM"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */ - "ECDHE-ECDSA-AES256-CCM"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */ - "ECDHE-ECDSA-AES128-CCM8"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */ - "ECDHE-ECDSA-AES256-CCM8"), + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */ + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */ #endif /* RFC 7905 TLS 1.2 ChaCha20-Poly1305 Supported since BearSSL 0.2 */ - CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ - "ECDHE-RSA-CHACHA20-POLY1305"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ - "ECDHE-ECDSA-CHACHA20-POLY1305"), + BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ + BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ }; #define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0])) -#define CIPHER_NAME_BUF_LEN 64 - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data, br_ssl_engine_context *ssl_eng, const char *ciphers) { - uint16_t selected_ciphers[NUM_OF_CIPHERS]; - size_t selected_count = 0; - const char *cipher_start = ciphers; - const char *cipher_end; - size_t i, j; + uint16_t selected[NUM_OF_CIPHERS]; + size_t count = 0, i; + const char *ptr, *end; - if(!cipher_start) - return CURLE_SSL_CIPHER; + for(ptr = ciphers; ptr[0] != '\0' && count < NUM_OF_CIPHERS; ptr = end) { + uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end); - while(true) { - const char *cipher; - size_t clen; - - /* Extract the next cipher name from the ciphers string */ - while(is_separator(*cipher_start)) - ++cipher_start; - if(!*cipher_start) - break; - cipher_end = cipher_start; - while(*cipher_end && !is_separator(*cipher_end)) - ++cipher_end; - - clen = cipher_end - cipher_start; - cipher = cipher_start; - - cipher_start = cipher_end; - - /* Lookup the cipher name in the table of available ciphers. If the cipher - name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try - to match cipher name by an (OpenSSL) alias. */ - if(strncasecompare(cipher, "TLS_", 4)) { - for(i = 0; i < NUM_OF_CIPHERS && - (strlen(ciphertable[i].name) == clen) && - !strncasecompare(cipher, ciphertable[i].name, clen); ++i); + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < NUM_OF_CIPHERS && ciphertable[i] != id; i++); + if(i == NUM_OF_CIPHERS) + id = 0; } - else { - for(i = 0; i < NUM_OF_CIPHERS && - (strlen(ciphertable[i].alias_name) == clen) && - !strncasecompare(cipher, ciphertable[i].alias_name, clen); ++i); - } - if(i == NUM_OF_CIPHERS) { - infof(data, "BearSSL: unknown cipher in list: %.*s", - (int)clen, cipher); + if(!id) { + if(ptr[0] != '\0') + infof(data, "BearSSL: unknown cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); continue; } /* No duplicates allowed */ - for(j = 0; j < selected_count && - selected_ciphers[j] != ciphertable[i].num; j++); - if(j < selected_count) { - infof(data, "BearSSL: duplicate cipher in list: %.*s", - (int)clen, cipher); + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) { + infof(data, "BearSSL: duplicate cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); continue; } - DEBUGASSERT(selected_count < NUM_OF_CIPHERS); - selected_ciphers[selected_count] = ciphertable[i].num; - ++selected_count; + selected[count++] = id; } - if(selected_count == 0) { + if(count == 0) { failf(data, "BearSSL: no supported cipher in list"); return CURLE_SSL_CIPHER; } - br_ssl_engine_set_suites(ssl_eng, selected_ciphers, selected_count); + br_ssl_engine_set_suites(ssl_eng, selected, count); return CURLE_OK; } @@ -686,7 +588,7 @@ CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); session_set = 1; infof(data, "BearSSL: reusing session ID"); @@ -707,7 +609,7 @@ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } - if(connssl->peer.is_ip_address) { + if(connssl->peer.type != CURL_SSL_PEER_DNS) { if(verifyhost) { failf(data, "BearSSL: " "host verification of IP address is not supported"); @@ -830,7 +732,7 @@ CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result); if(ret == 0) { failf(data, "SSL: EOF without close notify"); - return CURLE_READ_ERROR; + return CURLE_RECV_ERROR; } if(ret <= 0) { return result; @@ -846,6 +748,9 @@ struct ssl_connect_data *connssl = cf->ctx; struct bearssl_ssl_backend_data *backend = (struct bearssl_ssl_backend_data *)connssl->backend; + br_ssl_session_parameters session; + char cipher_str[64]; + char ver_str[16]; CURLcode ret; DEBUGASSERT(backend); @@ -856,6 +761,7 @@ return CURLE_OK; if(ret == CURLE_OK) { unsigned int tver; + if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { failf(data, "SSL: connection closed during handshake"); return CURLE_SSL_CONNECT_ERROR; @@ -863,16 +769,29 @@ connssl->connecting_state = ssl_connect_3; /* Informational message */ tver = br_ssl_engine_get_version(&backend->ctx.eng); - if(tver == 0x0303) - infof(data, "SSL connection using TLSv1.2"); - else if(tver == 0x0304) - infof(data, "SSL connection using TLSv1.3"); - else - infof(data, "SSL connection using TLS 0x%x", tver); + if(tver == BR_TLS12) + strcpy(ver_str, "TLSv1.2"); + else if(tver == BR_TLS11) + strcpy(ver_str, "TLSv1.1"); + else if(tver == BR_TLS10) + strcpy(ver_str, "TLSv1.0"); + else { + msnprintf(ver_str, sizeof(ver_str), "TLS 0x%04x", tver); + } + br_ssl_engine_get_session_parameters(&backend->ctx.eng, &session); + Curl_cipher_suite_get_str(session.cipher_suite, cipher_str, + sizeof(cipher_str), true); + infof(data, "BearSSL: %s connection using %s", ver_str, cipher_str); } return ret; } +static void bearssl_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + free(sessionid); +} + static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -896,7 +815,6 @@ if(ssl_config->primary.sessionid) { bool incache; - bool added = FALSE; void *oldsession; br_ssl_session_parameters *session; @@ -905,16 +823,16 @@ return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &oldsession, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + &oldsession, NULL)); if(incache) Curl_ssl_delsessionid(data, oldsession); - ret = Curl_ssl_addsessionid(cf, data, session, 0, &added); + + ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0, + bearssl_session_free); Curl_ssl_sessionid_unlock(data); - if(!added) - free(session); - if(ret) { - return CURLE_OUT_OF_MEMORY; - } + if(ret) + return ret; } connssl->connecting_state = ssl_connect_done; @@ -1173,11 +1091,6 @@ } } -static void bearssl_session_free(void *ptr) -{ - free(ptr); -} - static CURLcode bearssl_sha256sum(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, @@ -1210,7 +1123,6 @@ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ - bearssl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/cipher_suite.c b/Utilities/cmcurl/lib/vtls/cipher_suite.c new file mode 100644 index 0000000..a78838d --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/cipher_suite.c
@@ -0,0 +1,716 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jan Venekamp, <jan@venekamp.net> + * + * 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(USE_MBEDTLS) || defined(USE_BEARSSL) +#include "cipher_suite.h" +#include "curl_printf.h" +#include "strcase.h" +#include <string.h> + +/* + * To support the CURLOPT_SSL_CIPHER_LIST option on SSL backends + * that do not support it natively, but do support setting a list of + * IANA ids, we need a list of all supported cipher suite names + * (openssl and IANA) to be able to look up the IANA ids. + * + * To keep the binary size of this list down we compress each entry + * down to 2 + 6 bytes using the C preprocessor. + */ + +/* + * mbedTLS NOTE: mbedTLS has mbedtls_ssl_get_ciphersuite_id() to + * convert a string representation to an IANA id, we do not use that + * because it does not support "standard" openssl cipher suite + * names, nor IANA names. + */ + +/* NOTE: also see tests/unit/unit3205.c */ + +/* Text for cipher suite parts (max 64 entries), + keep indexes below in sync with this! */ +static const char *cs_txt = + "\0" + "TLS" "\0" + "WITH" "\0" + "128" "\0" + "256" "\0" + "3DES" "\0" + "8" "\0" + "AES" "\0" + "AES128" "\0" + "AES256" "\0" + "CBC" "\0" + "CBC3" "\0" + "CCM" "\0" + "CCM8" "\0" + "CHACHA20" "\0" + "DES" "\0" + "DHE" "\0" + "ECDH" "\0" + "ECDHE" "\0" + "ECDSA" "\0" + "EDE" "\0" + "GCM" "\0" + "MD5" "\0" + "NULL" "\0" + "POLY1305" "\0" + "PSK" "\0" + "RSA" "\0" + "SHA" "\0" + "SHA256" "\0" + "SHA384" "\0" +#if defined(USE_MBEDTLS) + "ARIA" "\0" + "ARIA128" "\0" + "ARIA256" "\0" + "CAMELLIA" "\0" + "CAMELLIA128" "\0" + "CAMELLIA256" "\0" +#endif +; +/* Indexes of above cs_txt */ +enum { + CS_TXT_IDX_, + CS_TXT_IDX_TLS, + CS_TXT_IDX_WITH, + CS_TXT_IDX_128, + CS_TXT_IDX_256, + CS_TXT_IDX_3DES, + CS_TXT_IDX_8, + CS_TXT_IDX_AES, + CS_TXT_IDX_AES128, + CS_TXT_IDX_AES256, + CS_TXT_IDX_CBC, + CS_TXT_IDX_CBC3, + CS_TXT_IDX_CCM, + CS_TXT_IDX_CCM8, + CS_TXT_IDX_CHACHA20, + CS_TXT_IDX_DES, + CS_TXT_IDX_DHE, + CS_TXT_IDX_ECDH, + CS_TXT_IDX_ECDHE, + CS_TXT_IDX_ECDSA, + CS_TXT_IDX_EDE, + CS_TXT_IDX_GCM, + CS_TXT_IDX_MD5, + CS_TXT_IDX_NULL, + CS_TXT_IDX_POLY1305, + CS_TXT_IDX_PSK, + CS_TXT_IDX_RSA, + CS_TXT_IDX_SHA, + CS_TXT_IDX_SHA256, + CS_TXT_IDX_SHA384, +#if defined(USE_MBEDTLS) + CS_TXT_IDX_ARIA, + CS_TXT_IDX_ARIA128, + CS_TXT_IDX_ARIA256, + CS_TXT_IDX_CAMELLIA, + CS_TXT_IDX_CAMELLIA128, + CS_TXT_IDX_CAMELLIA256, +#endif + CS_TXT_LEN, +}; + +#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \ +{ \ + (uint8_t) ((a) << 2 | ((b) & 0x3F) >> 4), \ + (uint8_t) ((b) << 4 | ((c) & 0x3F) >> 2), \ + (uint8_t) ((c) << 6 | ((d) & 0x3F)), \ + (uint8_t) ((e) << 2 | ((f) & 0x3F) >> 4), \ + (uint8_t) ((f) << 4 | ((g) & 0x3F) >> 2), \ + (uint8_t) ((g) << 6 | ((h) & 0x3F)) \ +} +#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \ +{ \ + id, \ + CS_ZIP_IDX( \ + CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \ + CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \ + CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \ + CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \ + ) \ +} + +struct cs_entry { + uint16_t id; + uint8_t zip[6]; +}; + +/* !checksrc! disable COMMANOSPACE all */ +static const struct cs_entry cs_list [] = { + CS_ENTRY(0x002F, TLS,RSA,WITH,AES,128,CBC,SHA,), + CS_ENTRY(0x002F, AES128,SHA,,,,,,), + CS_ENTRY(0x0035, TLS,RSA,WITH,AES,256,CBC,SHA,), + CS_ENTRY(0x0035, AES256,SHA,,,,,,), + CS_ENTRY(0x003C, TLS,RSA,WITH,AES,128,CBC,SHA256,), + CS_ENTRY(0x003C, AES128,SHA256,,,,,,), + CS_ENTRY(0x003D, TLS,RSA,WITH,AES,256,CBC,SHA256,), + CS_ENTRY(0x003D, AES256,SHA256,,,,,,), + CS_ENTRY(0x009C, TLS,RSA,WITH,AES,128,GCM,SHA256,), + CS_ENTRY(0x009C, AES128,GCM,SHA256,,,,,), + CS_ENTRY(0x009D, TLS,RSA,WITH,AES,256,GCM,SHA384,), + CS_ENTRY(0x009D, AES256,GCM,SHA384,,,,,), + CS_ENTRY(0xC004, TLS,ECDH,ECDSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC004, ECDH,ECDSA,AES128,SHA,,,,), + CS_ENTRY(0xC005, TLS,ECDH,ECDSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC005, ECDH,ECDSA,AES256,SHA,,,,), + CS_ENTRY(0xC009, TLS,ECDHE,ECDSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC009, ECDHE,ECDSA,AES128,SHA,,,,), + CS_ENTRY(0xC00A, TLS,ECDHE,ECDSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC00A, ECDHE,ECDSA,AES256,SHA,,,,), + CS_ENTRY(0xC00E, TLS,ECDH,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC00E, ECDH,RSA,AES128,SHA,,,,), + CS_ENTRY(0xC00F, TLS,ECDH,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC00F, ECDH,RSA,AES256,SHA,,,,), + CS_ENTRY(0xC013, TLS,ECDHE,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC013, ECDHE,RSA,AES128,SHA,,,,), + CS_ENTRY(0xC014, TLS,ECDHE,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC014, ECDHE,RSA,AES256,SHA,,,,), + CS_ENTRY(0xC023, TLS,ECDHE,ECDSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC023, ECDHE,ECDSA,AES128,SHA256,,,,), + CS_ENTRY(0xC024, TLS,ECDHE,ECDSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC024, ECDHE,ECDSA,AES256,SHA384,,,,), + CS_ENTRY(0xC025, TLS,ECDH,ECDSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC025, ECDH,ECDSA,AES128,SHA256,,,,), + CS_ENTRY(0xC026, TLS,ECDH,ECDSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC026, ECDH,ECDSA,AES256,SHA384,,,,), + CS_ENTRY(0xC027, TLS,ECDHE,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC027, ECDHE,RSA,AES128,SHA256,,,,), + CS_ENTRY(0xC028, TLS,ECDHE,RSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC028, ECDHE,RSA,AES256,SHA384,,,,), + CS_ENTRY(0xC029, TLS,ECDH,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC029, ECDH,RSA,AES128,SHA256,,,,), + CS_ENTRY(0xC02A, TLS,ECDH,RSA,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC02A, ECDH,RSA,AES256,SHA384,,,,), + CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC02C, TLS,ECDHE,ECDSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC02C, ECDHE,ECDSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC02D, TLS,ECDH,ECDSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02D, ECDH,ECDSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC02E, TLS,ECDH,ECDSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC02E, ECDH,ECDSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC02F, TLS,ECDHE,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02F, ECDHE,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC030, TLS,ECDHE,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC030, ECDHE,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC031, TLS,ECDH,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC031, ECDH,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xCCA8, TLS,ECDHE,RSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,), +#if defined(USE_MBEDTLS) + CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,), + CS_ENTRY(0x0001, NULL,MD5,,,,,,), + CS_ENTRY(0x0002, TLS,RSA,WITH,NULL,SHA,,,), + CS_ENTRY(0x0002, NULL,SHA,,,,,,), + CS_ENTRY(0x002C, TLS,PSK,WITH,NULL,SHA,,,), + CS_ENTRY(0x002C, PSK,NULL,SHA,,,,,), + CS_ENTRY(0x002D, TLS,DHE,PSK,WITH,NULL,SHA,,), + CS_ENTRY(0x002D, DHE,PSK,NULL,SHA,,,,), + CS_ENTRY(0x002E, TLS,RSA,PSK,WITH,NULL,SHA,,), + CS_ENTRY(0x002E, RSA,PSK,NULL,SHA,,,,), + CS_ENTRY(0x0033, TLS,DHE,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0033, DHE,RSA,AES128,SHA,,,,), + CS_ENTRY(0x0039, TLS,DHE,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0039, DHE,RSA,AES256,SHA,,,,), + CS_ENTRY(0x003B, TLS,RSA,WITH,NULL,SHA256,,,), + CS_ENTRY(0x003B, NULL,SHA256,,,,,,), + CS_ENTRY(0x0067, TLS,DHE,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x0067, DHE,RSA,AES128,SHA256,,,,), + CS_ENTRY(0x006B, TLS,DHE,RSA,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x006B, DHE,RSA,AES256,SHA256,,,,), + CS_ENTRY(0x008C, TLS,PSK,WITH,AES,128,CBC,SHA,), + CS_ENTRY(0x008C, PSK,AES128,CBC,SHA,,,,), + CS_ENTRY(0x008D, TLS,PSK,WITH,AES,256,CBC,SHA,), + CS_ENTRY(0x008D, PSK,AES256,CBC,SHA,,,,), + CS_ENTRY(0x0090, TLS,DHE,PSK,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0090, DHE,PSK,AES128,CBC,SHA,,,), + CS_ENTRY(0x0091, TLS,DHE,PSK,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0091, DHE,PSK,AES256,CBC,SHA,,,), + CS_ENTRY(0x0094, TLS,RSA,PSK,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0094, RSA,PSK,AES128,CBC,SHA,,,), + CS_ENTRY(0x0095, TLS,RSA,PSK,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0095, RSA,PSK,AES256,CBC,SHA,,,), + CS_ENTRY(0x009E, TLS,DHE,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x009E, DHE,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0x009F, TLS,DHE,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x009F, DHE,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A8, TLS,PSK,WITH,AES,128,GCM,SHA256,), + CS_ENTRY(0x00A8, PSK,AES128,GCM,SHA256,,,,), + CS_ENTRY(0x00A9, TLS,PSK,WITH,AES,256,GCM,SHA384,), + CS_ENTRY(0x00A9, PSK,AES256,GCM,SHA384,,,,), + CS_ENTRY(0x00AA, TLS,DHE,PSK,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00AA, DHE,PSK,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00AB, TLS,DHE,PSK,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00AB, DHE,PSK,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00AC, TLS,RSA,PSK,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00AC, RSA,PSK,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00AD, TLS,RSA,PSK,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00AD, RSA,PSK,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00AE, TLS,PSK,WITH,AES,128,CBC,SHA256,), + CS_ENTRY(0x00AE, PSK,AES128,CBC,SHA256,,,,), + CS_ENTRY(0x00AF, TLS,PSK,WITH,AES,256,CBC,SHA384,), + CS_ENTRY(0x00AF, PSK,AES256,CBC,SHA384,,,,), + CS_ENTRY(0x00B0, TLS,PSK,WITH,NULL,SHA256,,,), + CS_ENTRY(0x00B0, PSK,NULL,SHA256,,,,,), + CS_ENTRY(0x00B1, TLS,PSK,WITH,NULL,SHA384,,,), + CS_ENTRY(0x00B1, PSK,NULL,SHA384,,,,,), + CS_ENTRY(0x00B2, TLS,DHE,PSK,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x00B2, DHE,PSK,AES128,CBC,SHA256,,,), + CS_ENTRY(0x00B3, TLS,DHE,PSK,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0x00B3, DHE,PSK,AES256,CBC,SHA384,,,), + CS_ENTRY(0x00B4, TLS,DHE,PSK,WITH,NULL,SHA256,,), + CS_ENTRY(0x00B4, DHE,PSK,NULL,SHA256,,,,), + CS_ENTRY(0x00B5, TLS,DHE,PSK,WITH,NULL,SHA384,,), + CS_ENTRY(0x00B5, DHE,PSK,NULL,SHA384,,,,), + CS_ENTRY(0x00B6, TLS,RSA,PSK,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x00B6, RSA,PSK,AES128,CBC,SHA256,,,), + CS_ENTRY(0x00B7, TLS,RSA,PSK,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0x00B7, RSA,PSK,AES256,CBC,SHA384,,,), + CS_ENTRY(0x00B8, TLS,RSA,PSK,WITH,NULL,SHA256,,), + CS_ENTRY(0x00B8, RSA,PSK,NULL,SHA256,,,,), + CS_ENTRY(0x00B9, TLS,RSA,PSK,WITH,NULL,SHA384,,), + CS_ENTRY(0x00B9, RSA,PSK,NULL,SHA384,,,,), + CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), + CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), + CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), + CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,), + CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,), + CS_ENTRY(0xC001, TLS,ECDH,ECDSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC001, ECDH,ECDSA,NULL,SHA,,,,), + CS_ENTRY(0xC006, TLS,ECDHE,ECDSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC006, ECDHE,ECDSA,NULL,SHA,,,,), + CS_ENTRY(0xC00B, TLS,ECDH,RSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC00B, ECDH,RSA,NULL,SHA,,,,), + CS_ENTRY(0xC010, TLS,ECDHE,RSA,WITH,NULL,SHA,,), + CS_ENTRY(0xC010, ECDHE,RSA,NULL,SHA,,,,), + CS_ENTRY(0xC035, TLS,ECDHE,PSK,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC035, ECDHE,PSK,AES128,CBC,SHA,,,), + CS_ENTRY(0xC036, TLS,ECDHE,PSK,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC036, ECDHE,PSK,AES256,CBC,SHA,,,), + CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,), + CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,), +#endif +#if defined(USE_BEARSSL) + CS_ENTRY(0x000A, TLS,RSA,WITH,3DES,EDE,CBC,SHA,), + CS_ENTRY(0x000A, DES,CBC3,SHA,,,,,), + CS_ENTRY(0xC003, TLS,ECDH,ECDSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC003, ECDH,ECDSA,DES,CBC3,SHA,,,), + CS_ENTRY(0xC008, TLS,ECDHE,ECDSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC008, ECDHE,ECDSA,DES,CBC3,SHA,,,), + CS_ENTRY(0xC00D, TLS,ECDH,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC00D, ECDH,RSA,DES,CBC3,SHA,,,), + CS_ENTRY(0xC012, TLS,ECDHE,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC012, ECDHE,RSA,DES,CBC3,SHA,,,), +#endif + CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,), + CS_ENTRY(0xC09C, AES128,CCM,,,,,,), + CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,), + CS_ENTRY(0xC09D, AES256,CCM,,,,,,), + CS_ENTRY(0xC0A0, TLS,RSA,WITH,AES,128,CCM,8,), + CS_ENTRY(0xC0A0, AES128,CCM8,,,,,,), + CS_ENTRY(0xC0A1, TLS,RSA,WITH,AES,256,CCM,8,), + CS_ENTRY(0xC0A1, AES256,CCM8,,,,,,), + CS_ENTRY(0xC0AC, TLS,ECDHE,ECDSA,WITH,AES,128,CCM,), + CS_ENTRY(0xC0AC, ECDHE,ECDSA,AES128,CCM,,,,), + CS_ENTRY(0xC0AD, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,), + CS_ENTRY(0xC0AD, ECDHE,ECDSA,AES256,CCM,,,,), + CS_ENTRY(0xC0AE, TLS,ECDHE,ECDSA,WITH,AES,128,CCM,8), + 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,,,,), +#if defined(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,,,,,,), + CS_ENTRY(0x0045, TLS,DHE,RSA,WITH,CAMELLIA,128,CBC,SHA), + CS_ENTRY(0x0045, DHE,RSA,CAMELLIA128,SHA,,,,), + CS_ENTRY(0x0084, TLS,RSA,WITH,CAMELLIA,256,CBC,SHA,), + CS_ENTRY(0x0084, CAMELLIA256,SHA,,,,,,), + CS_ENTRY(0x0088, TLS,DHE,RSA,WITH,CAMELLIA,256,CBC,SHA), + CS_ENTRY(0x0088, DHE,RSA,CAMELLIA256,SHA,,,,), + CS_ENTRY(0x00BA, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA256,), + CS_ENTRY(0x00BA, CAMELLIA128,SHA256,,,,,,), + CS_ENTRY(0x00BE, TLS,DHE,RSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0x00BE, DHE,RSA,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0x00C0, TLS,RSA,WITH,CAMELLIA,256,CBC,SHA256,), + CS_ENTRY(0x00C0, CAMELLIA256,SHA256,,,,,,), + CS_ENTRY(0x00C4, TLS,DHE,RSA,WITH,CAMELLIA,256,CBC,SHA256), + CS_ENTRY(0x00C4, DHE,RSA,CAMELLIA256,SHA256,,,,), + CS_ENTRY(0xC037, TLS,ECDHE,PSK,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0xC037, ECDHE,PSK,AES128,CBC,SHA256,,,), + CS_ENTRY(0xC038, TLS,ECDHE,PSK,WITH,AES,256,CBC,SHA384), + CS_ENTRY(0xC038, ECDHE,PSK,AES256,CBC,SHA384,,,), + CS_ENTRY(0xC039, TLS,ECDHE,PSK,WITH,NULL,SHA,,), + CS_ENTRY(0xC039, ECDHE,PSK,NULL,SHA,,,,), + CS_ENTRY(0xC03A, TLS,ECDHE,PSK,WITH,NULL,SHA256,,), + CS_ENTRY(0xC03A, ECDHE,PSK,NULL,SHA256,,,,), + CS_ENTRY(0xC03B, TLS,ECDHE,PSK,WITH,NULL,SHA384,,), + CS_ENTRY(0xC03B, ECDHE,PSK,NULL,SHA384,,,,), + CS_ENTRY(0xC03C, TLS,RSA,WITH,ARIA,128,CBC,SHA256,), + CS_ENTRY(0xC03C, ARIA128,SHA256,,,,,,), /* ns */ + CS_ENTRY(0xC03D, TLS,RSA,WITH,ARIA,256,CBC,SHA384,), + CS_ENTRY(0xC03D, ARIA256,SHA384,,,,,,), /* ns */ + CS_ENTRY(0xC044, TLS,DHE,RSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC044, DHE,RSA,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC045, TLS,DHE,RSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC045, DHE,RSA,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC048, TLS,ECDHE,ECDSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC048, ECDHE,ECDSA,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC049, TLS,ECDHE,ECDSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC049, ECDHE,ECDSA,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC04A, TLS,ECDH,ECDSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC04A, ECDH,ECDSA,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC04B, TLS,ECDH,ECDSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC04B, ECDH,ECDSA,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC04C, TLS,ECDHE,RSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC04C, ECDHE,ARIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC04D, TLS,ECDHE,RSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC04D, ECDHE,ARIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC04E, TLS,ECDH,RSA,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC04E, ECDH,ARIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC04F, TLS,ECDH,RSA,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC04F, ECDH,ARIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC050, TLS,RSA,WITH,ARIA,128,GCM,SHA256,), + CS_ENTRY(0xC050, ARIA128,GCM,SHA256,,,,,), + CS_ENTRY(0xC051, TLS,RSA,WITH,ARIA,256,GCM,SHA384,), + CS_ENTRY(0xC051, ARIA256,GCM,SHA384,,,,,), + CS_ENTRY(0xC052, TLS,DHE,RSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC052, DHE,RSA,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC053, TLS,DHE,RSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC053, DHE,RSA,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC05C, TLS,ECDHE,ECDSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC05C, ECDHE,ECDSA,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC05D, TLS,ECDHE,ECDSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC05D, ECDHE,ECDSA,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC05E, TLS,ECDH,ECDSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC05E, ECDH,ECDSA,ARIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC05F, TLS,ECDH,ECDSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC05F, ECDH,ECDSA,ARIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC060, TLS,ECDHE,RSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC060, ECDHE,ARIA128,GCM,SHA256,,,,), + CS_ENTRY(0xC061, TLS,ECDHE,RSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC061, ECDHE,ARIA256,GCM,SHA384,,,,), + CS_ENTRY(0xC062, TLS,ECDH,RSA,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC062, ECDH,ARIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC063, TLS,ECDH,RSA,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC063, ECDH,ARIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC064, TLS,PSK,WITH,ARIA,128,CBC,SHA256,), + CS_ENTRY(0xC064, PSK,ARIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC065, TLS,PSK,WITH,ARIA,256,CBC,SHA384,), + CS_ENTRY(0xC065, PSK,ARIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC066, TLS,DHE,PSK,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC066, DHE,PSK,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC067, TLS,DHE,PSK,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC067, DHE,PSK,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC068, TLS,RSA,PSK,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC068, RSA,PSK,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC069, TLS,RSA,PSK,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC069, RSA,PSK,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC06A, TLS,PSK,WITH,ARIA,128,GCM,SHA256,), + CS_ENTRY(0xC06A, PSK,ARIA128,GCM,SHA256,,,,), + CS_ENTRY(0xC06B, TLS,PSK,WITH,ARIA,256,GCM,SHA384,), + CS_ENTRY(0xC06B, PSK,ARIA256,GCM,SHA384,,,,), + CS_ENTRY(0xC06C, TLS,DHE,PSK,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC06C, DHE,PSK,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC06D, TLS,DHE,PSK,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC06D, DHE,PSK,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC06E, TLS,RSA,PSK,WITH,ARIA,128,GCM,SHA256), + CS_ENTRY(0xC06E, RSA,PSK,ARIA128,GCM,SHA256,,,), + CS_ENTRY(0xC06F, TLS,RSA,PSK,WITH,ARIA,256,GCM,SHA384), + CS_ENTRY(0xC06F, RSA,PSK,ARIA256,GCM,SHA384,,,), + CS_ENTRY(0xC070, TLS,ECDHE,PSK,WITH,ARIA,128,CBC,SHA256), + CS_ENTRY(0xC070, ECDHE,PSK,ARIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC071, TLS,ECDHE,PSK,WITH,ARIA,256,CBC,SHA384), + CS_ENTRY(0xC071, ECDHE,PSK,ARIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC072, TLS,ECDHE,ECDSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC072, ECDHE,ECDSA,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC073, TLS,ECDHE,ECDSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC073, ECDHE,ECDSA,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC074, TLS,ECDH,ECDSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC074, ECDH,ECDSA,CAMELLIA128,SHA256,,,,), /* ns */ + CS_ENTRY(0xC075, TLS,ECDH,ECDSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC075, ECDH,ECDSA,CAMELLIA256,SHA384,,,,), /* ns */ + CS_ENTRY(0xC076, TLS,ECDHE,RSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC076, ECDHE,RSA,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC077, TLS,ECDHE,RSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC077, ECDHE,RSA,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC078, TLS,ECDH,RSA,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC078, ECDH,CAMELLIA128,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC079, TLS,ECDH,RSA,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC079, ECDH,CAMELLIA256,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC07A, TLS,RSA,WITH,CAMELLIA,128,GCM,SHA256,), + CS_ENTRY(0xC07A, CAMELLIA128,GCM,SHA256,,,,,), /* ns */ + CS_ENTRY(0xC07B, TLS,RSA,WITH,CAMELLIA,256,GCM,SHA384,), + CS_ENTRY(0xC07B, CAMELLIA256,GCM,SHA384,,,,,), /* ns */ + CS_ENTRY(0xC07C, TLS,DHE,RSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC07C, DHE,RSA,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC07D, TLS,DHE,RSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC07D, DHE,RSA,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC086, TLS,ECDHE,ECDSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC086, ECDHE,ECDSA,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC087, TLS,ECDHE,ECDSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC087, ECDHE,ECDSA,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC088, TLS,ECDH,ECDSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC088, ECDH,ECDSA,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC089, TLS,ECDH,ECDSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC089, ECDH,ECDSA,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC08A, TLS,ECDHE,RSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC08A, ECDHE,CAMELLIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC08B, TLS,ECDHE,RSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC08B, ECDHE,CAMELLIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC08C, TLS,ECDH,RSA,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC08C, ECDH,CAMELLIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC08D, TLS,ECDH,RSA,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC08D, ECDH,CAMELLIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC08E, TLS,PSK,WITH,CAMELLIA,128,GCM,SHA256,), + CS_ENTRY(0xC08E, PSK,CAMELLIA128,GCM,SHA256,,,,), /* ns */ + CS_ENTRY(0xC08F, TLS,PSK,WITH,CAMELLIA,256,GCM,SHA384,), + CS_ENTRY(0xC08F, PSK,CAMELLIA256,GCM,SHA384,,,,), /* ns */ + CS_ENTRY(0xC090, TLS,DHE,PSK,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC090, DHE,PSK,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC091, TLS,DHE,PSK,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC091, DHE,PSK,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC092, TLS,RSA,PSK,WITH,CAMELLIA,128,GCM,SHA256), + CS_ENTRY(0xC092, RSA,PSK,CAMELLIA128,GCM,SHA256,,,), /* ns */ + CS_ENTRY(0xC093, TLS,RSA,PSK,WITH,CAMELLIA,256,GCM,SHA384), + CS_ENTRY(0xC093, RSA,PSK,CAMELLIA256,GCM,SHA384,,,), /* ns */ + CS_ENTRY(0xC094, TLS,PSK,WITH,CAMELLIA,128,CBC,SHA256,), + CS_ENTRY(0xC094, PSK,CAMELLIA128,SHA256,,,,,), + CS_ENTRY(0xC095, TLS,PSK,WITH,CAMELLIA,256,CBC,SHA384,), + CS_ENTRY(0xC095, PSK,CAMELLIA256,SHA384,,,,,), + CS_ENTRY(0xC096, TLS,DHE,PSK,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC096, DHE,PSK,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC097, TLS,DHE,PSK,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC097, DHE,PSK,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC098, TLS,RSA,PSK,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC098, RSA,PSK,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC099, TLS,RSA,PSK,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC099, RSA,PSK,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC09A, TLS,ECDHE,PSK,WITH,CAMELLIA,128,CBC,SHA256), + CS_ENTRY(0xC09A, ECDHE,PSK,CAMELLIA128,SHA256,,,,), + CS_ENTRY(0xC09B, TLS,ECDHE,PSK,WITH,CAMELLIA,256,CBC,SHA384), + CS_ENTRY(0xC09B, ECDHE,PSK,CAMELLIA256,SHA384,,,,), + CS_ENTRY(0xC09E, TLS,DHE,RSA,WITH,AES,128,CCM,), + CS_ENTRY(0xC09E, DHE,RSA,AES128,CCM,,,,), + CS_ENTRY(0xC09F, TLS,DHE,RSA,WITH,AES,256,CCM,), + CS_ENTRY(0xC09F, DHE,RSA,AES256,CCM,,,,), + CS_ENTRY(0xC0A2, TLS,DHE,RSA,WITH,AES,128,CCM,8), + CS_ENTRY(0xC0A2, DHE,RSA,AES128,CCM8,,,,), + CS_ENTRY(0xC0A3, TLS,DHE,RSA,WITH,AES,256,CCM,8), + CS_ENTRY(0xC0A3, DHE,RSA,AES256,CCM8,,,,), + CS_ENTRY(0xC0A4, TLS,PSK,WITH,AES,128,CCM,,), + CS_ENTRY(0xC0A4, PSK,AES128,CCM,,,,,), + CS_ENTRY(0xC0A5, TLS,PSK,WITH,AES,256,CCM,,), + CS_ENTRY(0xC0A5, PSK,AES256,CCM,,,,,), + CS_ENTRY(0xC0A6, TLS,DHE,PSK,WITH,AES,128,CCM,), + CS_ENTRY(0xC0A6, DHE,PSK,AES128,CCM,,,,), + CS_ENTRY(0xC0A7, TLS,DHE,PSK,WITH,AES,256,CCM,), + CS_ENTRY(0xC0A7, DHE,PSK,AES256,CCM,,,,), + CS_ENTRY(0xC0A8, TLS,PSK,WITH,AES,128,CCM,8,), + CS_ENTRY(0xC0A8, PSK,AES128,CCM8,,,,,), + CS_ENTRY(0xC0A9, TLS,PSK,WITH,AES,256,CCM,8,), + CS_ENTRY(0xC0A9, PSK,AES256,CCM8,,,,,), + CS_ENTRY(0xC0AA, TLS,PSK,DHE,WITH,AES,128,CCM,8), + CS_ENTRY(0xC0AA, DHE,PSK,AES128,CCM8,,,,), + CS_ENTRY(0xC0AB, TLS,PSK,DHE,WITH,AES,256,CCM,8), + CS_ENTRY(0xC0AB, DHE,PSK,AES256,CCM8,,,,), + CS_ENTRY(0xCCAA, TLS,DHE,RSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAA, DHE,RSA,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCAC, TLS,ECDHE,PSK,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAC, ECDHE,PSK,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCAD, TLS,DHE,PSK,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAD, DHE,PSK,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCAE, TLS,RSA,PSK,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCAE, RSA,PSK,CHACHA20,POLY1305,,,,), +#endif +}; +#define CS_LIST_LEN (sizeof(cs_list) / sizeof(cs_list[0])) + +static int cs_str_to_zip(const char *cs_str, size_t cs_len, + uint8_t zip[6]) +{ + uint8_t indexes[8] = {0}; + const char *entry, *cur; + const char *nxt = cs_str; + const char *end = cs_str + cs_len; + char separator = '-'; + int idx, i = 0; + size_t len; + + /* split the cipher string by '-' or '_' */ + if(strncasecompare(cs_str, "TLS", 3)) + separator = '_'; + + do { + if(i == 8) + return -1; + + /* determine the length of the part */ + cur = nxt; + for(; nxt < end && *nxt != '\0' && *nxt != separator; nxt++); + len = nxt - cur; + + /* lookup index for the part (skip empty string at 0) */ + for(idx = 1, entry = cs_txt + 1; idx < CS_TXT_LEN; idx++) { + size_t elen = strlen(entry); + if(elen == len && strncasecompare(entry, cur, len)) + break; + entry += elen + 1; + } + if(idx == CS_TXT_LEN) + return -1; + + indexes[i++] = (uint8_t) idx; + } while(nxt < end && *(nxt++) != '\0'); + + /* zip the 8 indexes into 48 bits */ + zip[0] = (uint8_t) (indexes[0] << 2 | (indexes[1] & 0x3F) >> 4); + zip[1] = (uint8_t) (indexes[1] << 4 | (indexes[2] & 0x3F) >> 2); + zip[2] = (uint8_t) (indexes[2] << 6 | (indexes[3] & 0x3F)); + zip[3] = (uint8_t) (indexes[4] << 2 | (indexes[5] & 0x3F) >> 4); + zip[4] = (uint8_t) (indexes[5] << 4 | (indexes[6] & 0x3F) >> 2); + zip[5] = (uint8_t) (indexes[6] << 6 | (indexes[7] & 0x3F)); + + return 0; +} + +static int cs_zip_to_str(const uint8_t zip[6], + char *buf, size_t buf_size) +{ + uint8_t indexes[8] = {0}; + const char *entry; + char separator = '-'; + int idx, i, r; + size_t len = 0; + + /* unzip the 8 indexes */ + indexes[0] = zip[0] >> 2; + indexes[1] = ((zip[0] << 4) & 0x3F) | zip[1] >> 4; + indexes[2] = ((zip[1] << 2) & 0x3F) | zip[2] >> 6; + indexes[3] = ((zip[2] << 0) & 0x3F); + indexes[4] = zip[3] >> 2; + indexes[5] = ((zip[3] << 4) & 0x3F) | zip[4] >> 4; + indexes[6] = ((zip[4] << 2) & 0x3F) | zip[5] >> 6; + indexes[7] = ((zip[5] << 0) & 0x3F); + + if(indexes[0] == CS_TXT_IDX_TLS) + separator = '_'; + + for(i = 0; i < 8 && indexes[i] != 0 && len < buf_size; i++) { + if(indexes[i] >= CS_TXT_LEN) + return -1; + + /* lookup the part string for the index (skip empty string at 0) */ + for(idx = 1, entry = cs_txt + 1; idx < indexes[i]; idx++) { + size_t elen = strlen(entry); + entry += elen + 1; + } + + /* append the part string to the buffer */ + if(i > 0) + r = msnprintf(&buf[len], buf_size - len, "%c%s", separator, entry); + else + r = msnprintf(&buf[len], buf_size - len, "%s", entry); + + if(r < 0) + return -1; + len += r; + } + + return 0; +} + +uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len) +{ + size_t i; + uint8_t zip[6]; + + if(cs_len > 0 && cs_str_to_zip(cs_str, cs_len, zip) == 0) { + for(i = 0; i < CS_LIST_LEN; i++) { + if(memcmp(cs_list[i].zip, zip, sizeof(zip)) == 0) + return cs_list[i].id; + } + } + + return 0; +} + +static bool cs_is_separator(char c) +{ + switch(c) { + case ' ': + case '\t': + case ':': + case ',': + case ';': + return true; + default:; + } + return false; +} + +uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end) +{ + /* move string pointer to first non-separator or end of string */ + for(; cs_is_separator(*str[0]); (*str)++); + + /* move end pointer to next separator or end of string */ + for(*end = *str; *end[0] != '\0' && !cs_is_separator(*end[0]); (*end)++); + + return Curl_cipher_suite_lookup_id(*str, *end - *str); +} + +int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, + bool prefer_rfc) +{ + size_t i, j = CS_LIST_LEN; + int r = -1; + + for(i = 0; i < CS_LIST_LEN; i++) { + if(cs_list[i].id != id) + continue; + if((cs_list[i].zip[0] >> 2 != CS_TXT_IDX_TLS) == !prefer_rfc) { + j = i; + break; + } + if(j == CS_LIST_LEN) + j = i; + } + + if(j < CS_LIST_LEN) + r = cs_zip_to_str(cs_list[j].zip, buf, buf_size); + + if(r < 0) + msnprintf(buf, buf_size, "TLS_UNKNOWN_0x%04x", id); + + return r; +} + +#endif /* defined(USE_MBEDTLS) || defined(USE_BEARSSL) */
diff --git a/Utilities/cmcurl/lib/vtls/cipher_suite.h b/Utilities/cmcurl/lib/vtls/cipher_suite.h new file mode 100644 index 0000000..c139979 --- /dev/null +++ b/Utilities/cmcurl/lib/vtls/cipher_suite.h
@@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_CIPHER_SUITE_H +#define HEADER_CURL_CIPHER_SUITE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jan Venekamp, <jan@venekamp.net> + * + * 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(USE_MBEDTLS) || defined(USE_BEARSSL) +#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); + +/* Walk over cipher suite string, update str and end pointers to next + cipher suite in string, returns IANA id of that suite if recognized */ +uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end); + +/* Copy openssl or RFC name for cipher suite in supplied buffer. + Caller is responsible to supply sufficiently large buffer (size + of 64 should suffice), excess bytes are silently truncated. */ +int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, + bool prefer_rfc); + +#endif /* defined(USE_MBEDTLS) || defined(USE_BEARSSL) */ +#endif /* HEADER_CURL_CIPHER_SUITE_H */
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index b95c5be..5cf3bf9 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -43,6 +43,7 @@ #include "urldata.h" #include "sendf.h" #include "inet_pton.h" +#include "keylog.h" #include "gtls.h" #include "vtls.h" #include "vtls_int.h" @@ -59,6 +60,16 @@ /* The last #include file should be: */ #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#define QUIC_PRIORITY \ + "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ + "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ + "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ + "%DISABLE_TLS13_COMPAT_MODE" + /* Enable GnuTLS debugging by defining GTLSDEBUG */ /*#define GTLSDEBUG */ @@ -77,22 +88,25 @@ # include <gnutls/ocsp.h> struct gtls_ssl_backend_data { - struct gtls_instance gtls; + struct gtls_ctx gtls; }; static ssize_t gtls_push(void *s, const void *buf, size_t blen) { struct Curl_cfilter *cf = s; struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d", + blen, nwritten, result); + backend->gtls.io_result = result; if(nwritten < 0) { - struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; gnutls_transport_set_errno(backend->gtls.session, (CURLE_AGAIN == result)? EAGAIN : EINVAL); nwritten = -1; @@ -104,19 +118,33 @@ { struct Curl_cfilter *cf = s; struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result; DEBUGASSERT(data); + if(!backend->gtls.trust_setup) { + result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls); + if(result) { + gnutls_transport_set_errno(backend->gtls.session, EINVAL); + backend->gtls.io_result = result; + return -1; + } + } + nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %zd, err=%d", + blen, nread, result); + backend->gtls.io_result = result; if(nread < 0) { - struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; gnutls_transport_set_errno(backend->gtls.session, (CURLE_AGAIN == result)? EAGAIN : EINVAL); nread = -1; } + else if(nread == 0) + connssl->peer_closed = TRUE; return nread; } @@ -266,8 +294,17 @@ /* socket is readable or writable */ } + backend->gtls.io_result = CURLE_OK; rc = gnutls_handshake(session); + if(!backend->gtls.trust_setup) { + /* After having send off the ClientHello, we prepare the trust + * store to verify the coming certificate from the server */ + CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls); + if(result) + return result; + } + if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { connssl->connecting_state = gnutls_record_get_direction(session)? @@ -288,6 +325,9 @@ infof(data, "gnutls_handshake() warning: %s", strerr); continue; } + else if((rc < 0) && backend->gtls.io_result) { + return backend->gtls.io_result; + } else if(rc < 0) { const char *strerr = NULL; @@ -328,6 +368,7 @@ static CURLcode set_ssl_version_min_max(struct Curl_easy *data, + struct ssl_peer *peer, struct ssl_primary_config *conn_config, const char **prioritylist, const char *tls13support) @@ -335,6 +376,16 @@ long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; + if(peer->transport == TRNSPRT_QUIC) { + if((ssl_version != CURL_SSLVERSION_DEFAULT) && + (ssl_version < CURL_SSLVERSION_TLSv1_3)) { + failf(data, "QUIC needs at least TLS version 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } + *prioritylist = QUIC_PRIORITY; + return CURLE_OK; + } + if((ssl_version == CURL_SSLVERSION_DEFAULT) || (ssl_version == CURL_SSLVERSION_TLSv1)) ssl_version = CURL_SSLVERSION_TLSv1_0; @@ -399,13 +450,177 @@ return CURLE_SSL_CONNECT_ERROR; } -CURLcode gtls_client_init(struct Curl_easy *data, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - struct ssl_peer *peer, - struct gtls_instance *gtls, - long *pverifyresult) +CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_ctx *gtls) { + struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + int rc; + + CURL_TRC_CF(data, cf, "setup trust anchors and CRLs"); + if(config->verifypeer) { + bool imported_native_ca = false; + + if(ssl_config->native_ca_store) { + rc = gnutls_certificate_set_x509_system_trust(gtls->cred); + if(rc < 0) + infof(data, "error reading native ca store (%s), continuing anyway", + gnutls_strerror(rc)); + else { + infof(data, "found %d certificates in native ca store", rc); + if(rc > 0) + imported_native_ca = true; + } + } + + if(config->CAfile) { + /* set the trusted CA cert bundle file */ + gnutls_certificate_set_verify_flags(gtls->cred, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + + rc = gnutls_certificate_set_x509_trust_file(gtls->cred, + config->CAfile, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CAfile, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, "found %d certificates in %s", rc, config->CAfile); + } + + if(config->CApath) { + /* set the trusted CA cert directory */ + rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, + config->CApath, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + infof(data, "error reading ca cert file %s (%s)%s", + config->CApath, gnutls_strerror(rc), + (imported_native_ca ? ", continuing anyway" : "")); + if(!imported_native_ca) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, "found %d certificates in %s", rc, config->CApath); + } + } + + if(config->CRLfile) { + /* set the CRL list file */ + rc = gnutls_certificate_set_x509_crl_file(gtls->cred, + config->CRLfile, + GNUTLS_X509_FMT_PEM); + if(rc < 0) { + failf(data, "error reading crl file %s (%s)", + config->CRLfile, gnutls_strerror(rc)); + return CURLE_SSL_CRL_BADFILE; + } + else + infof(data, "found %d CRL in %s", rc, config->CRLfile); + } + + gtls->trust_setup = TRUE; + return CURLE_OK; +} + +static void gtls_sessionid_free(void *sessionid, size_t idsize) +{ + (void)idsize; + free(sessionid); +} + +static CURLcode gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + if(ssl_config->primary.sessionid) { + /* we always unconditionally get the session id here, as even if we + already got it from the cache and asked to use it in the connection, it + might've been rejected and then a new one is in use now and we need to + detect that. */ + void *connect_sessionid; + size_t connect_idsize = 0; + + /* get the session ID data size */ + gnutls_session_get_data(session, NULL, &connect_idsize); + connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ + if(!connect_sessionid) { + return CURLE_OUT_OF_MEMORY; + } + else { + bool incache; + void *ssl_sessionid; + + /* extract session ID to the allocated buffer */ + gnutls_session_get_data(session, connect_sessionid, &connect_idsize); + + DEBUGF(infof(data, "get session id (len=%zu) and store in cache", + connect_idsize)); + Curl_ssl_sessionid_lock(data); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + &ssl_sessionid, NULL)); + if(incache) { + /* there was one before in the cache, so instead of risking that the + previous one was rejected, we just kill that and store the new */ + Curl_ssl_delsessionid(data, ssl_sessionid); + } + + /* store this session id, takes ownership */ + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, + connect_sessionid, connect_idsize, + gtls_sessionid_free); + Curl_ssl_sessionid_unlock(data); + } + } + return result; +} + +static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, + const gnutls_datum_t *msg) +{ + struct Curl_cfilter *cf = gnutls_session_get_ptr(session); + + (void)msg; + (void)incoming; + if(when) { /* after message has been processed */ + struct Curl_easy *data = CF_DATA_CURRENT(cf); + if(data) { + DEBUGF(infof(data, "handshake: %s message type %d", + incoming? "incoming" : "outgoing", htype)); + switch(htype) { + case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { + gtls_update_session_id(cf, data, session); + break; + } + default: + break; + } + } + } + return 0; +} + +static CURLcode gtls_client_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct gtls_ctx *gtls) +{ + struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); unsigned int init_flags; int rc; bool sni = TRUE; /* default is SNI enabled */ @@ -417,8 +632,6 @@ if(!gtls_inited) gtls_init(); - *pverifyresult = 0; - if(config->version == CURL_SSLVERSION_SSLv2) { failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; @@ -455,74 +668,7 @@ } #endif - if(config->verifypeer) { - bool imported_native_ca = false; - - if(ssl_config->native_ca_store) { - rc = gnutls_certificate_set_x509_system_trust(gtls->cred); - if(rc < 0) - infof(data, "error reading native ca store (%s), continuing anyway", - gnutls_strerror(rc)); - else { - infof(data, "found %d certificates in native ca store", rc); - if(rc > 0) - imported_native_ca = true; - } - } - - if(config->CAfile) { - /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(gtls->cred, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - - rc = gnutls_certificate_set_x509_trust_file(gtls->cred, - config->CAfile, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)%s", - config->CAfile, gnutls_strerror(rc), - (imported_native_ca ? ", continuing anyway" : "")); - if(!imported_native_ca) { - *pverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", rc, config->CAfile); - } - - if(config->CApath) { - /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, - config->CApath, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)%s", - config->CApath, gnutls_strerror(rc), - (imported_native_ca ? ", continuing anyway" : "")); - if(!imported_native_ca) { - *pverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", rc, config->CApath); - } - } - - if(config->CRLfile) { - /* set the CRL list file */ - rc = gnutls_certificate_set_x509_crl_file(gtls->cred, - config->CRLfile, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - failf(data, "error reading crl file %s (%s)", - config->CRLfile, gnutls_strerror(rc)); - return CURLE_SSL_CRL_BADFILE; - } - else - infof(data, "found %d CRL in %s", rc, config->CRLfile); - } + ssl_config->certverifyresult = 0; /* Initialize TLS session as a client */ init_flags = GNUTLS_CLIENT; @@ -576,7 +722,8 @@ } /* At this point we know we have a supported TLS version, so set it */ - result = set_ssl_version_min_max(data, config, &prioritylist, tls13support); + result = set_ssl_version_min_max(data, peer, + config, &prioritylist, tls13support); if(result) return result; @@ -609,6 +756,11 @@ } if(config->clientcert) { + if(!gtls->trust_setup) { + result = Curl_gtls_client_trust_setup(cf, data, gtls); + if(result) + return result; + } if(ssl_config->key_passwd) { const unsigned int supported_key_encryption_algorithms = GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | @@ -675,46 +827,78 @@ return CURLE_OK; } -static CURLcode -gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +static int keylog_callback(gnutls_session_t session, const char *label, + const gnutls_datum_t *secret) { - struct ssl_connect_data *connssl = cf->ctx; - struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; + gnutls_datum_t crandom; + gnutls_datum_t srandom; + + gnutls_session_get_random(session, &crandom, &srandom); + if(crandom.size != 32) { + return -1; + } + + Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); + return 0; +} + +CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const unsigned char *alpn, size_t alpn_len, + Curl_gtls_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data) +{ 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); - long * const pverifyresult = &ssl_config->certverifyresult; CURLcode result; - DEBUGASSERT(backend); + DEBUGASSERT(gctx); - if(connssl->state == ssl_connection_complete) - /* to make us tolerant against being called more than once for the - same connection */ - return CURLE_OK; - - result = gtls_client_init(data, conn_config, ssl_config, - &connssl->peer, - &backend->gtls, pverifyresult); + result = gtls_client_init(cf, data, peer, gctx); if(result) return result; - if(connssl->alpn) { - struct alpn_proto_buf proto; - gnutls_datum_t alpn[ALPN_ENTRIES_MAX]; - size_t i; + gnutls_session_set_ptr(gctx->session, ssl_user_data); - for(i = 0; i < connssl->alpn->count; ++i) { - alpn[i].data = (unsigned char *)connssl->alpn->entries[i]; - alpn[i].size = (unsigned)strlen(connssl->alpn->entries[i]); + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + return result; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + gnutls_session_set_keylog_function(gctx->session, keylog_callback); + } + + /* convert the ALPN string from our arguments to a list of strings + * that gnutls wants and will convert internally back to this very + * string for sending to the server. nice. */ + if(alpn && alpn_len) { + gnutls_datum_t alpns[5]; + size_t i, alen = alpn_len; + unsigned char *s = (unsigned char *)alpn; + unsigned char slen; + for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) { + slen = s[0]; + if(slen >= alen) + return CURLE_FAILED_INIT; + alpns[i].data = s + 1; + alpns[i].size = slen; + s += slen + 1; + alen -= (size_t)slen + 1; } - if(gnutls_alpn_set_protocols(backend->gtls.session, alpn, - (unsigned)connssl->alpn->count, 0)) { + if(alen) /* not all alpn chars used, wrong format or too many */ + return CURLE_FAILED_INIT; + if(i && gnutls_alpn_set_protocols(gctx->session, + alpns, (unsigned int)i, + GNUTLS_ALPN_MANDATORY)) { failf(data, "failed setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } /* This might be a reconnect, so we check for a session ID in the cache @@ -724,16 +908,55 @@ size_t ssl_idsize; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) { + if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ - gnutls_session_set_data(backend->gtls.session, - ssl_sessionid, ssl_idsize); + int rc; - /* Informational message */ - infof(data, "SSL reusing session ID"); + rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize); + if(rc < 0) + infof(data, "SSL failed to set session ID"); + else + infof(data, "SSL reusing session ID (size=%zu)", ssl_idsize); } Curl_ssl_sessionid_unlock(data); } + return CURLE_OK; +} + +static CURLcode +gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + struct alpn_proto_buf proto; + CURLcode result; + + DEBUGASSERT(backend); + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + + if(connssl->state == ssl_connection_complete) + /* to make us tolerant against being called more than once for the + same connection */ + return CURLE_OK; + + memset(&proto, 0, sizeof(proto)); + if(connssl->alpn) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error determining ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer, + proto.data, proto.len, NULL, NULL, cf); + if(result) + return result; + + gnutls_handshake_set_hook_function(backend->gtls.session, + GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, + gtls_handshake_cb); /* register callback functions and handle to send and receive data. */ gnutls_transport_set_ptr(backend->gtls.session, cf); @@ -1069,7 +1292,7 @@ /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP addresses. */ if(!rc) { -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 #define use_addr in6_addr #else #define use_addr in_addr @@ -1079,7 +1302,7 @@ if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0) addrlen = 4; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0) addrlen = 16; #endif @@ -1243,9 +1466,13 @@ struct ssl_connect_data *connssl = cf->ctx; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); +#ifndef CURL_DISABLE_PROXY const char *pinned_key = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char *pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif CURLcode result; result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config, @@ -1264,47 +1491,10 @@ Curl_alpn_set_negotiated(cf, data, NULL, 0); } - if(ssl_config->primary.sessionid) { - /* we always unconditionally get the session id here, as even if we - already got it from the cache and asked to use it in the connection, it - might've been rejected and then a new one is in use now and we need to - detect that. */ - void *connect_sessionid; - size_t connect_idsize = 0; - - /* get the session ID data size */ - gnutls_session_get_data(session, NULL, &connect_idsize); - connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ - - if(connect_sessionid) { - bool incache; - bool added = FALSE; - void *ssl_sessionid; - - /* extract session ID to the allocated buffer */ - gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)); - if(incache) { - /* there was one before in the cache, so instead of risking that the - previous one was rejected, we just kill that and store the new */ - Curl_ssl_delsessionid(data, ssl_sessionid); - } - - /* store this session id */ - result = Curl_ssl_addsessionid(cf, data, connect_sessionid, - connect_idsize, &added); - Curl_ssl_sessionid_unlock(data); - if(!added) - free(connect_sessionid); - if(result) { - result = CURLE_OUT_OF_MEMORY; - } - } - else - result = CURLE_OUT_OF_MEMORY; - } + /* Only on TLSv1.2 or lower do we have the session id now. For + * TLSv1.3 we get it via a SESSION_TICKET message that arrives later. */ + if(gnutls_protocol_get_version(session) < GNUTLS_TLS1_3) + result = gtls_update_session_id(cf, data, session); out: return result; @@ -1416,12 +1606,13 @@ (void)data; DEBUGASSERT(backend); + backend->gtls.io_result = CURLE_OK; rc = gnutls_record_send(backend->gtls.session, mem, len); if(rc < 0) { - *curlcode = (rc == GNUTLS_E_AGAIN) - ? CURLE_AGAIN - : CURLE_SEND_ERROR; + *curlcode = (rc == GNUTLS_E_AGAIN)? + CURLE_AGAIN : + (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR); rc = -1; } @@ -1489,7 +1680,7 @@ bool done = FALSE; char buf[120]; - while(!done) { + while(!done && !connssl->peer_closed) { int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); if(what > 0) { @@ -1557,6 +1748,7 @@ (void)data; DEBUGASSERT(backend); + backend->gtls.io_result = CURLE_OK; ret = gnutls_record_recv(backend->gtls.session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; @@ -1581,7 +1773,8 @@ failf(data, "GnuTLS recv error (%d): %s", (int)ret, gnutls_strerror((int)ret)); - *curlcode = CURLE_RECV_ERROR; + *curlcode = backend->gtls.io_result? + backend->gtls.io_result : CURLE_RECV_ERROR; ret = -1; goto out; } @@ -1590,11 +1783,6 @@ return ret; } -static void gtls_session_free(void *ptr) -{ - free(ptr); -} - static size_t gtls_version(char *buffer, size_t size) { return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); @@ -1661,7 +1849,6 @@ gtls_get_internals, /* get_internals */ gtls_close, /* close_one */ Curl_none_close_all, /* close_all */ - gtls_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index 1a81c01..f8388b3 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -45,29 +45,39 @@ struct ssl_config_data; struct ssl_peer; -struct gtls_instance { +struct gtls_ctx { gnutls_session_t session; gnutls_certificate_credentials_t cred; #ifdef USE_GNUTLS_SRP gnutls_srp_client_credentials_t srp_client_cred; #endif + CURLcode io_result; /* result of last IO cfilter operation */ + BIT(trust_setup); /* x509 anchors + CRLs have been set up */ }; -CURLcode -gtls_client_init(struct Curl_easy *data, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - struct ssl_peer *peer, - struct gtls_instance *gtls, - long *pverifyresult); +typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data); -CURLcode -Curl_gtls_verifyserver(struct Curl_easy *data, - gnutls_session_t session, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - struct ssl_peer *peer, - const char *pinned_key); +CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const unsigned char *alpn, size_t alpn_len, + Curl_gtls_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data); + +CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_ctx *gtls); + +CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, + gnutls_session_t session, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + struct ssl_peer *peer, + const char *pinned_key); extern const struct Curl_ssl Curl_ssl_gnutls;
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c index fbcb25c..ab7baaa 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.c +++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -24,6 +24,7 @@ #include "curl_setup.h" #if defined(USE_OPENSSL) || \ + defined(USE_GNUTLS) || \ defined(USE_WOLFSSL) || \ (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ defined(USE_QUICHE)
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 7d70de5..ec0b10d 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -67,6 +67,8 @@ #pragma GCC diagnostic pop #endif +#include "cipher_suite.h" +#include "strcase.h" #include "urldata.h" #include "sendf.h" #include "inet_pton.h" @@ -107,10 +109,12 @@ #ifdef HAS_ALPN const char *protocols[3]; #endif + int *ciphersuites; }; /* apply threading? */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ + defined(_WIN32) #define THREADING_SUPPORT #endif @@ -118,12 +122,15 @@ #define mbedtls_strerror(a,b,c) b[0] = 0 #endif +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000 +#define TLS13_SUPPORT +#endif + #if defined(THREADING_SUPPORT) static mbedtls_entropy_context ts_entropy; static int entropy_init_initialized = 0; -/* start of entropy_init_mutex() */ static void entropy_init_mutex(mbedtls_entropy_context *ctx) { /* lock 0 = entropy_init_mutex() */ @@ -134,9 +141,18 @@ } Curl_mbedtlsthreadlock_unlock_function(0); } -/* end of entropy_init_mutex() */ -/* start of entropy_func_mutex() */ +static void entropy_cleanup_mutex(mbedtls_entropy_context *ctx) +{ + /* lock 0 = use same lock as init */ + Curl_mbedtlsthreadlock_lock_function(0); + if(entropy_init_initialized == 1) { + mbedtls_entropy_free(ctx); + entropy_init_initialized = 0; + } + Curl_mbedtlsthreadlock_unlock_function(0); +} + static int entropy_func_mutex(void *data, unsigned char *output, size_t len) { int ret; @@ -147,7 +163,6 @@ return ret; } -/* end of entropy_func_mutex() */ #endif /* THREADING_SUPPORT */ @@ -155,15 +170,18 @@ static void mbed_debug(void *context, int level, const char *f_name, int line_nb, const char *line) { - struct Curl_easy *data = NULL; - - if(!context) - return; - - data = (struct Curl_easy *)context; - - infof(data, "%s", line); + struct Curl_easy *data = (struct Curl_easy *)context; (void) level; + (void) line_nb; + (void) f_name; + + if(data) { + size_t len = strlen(line); + if(len && (line[len - 1] == '\n')) + /* discount any trailing newline */ + len--; + infof(data, "%.*s", (int)len, line); + } } #endif @@ -237,6 +255,28 @@ #define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 +static CURLcode mbedtls_version_from_curl( + mbedtls_ssl_protocol_version* mbedver, long version) +{ + switch(version) { + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + *mbedver = MBEDTLS_SSL_VERSION_TLS1_2; + return CURLE_OK; + case CURL_SSLVERSION_TLSv1_3: +#ifdef TLS13_SUPPORT + *mbedver = MBEDTLS_SSL_VERSION_TLS1_3; + return CURLE_OK; +#else + break; +#endif + } + + return CURLE_SSL_CONNECT_ERROR; +} +#else static CURLcode mbedtls_version_from_curl(int *mbedver, long version) { #if MBEDTLS_VERSION_NUMBER >= 0x03000000 @@ -267,6 +307,7 @@ return CURLE_SSL_CONNECT_ERROR; } +#endif static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -275,7 +316,14 @@ struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + mbedtls_ssl_protocol_version mbedtls_ver_min = MBEDTLS_SSL_VERSION_TLS1_2; +#ifdef TLS13_SUPPORT + mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_3; +#else + mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_2; +#endif +#elif MBEDTLS_VERSION_NUMBER >= 0x03000000 int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3; int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3; #else @@ -298,7 +346,11 @@ switch(ssl_version_max) { case CURL_SSLVERSION_MAX_NONE: case CURL_SSLVERSION_MAX_DEFAULT: +#ifdef TLS13_SUPPORT + ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; +#else ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; +#endif break; } @@ -313,14 +365,123 @@ return result; } +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + mbedtls_ssl_conf_min_tls_version(&backend->config, mbedtls_ver_min); + mbedtls_ssl_conf_max_tls_version(&backend->config, mbedtls_ver_max); +#else mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, mbedtls_ver_min); mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, mbedtls_ver_max); +#endif + +#ifdef TLS13_SUPPORT + if(mbedtls_ver_min == MBEDTLS_SSL_VERSION_TLS1_3) { + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_REQUIRED); + } + else { + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); + } +#else + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); +#endif return result; } +/* 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) +{ + if(id == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) + msnprintf(buf, buf_size, "%s", "TLS_ECJPAKE_WITH_AES_128_CCM_8"); + else + return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); + return 0; +} + +static uint16_t +mbed_cipher_suite_walk_str(const char **str, const char **end) +{ + uint16_t id = Curl_cipher_suite_walk_str(str, end); + size_t len = *end - *str; + + if(!id) { + if(strncasecompare("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) + id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; + } + return id; +} +#else +#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str +#define mbed_cipher_suite_walk_str Curl_cipher_suite_walk_str +#endif + +static CURLcode +mbed_set_selected_ciphers(struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + const char *ciphers) +{ + const int *supported; + int *selected; + size_t supported_len, count = 0, i; + const char *ptr, *end; + + supported = mbedtls_ssl_list_ciphersuites(); + for(i = 0; supported[i] != 0; i++); + supported_len = i; + + selected = malloc(sizeof(int) * (supported_len + 1)); + if(!selected) + return CURLE_OUT_OF_MEMORY; + + for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { + uint16_t id = mbed_cipher_suite_walk_str(&ptr, &end); + + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < supported_len && supported[i] != id; i++); + if(i == supported_len) + id = 0; + } + if(!id) { + if(ptr[0] != '\0') + infof(data, "mbedTLS: unknown cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) { + infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + selected[count++] = id; + } + + selected[count] = 0; + + if(count == 0) { + free(selected); + failf(data, "mbedTLS: no supported cipher in list"); + return CURLE_SSL_CIPHER; + } + + /* mbedtls_ssl_conf_ciphersuites(): The ciphersuites array is not copied. + It must remain valid for the lifetime of the SSL configuration */ + backend->ciphersuites = selected; + mbedtls_ssl_conf_ciphersuites(&backend->config, backend->ciphersuites); + return CURLE_OK; +} + static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -350,8 +511,17 @@ return CURLE_NOT_BUILT_IN; } +#ifdef TLS13_SUPPORT + ret = psa_crypto_init(); + if(ret != PSA_SUCCESS) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "mbedTLS psa_crypto_init returned (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* TLS13_SUPPORT */ + #ifdef THREADING_SUPPORT - entropy_init_mutex(&ts_entropy); mbedtls_ctr_drbg_init(&backend->ctr_drbg); ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, @@ -556,7 +726,7 @@ } #endif - infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->port); + infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -569,10 +739,6 @@ } mbedtls_ssl_init(&backend->ssl); - if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { - failf(data, "mbedTLS: ssl_init failed"); - return CURLE_SSL_CONNECT_ERROR; - } /* new profile with RSA min key len = 1024 ... */ mbedtls_ssl_conf_cert_profile(&backend->config, @@ -602,17 +768,34 @@ return CURLE_SSL_CONNECT_ERROR; } - mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, &backend->ctr_drbg); + + ret = mbedtls_ssl_setup(&backend->ssl, &backend->config); + if(ret) { + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "ssl_setup failed - mbedTLS: (-0x%04X) %s", + -ret, errorbuf); + return CURLE_SSL_CONNECT_ERROR; + } + mbedtls_ssl_set_bio(&backend->ssl, cf, mbedtls_bio_cf_write, mbedtls_bio_cf_read, NULL /* rev_timeout() */); - mbedtls_ssl_conf_ciphersuites(&backend->config, - mbedtls_ssl_list_ciphersuites()); + if(conn_config->cipher_list) { + ret = mbed_set_selected_ciphers(data, backend, conn_config->cipher_list); + if(ret) { + failf(data, "mbedTLS: failed to set cipher suites"); + return ret; + } + } + else { + mbedtls_ssl_conf_ciphersuites(&backend->config, + mbedtls_ssl_list_ciphersuites()); + } + #if defined(MBEDTLS_SSL_RENEGOTIATION) mbedtls_ssl_conf_renegotiation(&backend->config, @@ -629,7 +812,7 @@ void *old_session = NULL; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &old_session, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) { ret = mbedtls_ssl_set_session(&backend->ssl, old_session); if(ret) { Curl_ssl_sessionid_unlock(data); @@ -654,14 +837,13 @@ &backend->clicert, &backend->pk); } - if(connssl->peer.sni) { - if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni)) { - /* 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. */ - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } + 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. */ + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; } #ifdef HAS_ALPN @@ -720,9 +902,15 @@ (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const mbedtls_x509_crt *peercert; + char cipher_str[64]; + uint16_t cipher_id; +#ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif DEBUGASSERT(backend); @@ -744,8 +932,10 @@ return CURLE_SSL_CONNECT_ERROR; } - infof(data, "mbedTLS: Handshake complete, cipher is %s", - mbedtls_ssl_get_ciphersuite(&backend->ssl)); + cipher_id = (uint16_t) + mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); + mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); + infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str); ret = mbedtls_ssl_get_verify_result(&backend->ssl); @@ -775,6 +965,7 @@ peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); if(peercert && data->set.verbose) { +#ifndef MBEDTLS_X509_REMOVE_INFO const size_t bufsize = 16384; char *buffer = malloc(bufsize); @@ -787,6 +978,9 @@ infof(data, "Unable to dump certificate information"); free(buffer); +#else + infof(data, "Unable to dump certificate information"); +#endif } if(pinnedpubkey) { @@ -875,6 +1069,13 @@ return CURLE_OK; } +static void mbedtls_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + mbedtls_ssl_session_free(sessionid); + free(sessionid); +} + static CURLcode mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -891,7 +1092,6 @@ int ret; mbedtls_ssl_session *our_ssl_sessionid; void *old_ssl_sessionid = NULL; - bool added = FALSE; our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); if(!our_ssl_sessionid) @@ -910,20 +1110,16 @@ /* If there's already a matching session in the cache, delete it */ Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)) + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &old_ssl_sessionid, NULL)) Curl_ssl_delsessionid(data, old_ssl_sessionid); - retcode = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, - 0, &added); + retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + mbedtls_session_free); Curl_ssl_sessionid_unlock(data); - if(!added) { - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); - } - if(retcode) { - failf(data, "failed to store ssl session"); + if(retcode) return retcode; - } } connssl->connecting_state = ssl_connect_done; @@ -978,6 +1174,7 @@ #ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_free(&backend->crl); #endif + Curl_safefree(backend->ciphersuites); mbedtls_ssl_config_free(&backend->config); mbedtls_ssl_free(&backend->ssl); mbedtls_ctr_drbg_free(&backend->ctr_drbg); @@ -1006,8 +1203,11 @@ if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) return 0; - *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? - CURLE_AGAIN : CURLE_RECV_ERROR; + *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ) +#ifdef TLS13_SUPPORT + || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) +#endif + ) ? CURLE_AGAIN : CURLE_RECV_ERROR; return -1; } @@ -1016,12 +1216,6 @@ return len; } -static void mbedtls_session_free(void *ptr) -{ - mbedtls_ssl_session_free(ptr); - free(ptr); -} - static size_t mbedtls_version(char *buffer, size_t size) { #ifdef MBEDTLS_VERSION_C @@ -1216,14 +1410,19 @@ */ static int mbedtls_init(void) { - return Curl_mbedtlsthreadlock_thread_setup(); + if(!Curl_mbedtlsthreadlock_thread_setup()) + return 0; +#ifdef THREADING_SUPPORT + entropy_init_mutex(&ts_entropy); +#endif + return 1; } static void mbedtls_cleanup(void) { #ifdef THREADING_SUPPORT - mbedtls_entropy_free(&ts_entropy); -#endif /* THREADING_SUPPORT */ + entropy_cleanup_mutex(&ts_entropy); +#endif (void)Curl_mbedtlsthreadlock_thread_cleanup(); } @@ -1295,7 +1494,6 @@ mbedtls_get_internals, /* get_internals */ mbedtls_close, /* close_one */ mbedtls_close_all, /* close_all */ - mbedtls_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c index 22b1b22..b96a904 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
@@ -26,12 +26,12 @@ #if defined(USE_MBEDTLS) && \ ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - defined(USE_THREADS_WIN32)) + defined(_WIN32)) #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) # include <pthread.h> # define MBEDTLS_MUTEX_T pthread_mutex_t -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) # define MBEDTLS_MUTEX_T HANDLE #endif @@ -59,7 +59,7 @@ #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) if(pthread_mutex_init(&mutex_buf[i], NULL)) return 0; /* pthread_mutex_init failed */ -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) mutex_buf[i] = CreateMutex(0, FALSE, 0); if(mutex_buf[i] == 0) return 0; /* CreateMutex failed */ @@ -80,7 +80,7 @@ #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) if(pthread_mutex_destroy(&mutex_buf[i])) return 0; /* pthread_mutex_destroy failed */ -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) if(!CloseHandle(mutex_buf[i])) return 0; /* CloseHandle failed */ #endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ @@ -100,7 +100,7 @@ "Error: mbedtlsthreadlock_lock_function failed\n")); return 0; /* pthread_mutex_lock failed */ } -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) { DEBUGF(fprintf(stderr, "Error: mbedtlsthreadlock_lock_function failed\n")); @@ -120,7 +120,7 @@ "Error: mbedtlsthreadlock_unlock_function failed\n")); return 0; /* pthread_mutex_unlock failed */ } -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) if(!ReleaseMutex(mutex_buf[n])) { DEBUGF(fprintf(stderr, "Error: mbedtlsthreadlock_unlock_function failed\n"));
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h index 2b0bd41..4846268 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
@@ -29,7 +29,7 @@ #ifdef USE_MBEDTLS #if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - defined(USE_THREADS_WIN32) + defined(_WIN32) int Curl_mbedtlsthreadlock_thread_setup(void); int Curl_mbedtlsthreadlock_thread_cleanup(void); @@ -43,7 +43,7 @@ #define Curl_mbedtlsthreadlock_lock_function(x) 1 #define Curl_mbedtlsthreadlock_unlock_function(x) 1 -#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ +#endif /* (USE_THREADS_POSIX && HAVE_PTHREAD_H) || _WIN32 */ #endif /* USE_MBEDTLS */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index ef5883d..7628e79 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -82,6 +82,17 @@ #include <openssl/tls1.h> #include <openssl/evp.h> +#ifdef USE_ECH +# ifndef OPENSSL_IS_BORINGSSL +# include <openssl/ech.h> +# endif +# include "curl_base64.h" +# define ECH_ENABLED(__data__) \ + (__data__->set.tls_ech && \ + !(__data__->set.tls_ech & CURLECH_DISABLE)\ + ) +#endif /* USE_ECH */ + #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) #include <openssl/ocsp.h> #endif @@ -193,12 +204,10 @@ * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) - * LibreSSL: supported since 3.5.0 (released 2022-02-24) + * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. */ #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ !defined(LIBRESSL_VERSION_NUMBER)) || \ - (defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER >= 0x3050000fL) || \ defined(OPENSSL_IS_BORINGSSL) #define HAVE_KEYLOG_CALLBACK #endif @@ -306,20 +315,6 @@ #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) #endif /* !LIBRESSL_VERSION_NUMBER */ -struct ossl_ssl_backend_data { - /* these ones requires specific SSL-types */ - SSL_CTX* ctx; - SSL* handle; - X509* server_cert; - BIO_METHOD *bio_method; - CURLcode io_result; /* result of last BIO cfilter operation */ -#ifndef HAVE_KEYLOG_CALLBACK - /* Set to true once a valid keylog entry has been created to avoid dupes. */ - bool keylog_done; -#endif - bool x509_store_setup; /* x509 store has been set up */ -}; - #if defined(HAVE_SSL_X509_STORE_SHARE) struct multi_ssl_backend_data { char *CAfile; /* CAfile path used to generate X509 store */ @@ -734,8 +729,7 @@ { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_SEND_ERROR; @@ -745,7 +739,7 @@ CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d", blen, (int)nwritten, result); BIO_clear_retry_flags(bio); - backend->io_result = result; + octx->io_result = result; if(nwritten < 0) { if(CURLE_AGAIN == result) BIO_set_retry_write(bio); @@ -757,8 +751,7 @@ { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_RECV_ERROR; @@ -772,21 +765,24 @@ CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d", blen, (int)nread, result); BIO_clear_retry_flags(bio); - backend->io_result = result; + octx->io_result = result; if(nread < 0) { if(CURLE_AGAIN == result) BIO_set_retry_read(bio); } + else if(nread == 0) { + connssl->peer_closed = TRUE; + } /* Before returning server replies to the SSL instance, we need * to have setup the x509 store or verification will fail. */ - if(!backend->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(!octx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); if(result) { - backend->io_result = result; + octx->io_result = result; return -1; } - backend->x509_store_setup = TRUE; + octx->x509_store_setup = TRUE; } return (int)nread; @@ -1888,28 +1884,52 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(backend); + DEBUGASSERT(octx); - if(backend->handle) { - if(cf->next && cf->next->connected) { + if(octx->ssl) { + /* Send the TLS shutdown if we are still connected *and* if + * the peer did not already close the connection. */ + if(cf->next && cf->next->connected && !connssl->peer_closed) { char buf[1024]; int nread, err; long sslerr; /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); ERR_clear_error(); - if(SSL_shutdown(backend->handle) == 1) { + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + err = SSL_get_error(octx->ssl, nread); + if(!nread && err == SSL_ERROR_ZERO_RETURN) { + CURLcode result; + ssize_t n; + size_t blen = sizeof(buf); + CURL_TRC_CF(data, cf, "peer has shutdown TLS"); + /* SSL_read() will not longer touch the socket, let's receive + * directly from the next filter to see if the underlying + * connection has also been closed. */ + n = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + if(!n) { + connssl->peer_closed = TRUE; + CURL_TRC_CF(data, cf, "peer closed connection"); + } + } + ERR_clear_error(); + if(connssl->peer_closed) { + /* As the peer closed, we do not expect it to read anything more we + * may send. It may be harmful, leading to TCP RST and delaying + * a lingering close. Just leave. */ + CURL_TRC_CF(data, cf, "not from sending TLS shutdown on " + "connection closed by peer"); + } + else if(SSL_shutdown(octx->ssl) == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); } else { - nread = SSL_read(backend->handle, buf, (int)sizeof(buf)); - err = SSL_get_error(backend->handle, nread); + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ case SSL_ERROR_ZERO_RETURN: /* no more data */ @@ -1935,20 +1955,20 @@ } ERR_clear_error(); - SSL_set_connect_state(backend->handle); + SSL_set_connect_state(octx->ssl); } - SSL_free(backend->handle); - backend->handle = NULL; + SSL_free(octx->ssl); + octx->ssl = NULL; } - if(backend->ctx) { - SSL_CTX_free(backend->ctx); - backend->ctx = NULL; - backend->x509_store_setup = FALSE; + if(octx->ssl_ctx) { + SSL_CTX_free(octx->ssl_ctx); + octx->ssl_ctx = NULL; + octx->x509_store_setup = FALSE; } - if(backend->bio_method) { - ossl_bio_cf_method_free(backend->bio_method); - backend->bio_method = NULL; + if(octx->bio_method) { + ossl_bio_cf_method_free(octx->bio_method); + octx->bio_method = NULL; } } @@ -1968,11 +1988,10 @@ int buffsize; int err; bool done = FALSE; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; int loop = 10; - DEBUGASSERT(backend); + DEBUGASSERT(octx); #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code @@ -1981,10 +2000,10 @@ we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(backend->handle); + (void)SSL_shutdown(octx->ssl); #endif - if(backend->handle) { + if(octx->ssl) { buffsize = (int)sizeof(buf); while(!done && loop--) { int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), @@ -1994,8 +2013,8 @@ /* Something to read, let's do it and hope that it is the close notify alert from the server */ - nread = SSL_read(backend->handle, buf, buffsize); - err = SSL_get_error(backend->handle, nread); + nread = SSL_read(octx->ssl, buf, buffsize); + err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -2040,7 +2059,7 @@ if(data->set.verbose) { #ifdef HAVE_SSL_GET_SHUTDOWN - switch(SSL_get_shutdown(backend->handle)) { + switch(SSL_get_shutdown(octx->ssl)) { case SSL_SENT_SHUTDOWN: infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN"); break; @@ -2055,16 +2074,17 @@ #endif } - SSL_free(backend->handle); - backend->handle = NULL; + SSL_free(octx->ssl); + octx->ssl = NULL; } return retval; } -static void ossl_session_free(void *ptr) +static void ossl_session_free(void *sessionid, size_t idsize) { /* free the ID */ - SSL_SESSION_free(ptr); + (void)idsize; + SSL_SESSION_free(sessionid); } /* @@ -2142,34 +2162,43 @@ struct ssl_peer *peer, X509 *server_cert) { bool matched = FALSE; - int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ + int target; /* target type, GEN_DNS or GEN_IPADD */ size_t addrlen = 0; STACK_OF(GENERAL_NAME) *altnames; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ - bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ + bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ size_t hostlen; (void)conn; hostlen = strlen(peer->hostname); - if(peer->is_ip_address) { -#ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, peer->hostname, &addr)) { - target = GEN_IPADD; - addrlen = sizeof(struct in6_addr); - } - else + switch(peer->type) { + case CURL_SSL_PEER_IPV4: + if(!Curl_inet_pton(AF_INET, peer->hostname, &addr)) + return CURLE_PEER_FAILED_VERIFICATION; + target = GEN_IPADD; + addrlen = sizeof(struct in_addr); + break; +#ifdef USE_IPV6 + case CURL_SSL_PEER_IPV6: + if(!Curl_inet_pton(AF_INET6, peer->hostname, &addr)) + return CURLE_PEER_FAILED_VERIFICATION; + target = GEN_IPADD; + addrlen = sizeof(struct in6_addr); + break; #endif - if(Curl_inet_pton(AF_INET, peer->hostname, &addr)) { - target = GEN_IPADD; - addrlen = sizeof(struct in_addr); - } + case CURL_SSL_PEER_DNS: + target = GEN_DNS; + break; + default: + DEBUGASSERT(0); + failf(data, "unexpected ssl peer type: %d", peer->type); + return CURLE_PEER_FAILED_VERIFICATION; } /* get a "list" of alternative names */ @@ -2250,9 +2279,12 @@ /* an alternative name matched */ ; else if(dNSName || iPAddress) { - infof(data, " subjectAltName does not match %s", peer->dispname); + const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "host name" : + (peer->type == CURL_SSL_PEER_IPV4) ? + "ipv4 address" : "ipv6 address"; + infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); failf(data, "SSL: no alternative certificate subject name matches " - "target host name '%s'", peer->dispname); + "target %s '%s'", tname, peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2349,8 +2381,7 @@ OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; X509 *cert; OCSP_CERTID *id = NULL; int cert_status, crl_reason; @@ -2358,9 +2389,9 @@ int ret; long len; - DEBUGASSERT(backend); + DEBUGASSERT(octx); - len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); + len = SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status); if(!status) { failf(data, "No OCSP response received"); @@ -2390,13 +2421,13 @@ goto end; } - ch = SSL_get_peer_cert_chain(backend->handle); + ch = SSL_get_peer_cert_chain(octx->ssl); if(!ch) { failf(data, "Could not get peer certificate chain"); result = CURLE_SSL_INVALIDCERTSTATUS; goto end; } - st = SSL_CTX_get_cert_store(backend->ctx); + st = SSL_CTX_get_cert_store(octx->ssl_ctx); #if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -2433,7 +2464,7 @@ } /* Compute the certificate's ID */ - cert = SSL_get1_peer_certificate(backend->handle); + cert = SSL_get1_peer_certificate(octx->ssl); if(!cert) { failf(data, "Error getting peer certificate"); result = CURLE_SSL_INVALIDCERTSTATUS; @@ -2856,10 +2887,9 @@ #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); - SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + DEBUGASSERT(octx); + SSL_CTX_set_max_proto_version(octx->ssl_ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } #else @@ -2917,61 +2947,64 @@ } #endif /* ! HAS_MODERN_SET_PROTO_VER */ -/* The "new session" callback must return zero if the session can be removed - * or non-zero if the session has been put into the session cache. - */ -static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + SSL_SESSION *session) { - int res = 0; - struct Curl_easy *data; - struct Curl_cfilter *cf; const struct ssl_config_data *config; - struct ssl_connect_data *connssl; bool isproxy; + bool added = FALSE; - cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); - connssl = cf? cf->ctx : NULL; - data = connssl? CF_DATA_CURRENT(cf) : NULL; - /* The sockindex has been stored as a pointer to an array element */ if(!cf || !data) - return 0; + goto out; isproxy = Curl_ssl_cf_is_proxy(cf); config = Curl_ssl_cf_get_config(cf, data); if(config->primary.sessionid) { bool incache; - bool added = FALSE; - void *old_ssl_sessionid = NULL; + void *old_session = NULL; Curl_ssl_sessionid_lock(data); if(isproxy) incache = FALSE; else - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); - if(incache) { - if(old_ssl_sessionid != ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } + incache = !(Curl_ssl_getsessionid(cf, data, peer, + &old_session, NULL)); + if(incache && (old_session != session)) { + infof(data, "old SSL session ID is stale, removing"); + Curl_ssl_delsessionid(data, old_session); + incache = FALSE; } if(!incache) { - if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid, - 0 /* unknown size */, &added)) { - if(added) { - /* the session has been put into the session cache */ - res = 1; - } - } - else - failf(data, "failed to store ssl session"); + added = TRUE; + Curl_ssl_addsessionid(cf, data, peer, session, 0, ossl_session_free); } Curl_ssl_sessionid_unlock(data); } - return res; +out: + if(!added) + ossl_session_free(session, 0); + return CURLE_OK; +} + +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) +{ + struct Curl_cfilter *cf; + struct Curl_easy *data; + struct ssl_connect_data *connssl; + + cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); + connssl = cf? cf->ctx : NULL; + data = connssl? CF_DATA_CURRENT(cf) : NULL; + Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid); + return 1; } static CURLcode load_cacert_from_memory(X509_STORE *store, @@ -3366,7 +3399,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, const struct Curl_easy *data) { - struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + struct Curl_multi *multi = data->multi; X509_STORE *store = NULL; DEBUGASSERT(multi); @@ -3386,7 +3419,7 @@ X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + struct Curl_multi *multi = data->multi; struct multi_ssl_backend_data *mbackend; DEBUGASSERT(multi); @@ -3469,29 +3502,33 @@ } #endif /* HAVE_SSL_X509_STORE_SHARE */ -static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) +CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + int transport, /* TCP or QUIC */ + const unsigned char *alpn, size_t alpn_len, + Curl_ossl_ctx_setup_cb *cb_setup, + void *cb_user_data, + Curl_ossl_new_session_cb *cb_new_session, + void *ssl_user_data) { CURLcode result = CURLE_OK; - char *ciphers; + const char *ciphers; SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; - struct ssl_connect_data *connssl = cf->ctx; ctx_option_t ctx_options = 0; void *ssl_sessionid = NULL; 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); - BIO *bio; const long int ssl_version = conn_config->version; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; const char * const ssl_cert_type = ssl_config->cert_type; const bool verifypeer = conn_config->verifypeer; char error_buffer[256]; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - - DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); - DEBUGASSERT(backend); +#ifdef USE_ECH + struct ssl_connect_data *connssl = cf->ctx; +#endif /* Make funny stuff to get random input */ result = ossl_seed(data); @@ -3500,56 +3537,74 @@ ssl_config->certverifyresult = !X509_V_OK; - /* check to see if we've been told to use an explicit SSL/TLS version */ - - switch(ssl_version) { - 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 */ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) - req_method = TLS_client_method(); + switch(transport) { + case TRNSPRT_TCP: + /* check to see if we've been told to use an explicit SSL/TLS version */ + switch(ssl_version) { + 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 */ + #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + req_method = TLS_client_method(); + #else + req_method = SSLv23_client_method(); + #endif + break; + case CURL_SSLVERSION_SSLv2: + failf(data, "No SSLv2 support"); + return CURLE_NOT_BUILT_IN; + case CURL_SSLVERSION_SSLv3: + failf(data, "No SSLv3 support"); + return CURLE_NOT_BUILT_IN; + default: + failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + return CURLE_SSL_CONNECT_ERROR; + } + break; + case TRNSPRT_QUIC: + if((ssl_version != CURL_SSLVERSION_DEFAULT) && + (ssl_version < CURL_SSLVERSION_TLSv1_3)) { + failf(data, "QUIC needs at least TLS version 1.3"); + return CURLE_SSL_CONNECT_ERROR; + } +#ifdef USE_OPENSSL_QUIC + req_method = OSSL_QUIC_client_method(); +#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L) + req_method = TLS_method(); #else req_method = SSLv23_client_method(); #endif break; - case CURL_SSLVERSION_SSLv2: - failf(data, "No SSLv2 support"); - return CURLE_NOT_BUILT_IN; - case CURL_SSLVERSION_SSLv3: - failf(data, "No SSLv3 support"); - return CURLE_NOT_BUILT_IN; default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + failf(data, "unsupported transport %d in SSL init", transport); return CURLE_SSL_CONNECT_ERROR; } - if(backend->ctx) { - /* This happens when an error was encountered before in this - * step and we are called to do it again. Get rid of any leftover - * from the previous call. */ - ossl_close(cf, data); - } - backend->ctx = SSL_CTX_new(req_method); - if(!backend->ctx) { + DEBUGASSERT(!octx->ssl_ctx); + octx->ssl_ctx = SSL_CTX_new(req_method); + + if(!octx->ssl_ctx) { failf(data, "SSL: couldn't create a context: %s", ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); return CURLE_OUT_OF_MEMORY; } -#ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS); -#endif + if(cb_setup) { + result = cb_setup(cf, data, cb_user_data); + if(result) + return result; + } #ifdef SSL_CTRL_SET_MSG_CALLBACK if(data->set.fdebug && data->set.verbose) { /* the SSL trace callback is only used for verbose logging */ - SSL_CTX_set_msg_callback(backend->ctx, ossl_trace); - SSL_CTX_set_msg_callback_arg(backend->ctx, cf); + SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace); + SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf); } #endif @@ -3629,7 +3684,7 @@ ctx_options |= SSL_OP_NO_SSLv3; #if HAS_MODERN_SET_PROTO_VER /* 1.1.0 */ - result = ossl_set_ssl_version_min_max(cf, backend->ctx); + result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx); #else result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data); #endif @@ -3642,26 +3697,20 @@ return CURLE_SSL_CONNECT_ERROR; } - SSL_CTX_set_options(backend->ctx, ctx_options); + SSL_CTX_set_options(octx->ssl_ctx, ctx_options); #ifdef HAS_ALPN - if(connssl->alpn) { - struct alpn_proto_buf proto; - - result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); - if(result || - SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) { + if(alpn && alpn_len) { + if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif if(ssl_cert || ssl_cert_blob || ssl_cert_type) { if(!result && - !cert_stuff(data, backend->ctx, + !cert_stuff(data, octx->ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, ssl_config->key, ssl_config->key_blob, ssl_config->key_type, ssl_config->key_passwd)) @@ -3672,10 +3721,10 @@ } ciphers = conn_config->cipher_list; - if(!ciphers) - ciphers = (char *)DEFAULT_CIPHER_SELECTION; + if(!ciphers && (peer->transport != TRNSPRT_QUIC)) + ciphers = DEFAULT_CIPHER_SELECTION; if(ciphers) { - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { + if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } @@ -3684,9 +3733,9 @@ #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES { - char *ciphers13 = conn_config->cipher_list13; + const char *ciphers13 = conn_config->cipher_list13; if(ciphers13) { - if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) { + if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) { failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; } @@ -3697,14 +3746,14 @@ #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH /* OpenSSL 1.1.1 requires clients to opt-in for PHA */ - SSL_CTX_set_post_handshake_auth(backend->ctx, 1); + SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1); #endif #ifdef HAVE_SSL_CTX_SET_EC_CURVES { - char *curves = conn_config->curves; + const char *curves = conn_config->curves; if(curves) { - if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { + if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); return CURLE_SSL_CIPHER; } @@ -3718,18 +3767,18 @@ char * const ssl_password = ssl_config->primary.password; infof(data, "Using TLS-SRP username: %s", ssl_username); - if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { + if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) { failf(data, "Unable to set SRP user name"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) { + if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) { failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } if(!conn_config->cipher_list) { infof(data, "Setting cipher list SRP"); - if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { + if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, "SRP")) { failf(data, "failed setting SRP cipher list"); return CURLE_SSL_CIPHER; } @@ -3741,38 +3790,40 @@ * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(backend->ctx, + SSL_CTX_set_verify(octx->ssl_ctx, verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ #ifdef HAVE_KEYLOG_CALLBACK if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback); + SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback); } #endif - /* Enable the session cache because it's a prerequisite for the "new session" - * callback. Use the "external storage" mode to prevent OpenSSL from creating - * an internal session cache. - */ - SSL_CTX_set_session_cache_mode(backend->ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb); + if(cb_new_session) { + /* Enable the session cache because it's a prerequisite for the + * "new session" callback. Use the "external storage" mode to prevent + * OpenSSL from creating an internal session cache. + */ + SSL_CTX_set_session_cache_mode(octx->ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(octx->ssl_ctx, cb_new_session); + } /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { /* When a user callback is installed to modify the SSL_CTX, * we need to do the full initialization before calling it. * See: #11800 */ - if(!backend->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(!octx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); if(result) return result; - backend->x509_store_setup = TRUE; + octx->x509_store_setup = TRUE; } Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, backend->ctx, + result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, false); if(result) { @@ -3782,47 +3833,174 @@ } /* Let's make an SSL structure */ - if(backend->handle) - SSL_free(backend->handle); - backend->handle = SSL_new(backend->ctx); - if(!backend->handle) { + if(octx->ssl) + SSL_free(octx->ssl); + octx->ssl = SSL_new(octx->ssl_ctx); + if(!octx->ssl) { failf(data, "SSL: couldn't create a context (handle)"); return CURLE_OUT_OF_MEMORY; } - SSL_set_app_data(backend->handle, cf); + SSL_set_app_data(octx->ssl, ssl_user_data); #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus) - SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); + SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp); #endif #if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \ defined(ALLOW_RENEG) - SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely); + SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely); #endif - SSL_set_connect_state(backend->handle); + SSL_set_connect_state(octx->ssl); - backend->server_cert = 0x0; + octx->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if(connssl->peer.sni) { - if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) { + if(peer->sni) { + if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) { failf(data, "Failed set SNI"); return CURLE_SSL_CONNECT_ERROR; } } + +#ifdef USE_ECH + if(ECH_ENABLED(data)) { + unsigned char *ech_config = NULL; + size_t ech_config_len = 0; + char *outername = data->set.str[STRING_ECH_PUBLIC]; + int trying_ech_now = 0; + + if(data->set.tls_ech & CURLECH_GREASE) { + infof(data, "ECH: will GREASE ClientHello"); +# ifdef OPENSSL_IS_BORINGSSL + SSL_set_enable_ech_grease(octx->ssl, 1); +# else + SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); +# endif + } + else if(data->set.tls_ech & CURLECH_CLA_CFG) { +# ifdef OPENSSL_IS_BORINGSSL + /* have to do base64 decode here for boring */ + const char *b64 = data->set.str[STRING_ECH_CONFIG]; + + if(!b64) { + infof(data, "ECH: ECHConfig from command line empty"); + return CURLE_SSL_CONNECT_ERROR; + } + ech_config_len = 2 * strlen(b64); + result = Curl_base64_decode(b64, &ech_config, &ech_config_len); + if(result || !ech_config) { + infof(data, "ECH: can't base64 decode ECHConfig from command line"); + if(data->set.tls_ech & CURLECH_HARD) + return result; + } + if(SSL_set1_ech_config_list(octx->ssl, ech_config, + ech_config_len) != 1) { + infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(data->set.tls_ech & CURLECH_HARD) { + free(ech_config); + return CURLE_SSL_CONNECT_ERROR; + } + } + free(ech_config); + trying_ech_now = 1; +# else + ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG]; + if(!ech_config) { + infof(data, "ECH: ECHConfig from command line empty"); + return CURLE_SSL_CONNECT_ERROR; + } + ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); + if(SSL_ech_set1_echconfig(octx->ssl, ech_config, ech_config_len) != 1) { + infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else + trying_ech_now = 1; +# endif + infof(data, "ECH: ECHConfig from command line"); + } + else { + struct Curl_dns_entry *dns = NULL; + + dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port); + 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; + + 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"); +# ifndef OPENSSL_IS_BORINGSSL + if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_ECH_set1_echconfig failed"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } +# else + if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_set1_ech_config_list failed (boring)"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } +# endif + 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) + return CURLE_SSL_CONNECT_ERROR; + } + Curl_resolv_unlock(data, dns); + } + } +# ifdef OPENSSL_IS_BORINGSSL + if(trying_ech_now && outername) { + infof(data, "ECH: setting public_name not supported with boringssl"); + return CURLE_SSL_CONNECT_ERROR; + } +# else + if(trying_ech_now && outername) { + infof(data, "ECH: inner: '%s', outer: '%s'", + connssl->peer.hostname, outername); + result = SSL_ech_set_server_names(octx->ssl, + connssl->peer.hostname, outername, + 0 /* do send outer */); + if(result != 1) { + infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); + return CURLE_SSL_CONNECT_ERROR; + } + } +# endif /* not BORING */ + if(trying_ech_now + && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { + infof(data, "ECH: Can't force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif /* USE_ECH */ + #endif - SSL_set_app_data(backend->handle, cf); - - connssl->reused_session = FALSE; - if(ssl_config->primary.sessionid) { + octx->reused_session = FALSE; + if(ssl_config->primary.sessionid && transport == TRNSPRT_TCP) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { + if(!SSL_set_session(octx->ssl, ssl_sessionid)) { Curl_ssl_sessionid_unlock(data); failf(data, "SSL: SSL_set_session failed: %s", ossl_strerror(ERR_get_error(), error_buffer, @@ -3831,15 +4009,46 @@ } /* Informational message */ infof(data, "SSL reusing session ID"); - connssl->reused_session = TRUE; + octx->reused_session = TRUE; } Curl_ssl_sessionid_unlock(data); } - backend->bio_method = ossl_bio_cf_method_create(); - if(!backend->bio_method) + return CURLE_OK; +} + +static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + struct alpn_proto_buf proto; + BIO *bio; + CURLcode result; + + DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(octx); + memset(&proto, 0, sizeof(proto)); +#ifdef HAS_ALPN + if(connssl->alpn) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error determining ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + } +#endif + + result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP, + proto.data, proto.len, NULL, NULL, + ossl_new_session_cb, cf); + if(result) + return result; + + octx->bio_method = ossl_bio_cf_method_create(); + if(!octx->bio_method) return CURLE_OUT_OF_MEMORY; - bio = BIO_new(backend->bio_method); + bio = BIO_new(octx->bio_method); if(!bio) return CURLE_OUT_OF_MEMORY; @@ -3851,40 +4060,109 @@ * We check on the function in configure, since libressl and friends * each have their own versions to add support for this. */ BIO_up_ref(bio); - SSL_set0_rbio(backend->handle, bio); - SSL_set0_wbio(backend->handle, bio); + SSL_set0_rbio(octx->ssl, bio); + SSL_set0_wbio(octx->ssl, bio); #else - SSL_set_bio(backend->handle, bio, bio); + SSL_set_bio(octx->ssl, bio, bio); +#endif + +#ifdef HAS_ALPN + if(connssl->alpn) { + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } #endif connssl->connecting_state = ssl_connect_2; - return CURLE_OK; } +#ifdef USE_ECH +/* If we have retry configs, then trace those out */ +static void 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 OPENSSL_IS_BORINGSSL + char *inner = NULL; + unsigned char *rcs = NULL; + char *outer = NULL; +# 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 + + /* nothing to trace if not doing ECH */ + if(!ECH_ENABLED(data)) + return; +# ifndef OPENSSL_IS_BORINGSSL + rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl); +# else + SSL_get0_ech_retry_configs(ssl, &rcs, &rcl); + rv = (int)rcl; +# endif + + if(rv && rcs) { +# define HEXSTR_MAX 800 + char *b64str = NULL; + size_t blen = 0; + + result = Curl_base64_encode((const char *)rcs, rcl, + &b64str, &blen); + if(!result && b64str) + infof(data, "ECH: retry_configs %s", b64str); + free(b64str); +# ifndef OPENSSL_IS_BORINGSSL + rv = SSL_ech_get_status(ssl, &inner, &outer); + infof(data, "ECH: retry_configs for %s from %s, %d %d", + inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); +#else + rv = SSL_ech_accepted(ssl); + servername_type = SSL_get_servername_type(ssl); + inner = SSL_get_servername(ssl, servername_type); + SSL_get0_ech_name_override(ssl, &outer, &out_name_len); + /* TODO: get the inner from boring */ + infof(data, "ECH: retry_configs for %s from %s, %d %d", + inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); +#endif + } + else + infof(data, "ECH: no retry_configs (rv = %d)", rv); +# ifndef OPENSSL_IS_BORINGSSL + OPENSSL_free((void *)rcs); +# endif + return; +} + +#endif + static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { int err; struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); - DEBUGASSERT(backend); + DEBUGASSERT(octx); ERR_clear_error(); - err = SSL_connect(backend->handle); + err = SSL_connect(octx->ssl); - if(!backend->x509_store_setup) { + if(!octx->x509_store_setup) { /* After having send off the ClientHello, we prepare the x509 * store to verify the coming certificate from the server */ - CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); if(result) return result; - backend->x509_store_setup = TRUE; + octx->x509_store_setup = TRUE; } #ifndef HAVE_KEYLOG_CALLBACK @@ -3892,7 +4170,9 @@ /* If key logging is enabled, wait for the handshake to complete and then * proceed with logging secrets (for TLS 1.2 or older). */ - ossl_log_tls12_secret(backend->handle, &backend->keylog_done); + bool done = FALSE; + ossl_log_tls12_secret(octx->ssl, &done); + octx->keylog_done = done; } #endif @@ -3900,7 +4180,7 @@ 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { - int detail = SSL_get_error(backend->handle, err); + int detail = SSL_get_error(octx->ssl, err); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -3922,7 +4202,7 @@ return CURLE_OK; } #endif - if(backend->io_result == CURLE_AGAIN) { + if(octx->io_result == CURLE_AGAIN) { return CURLE_OK; } else { @@ -3950,7 +4230,7 @@ (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { result = CURLE_PEER_FAILED_VERIFICATION; - lerr = SSL_get_verify_result(backend->handle); + lerr = SSL_get_verify_result(octx->ssl); if(lerr != X509_V_OK) { ssl_config->certverifyresult = lerr; msnprintf(error_buffer, sizeof(error_buffer), @@ -3973,6 +4253,21 @@ ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); } #endif +#ifdef USE_ECH + else if((lib == ERR_LIB_SSL) && +# ifndef OPENSSL_IS_BORINGSSL + (reason == SSL_R_ECH_REQUIRED)) { +# else + (reason == SSL_R_ECH_REJECTED)) { +# endif + + /* trace retry_configs if we got some */ + ossl_trace_ech_retry_configs(data, octx->ssl, reason); + + result = CURLE_ECH_REQUIRED; + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + } +#endif else { result = CURLE_SSL_CONNECT_ERROR; ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); @@ -3992,7 +4287,7 @@ Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - connssl->peer.hostname, connssl->port); + connssl->peer.hostname, connssl->peer.port); return result; } @@ -4010,22 +4305,84 @@ connssl->connecting_state = ssl_connect_3; #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) - SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid); + SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid); #if (OPENSSL_VERSION_NUMBER >= 0x30200000L) - negotiated_group_name = SSL_get0_group_name(backend->handle); + negotiated_group_name = SSL_get0_group_name(octx->ssl); #else negotiated_group_name = - OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF); + OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF); #endif #endif /* Informational message */ infof(data, "SSL connection using %s / %s / %s / %s", - SSL_get_version(backend->handle), - SSL_get_cipher(backend->handle), + SSL_get_version(octx->ssl), + SSL_get_cipher(octx->ssl), negotiated_group_name? negotiated_group_name : "[blank]", OBJ_nid2sn(psigtype_nid)); +#ifdef USE_ECH +# ifndef OPENSSL_IS_BORINGSSL + if(ECH_ENABLED(data)) { + char *inner = NULL, *outer = NULL; + const char *status = NULL; + int rv; + + rv = SSL_ech_get_status(octx->ssl, &inner, &outer); + switch(rv) { + case SSL_ECH_STATUS_SUCCESS: + status = "succeeded"; + break; + case SSL_ECH_STATUS_GREASE_ECH: + status = "sent GREASE, got retry-configs"; + break; + case SSL_ECH_STATUS_GREASE: + status = "sent GREASE"; + break; + case SSL_ECH_STATUS_NOT_TRIED: + status = "not attempted"; + break; + case SSL_ECH_STATUS_NOT_CONFIGURED: + status = "not configured"; + break; + case SSL_ECH_STATUS_BACKEND: + status = "backend (unexpected)"; + break; + case SSL_ECH_STATUS_FAILED: + status = "failed"; + break; + case SSL_ECH_STATUS_BAD_CALL: + status = "bad call (unexpected)"; + break; + case SSL_ECH_STATUS_BAD_NAME: + status = "bad name (unexpected)"; + break; + default: + status = "unexpected status"; + infof(data, "ECH: unexpected status %d",rv); + } + infof(data, "ECH: result: status is %s, inner is %s, outer is %s", + (status?status:"NULL"), + (inner?inner:"NULL"), + (outer?outer:"NULL")); + OPENSSL_free(inner); + OPENSSL_free(outer); + if(rv == SSL_ECH_STATUS_GREASE_ECH) { + /* 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) { + infof(data, "ECH: ech-hard failed"); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + infof(data, "ECH: result: status is not attempted"); + } +# endif /* BORING */ +#endif /* USE_ECH */ + #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was * negotiated @@ -4033,7 +4390,7 @@ if(connssl->alpn) { const unsigned char *neg_protocol; unsigned int len; - SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); + SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len); return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); } @@ -4170,20 +4527,12 @@ #define infof_certstack(data, ssl) #endif -/* - * Get the server cert, verify it and show it, etc., only call failf() if the - * 'strict' argument is TRUE as otherwise all this is for informational - * purposes only! - * - * We check certificates to authenticate the server; otherwise we risk - * man-in-the-middle attack. - */ -static CURLcode servercert(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool strict) +CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer) { struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); CURLcode result = CURLE_OK; @@ -4195,10 +4544,9 @@ char buffer[2048]; const char *ptr; BIO *mem = BIO_new(BIO_s_mem()); - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + bool strict = (conn_config->verifypeer || conn_config->verifyhost); - DEBUGASSERT(backend); + DEBUGASSERT(octx); if(!mem) { failf(data, @@ -4211,10 +4559,10 @@ if(data->set.ssl.certinfo) /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, backend->handle); + (void)Curl_ossl_certchain(data, octx->ssl); - backend->server_cert = SSL_get1_peer_certificate(backend->handle); - if(!backend->server_cert) { + octx->server_cert = SSL_get1_peer_certificate(octx->ssl); + if(!octx->server_cert) { BIO_free(mem); if(!strict) return CURLE_OK; @@ -4226,19 +4574,19 @@ infof(data, "%s certificate:", Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server"); - rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), + rc = x509_name_oneline(X509_get_subject_name(octx->server_cert), buffer, sizeof(buffer)); infof(data, " subject: %s", rc?"[NONE]":buffer); #ifndef CURL_DISABLE_VERBOSE_STRINGS { long len; - ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert)); + ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " start date: %.*s", (int)len, ptr); (void)BIO_reset(mem); - ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert)); + ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " expire date: %.*s", (int)len, ptr); (void)BIO_reset(mem); @@ -4248,16 +4596,15 @@ BIO_free(mem); if(conn_config->verifyhost) { - result = Curl_ossl_verifyhost(data, conn, &connssl->peer, - backend->server_cert); + result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert); if(result) { - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return result; } } - rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert), + rc = x509_name_oneline(X509_get_issuer_name(octx->server_cert), buffer, sizeof(buffer)); if(rc) { if(strict) @@ -4281,8 +4628,8 @@ " error %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)) ); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_OUT_OF_MEMORY; } } @@ -4294,8 +4641,8 @@ " error %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)) ); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_OUT_OF_MEMORY; } @@ -4304,8 +4651,8 @@ failf(data, "SSL: Unable to open issuer cert (%s)", conn_config->issuercert); BIO_free(fp); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } } @@ -4317,19 +4664,19 @@ conn_config->issuercert); BIO_free(fp); X509_free(issuer); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { + if(X509_check_issued(issuer, octx->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", conn_config->issuercert); BIO_free(fp); X509_free(issuer); - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } @@ -4339,7 +4686,7 @@ X509_free(issuer); } - lerr = SSL_get_verify_result(backend->handle); + lerr = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = lerr; if(lerr != X509_V_OK) { if(conn_config->verifypeer) { @@ -4359,11 +4706,11 @@ infof(data, " SSL certificate verify ok."); } - infof_certstack(data, backend->handle); + infof_certstack(data, octx->ssl); #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus && !connssl->reused_session) { + if(conn_config->verifystatus && !octx->reused_session) { /* don't do this after Session ID reuse */ result = verifystatus(cf, data); if(result) { @@ -4373,7 +4720,8 @@ void *old_ssl_sessionid = NULL; bool incache; Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, peer, + &old_ssl_sessionid, NULL)); if(incache) { infof(data, "Remove session ID again from cache"); Curl_ssl_delsessionid(data, old_ssl_sessionid); @@ -4381,8 +4729,8 @@ Curl_ssl_sessionid_unlock(data); } - X509_free(backend->server_cert); - backend->server_cert = NULL; + X509_free(octx->server_cert); + octx->server_cert = NULL; return result; } } @@ -4392,18 +4740,21 @@ /* when not strict, we don't bother about the verify cert problems */ result = CURLE_OK; +#ifndef CURL_DISABLE_PROXY ptr = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif if(!result && ptr) { - result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr); + result = ossl_pkp_pin_peer_pubkey(data, octx->server_cert, ptr); if(result) failf(data, "SSL: public key does not match pinned public key"); } - X509_free(backend->server_cert); - backend->server_cert = NULL; - connssl->connecting_state = ssl_connect_done; + X509_free(octx->server_cert); + octx->server_cert = NULL; return result; } @@ -4413,7 +4764,7 @@ { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -4424,9 +4775,7 @@ * operations. */ - result = servercert(cf, data, conn_config->verifypeer || - conn_config->verifyhost); - + result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer); if(!result) connssl->connecting_state = ssl_connect_done; @@ -4566,12 +4915,11 @@ const struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(connssl && backend); - if(backend->handle && SSL_pending(backend->handle)) + DEBUGASSERT(connssl && octx); + if(octx->ssl && SSL_pending(octx->ssl)) return TRUE; return FALSE; } @@ -4590,19 +4938,18 @@ int memlen; int rc; struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(backend); + DEBUGASSERT(octx); ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - rc = SSL_write(backend->handle, mem, memlen); + rc = SSL_write(octx->ssl, mem, memlen); if(rc <= 0) { - err = SSL_get_error(backend->handle, rc); + err = SSL_get_error(octx->ssl, rc); switch(err) { case SSL_ERROR_WANT_READ: @@ -4617,7 +4964,7 @@ { int sockerr = SOCKERRNO; - if(backend->io_result == CURLE_AGAIN) { + if(octx->io_result == CURLE_AGAIN) { *curlcode = CURLE_AGAIN; rc = -1; goto out; @@ -4674,20 +5021,19 @@ int buffsize; struct connectdata *conn = cf->conn; struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; (void)data; - DEBUGASSERT(backend); + DEBUGASSERT(octx); ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); + nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize); if(nread <= 0) { /* failed SSL_read */ - int err = SSL_get_error(backend->handle, (int)nread); + int err = SSL_get_error(octx->ssl, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -4709,7 +5055,7 @@ /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ - if(backend->io_result == CURLE_AGAIN) { + if(octx->io_result == CURLE_AGAIN) { *curlcode = CURLE_AGAIN; nread = -1; goto out; @@ -4900,11 +5246,10 @@ CURLINFO info) { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ - struct ossl_ssl_backend_data *backend = - (struct ossl_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); + struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + DEBUGASSERT(octx); return info == CURLINFO_TLS_SESSION ? - (void *)backend->ctx : (void *)backend->handle; + (void *)octx->ssl_ctx : (void *)octx->ssl; } static void ossl_free_multi_ssl_backend_data( @@ -4932,9 +5277,12 @@ #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES SSLSUPP_TLS13_CIPHERSUITES | #endif +#ifdef USE_ECH + SSLSUPP_ECH | +#endif SSLSUPP_HTTPS_PROXY, - sizeof(struct ossl_ssl_backend_data), + sizeof(struct ossl_ctx), ossl_init, /* init */ ossl_cleanup, /* cleanup */ @@ -4950,7 +5298,6 @@ ossl_get_internals, /* get_internals */ ossl_close, /* close_one */ ossl_close_all, /* close_all */ - ossl_session_free, /* session_free */ ossl_set_engine, /* set_engine */ ossl_set_engine_default, /* set_engine_default */ ossl_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index e802363..55e06bd 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -36,6 +36,39 @@ #include "urldata.h" +/* Struct to hold a Curl OpenSSL instance */ +struct ossl_ctx { + /* these ones requires specific SSL-types */ + SSL_CTX* ssl_ctx; + SSL* ssl; + X509* server_cert; + BIO_METHOD *bio_method; + CURLcode io_result; /* result of last BIO cfilter operation */ +#ifndef HAVE_KEYLOG_CALLBACK + /* Set to true once a valid keylog entry has been created to avoid dupes. */ + BIT(keylog_done); +#endif + BIT(x509_store_setup); /* x509 store has been set up */ + BIT(reused_session); /* session-ID was reused for this */ +}; + +typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data); + +typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid); + +CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + int transport, /* TCP or QUIC */ + const unsigned char *alpn, size_t alpn_len, + Curl_ossl_ctx_setup_cb *cb_setup, + void *cb_user_data, + Curl_ossl_new_session_cb *cb_new_session, + void *ssl_user_data); + #if (OPENSSL_VERSION_NUMBER < 0x30000000L) #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif @@ -66,5 +99,23 @@ struct Curl_easy *data, SSL_CTX *ssl_ctx); +/* + * Add a new session to the cache. Takes ownership of the session. + */ +CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + SSL_SESSION *ssl_sessionid); + +/* + * Get the server cert, verify it and show it, etc., only call failf() if + * ssl config verifypeer or -host is set. Otherwise all this is for + * informational purposes only! + */ +CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer); + #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index d589709..8b6588a 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -7,6 +7,7 @@ * * Copyright (C) Jacob Hoffman-Andrews, * <github@hoffman-andrews.com> + * Copyright (C) kpcyrd, <kpcyrd@archlinux.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -45,7 +46,8 @@ { const struct rustls_client_config *config; struct rustls_connection *conn; - bool data_pending; + size_t plain_out_buffered; + BIT(data_in_pending); }; /* For a given rustls_result error code, return the best-matching CURLcode. */ @@ -60,7 +62,7 @@ case RUSTLS_RESULT_NULL_PARAMETER: return CURLE_BAD_FUNCTION_ARGUMENT; default: - return CURLE_READ_ERROR; + return CURLE_RECV_ERROR; } } @@ -73,7 +75,7 @@ (void)data; DEBUGASSERT(ctx && ctx->backend); backend = (struct rustls_ssl_backend_data *)ctx->backend; - return backend->data_pending; + return backend->data_in_pending; } struct io_ctx { @@ -85,6 +87,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) { struct io_ctx *io_ctx = userdata; + struct ssl_connect_data *const connssl = io_ctx->cf->ctx; CURLcode result; int ret = 0; ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, @@ -96,7 +99,11 @@ else ret = EINVAL; } + else if(nread == 0) + connssl->peer_closed = TRUE; *out_n = (int)nread; + CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", + len, nread, result); return ret; } @@ -116,10 +123,8 @@ ret = EINVAL; } *out_n = (int)nwritten; - /* - CURL_TRC_CFX(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", - len, nwritten, result)); - */ + CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", + len, nwritten, result); return ret; } @@ -146,7 +151,7 @@ char buffer[STRERROR_LEN]; failf(data, "reading from socket: %s", Curl_strerror(io_error, buffer, sizeof(buffer))); - *err = CURLE_READ_ERROR; + *err = CURLE_RECV_ERROR; return -1; } @@ -161,7 +166,7 @@ return -1; } - backend->data_pending = TRUE; + backend->data_in_pending = TRUE; *err = CURLE_OK; return (ssize_t)tls_bytes_read; } @@ -196,7 +201,7 @@ rconn = backend->conn; while(plain_bytes_copied < plainlen) { - if(!backend->data_pending) { + if(!backend->data_in_pending) { if(tls_recv_more(cf, data, err) < 0) { if(*err != CURLE_AGAIN) { nread = -1; @@ -211,12 +216,12 @@ plainlen - plain_bytes_copied, &n); if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { - backend->data_pending = FALSE; + backend->data_in_pending = FALSE; } else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { failf(data, "rustls: peer closed TCP connection " "without first closing TLS connection"); - *err = CURLE_READ_ERROR; + *err = CURLE_RECV_ERROR; nread = -1; goto out; } @@ -226,7 +231,7 @@ size_t errorlen; rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf); - *err = CURLE_READ_ERROR; + *err = CURLE_RECV_ERROR; nread = -1; goto out; } @@ -261,6 +266,42 @@ return nread; } +static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, + struct rustls_connection *rconn) +{ + struct io_ctx io_ctx; + rustls_io_result io_error; + size_t tlswritten = 0; + size_t tlswritten_total = 0; + CURLcode result = CURLE_OK; + + io_ctx.cf = cf; + io_ctx.data = data; + + while(rustls_connection_wants_write(rconn)) { + io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, + &tlswritten); + if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", + tlswritten_total); + return CURLE_AGAIN; + } + else if(io_error) { + char buffer[STRERROR_LEN]; + failf(data, "writing to socket: %s", + Curl_strerror(io_error, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; + } + if(tlswritten == 0) { + failf(data, "EOF in swrite"); + return CURLE_SEND_ERROR; + } + CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten); + tlswritten_total += tlswritten; + } + return result; +} + /* * On each call: * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0). @@ -279,26 +320,43 @@ struct rustls_ssl_backend_data *const backend = (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; - struct io_ctx io_ctx; size_t plainwritten = 0; - size_t tlswritten = 0; - size_t tlswritten_total = 0; rustls_result rresult; - rustls_io_result io_error; char errorbuf[256]; size_t errorlen; + const unsigned char *buf = plainbuf; + size_t blen = plainlen; + ssize_t nwritten = 0; DEBUGASSERT(backend); rconn = backend->conn; + DEBUGASSERT(rconn); - CURL_TRC_CF(data, cf, "cf_send: %ld plain bytes", plainlen); + CURL_TRC_CF(data, cf, "cf_send(len=%zu)", plainlen); - io_ctx.cf = cf; - io_ctx.data = data; + /* If a previous send blocked, we already added its plain bytes + * to rustsls and must not do that again. Flush the TLS bytes and, + * if successful, deduct the previous plain bytes from the current + * send. */ + if(backend->plain_out_buffered) { + *err = cr_flush_out(cf, data, rconn); + CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d", + backend->plain_out_buffered, *err); + if(*err) + return -1; + if(blen > backend->plain_out_buffered) { + blen -= backend->plain_out_buffered; + buf += backend->plain_out_buffered; + } + else + blen = 0; + nwritten += (ssize_t)backend->plain_out_buffered; + backend->plain_out_buffered = 0; + } - if(plainlen > 0) { - rresult = rustls_connection_write(rconn, plainbuf, plainlen, - &plainwritten); + if(blen > 0) { + CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to rustls", blen); + rresult = rustls_connection_write(rconn, buf, blen, &plainwritten); if(rresult != RUSTLS_RESULT_OK) { rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf); @@ -312,37 +370,32 @@ } } - while(rustls_connection_wants_write(rconn)) { - io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, - &tlswritten); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", - tlswritten_total); - *err = CURLE_AGAIN; - return -1; + *err = cr_flush_out(cf, data, rconn); + if(*err) { + if(CURLE_AGAIN == *err) { + /* The TLS bytes may have been partially written, but we fail the + * complete send() and remember how much we already added to rustls. */ + CURL_TRC_CF(data, cf, "cf_send: EAGAIN, remember we added %zu plain" + " bytes already to rustls", blen); + backend->plain_out_buffered = plainwritten; + if(nwritten) { + *err = CURLE_OK; + return (ssize_t)nwritten; + } } - else if(io_error) { - char buffer[STRERROR_LEN]; - failf(data, "writing to socket: %s", - Curl_strerror(io_error, buffer, sizeof(buffer))); - *err = CURLE_WRITE_ERROR; - return -1; - } - if(tlswritten == 0) { - failf(data, "EOF in swrite"); - *err = CURLE_WRITE_ERROR; - return -1; - } - CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten); - tlswritten_total += tlswritten; + return -1; } + else + nwritten += (ssize_t)plainwritten; - return plainwritten; + CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zd", + plainlen, *err, nwritten); + return nwritten; } /* A server certificate verify callback for rustls that always returns RUSTLS_RESULT_OK, or in other words disable certificate verification. */ -static enum rustls_result +static uint32_t cr_verify_none(void *userdata UNUSED_PARAM, const rustls_verify_server_cert_params *params UNUSED_PARAM) { @@ -353,12 +406,12 @@ cr_hostname_is_ip(const char *hostname) { struct in_addr in; -#ifdef ENABLE_IPV6 +#ifdef USE_IPV6 struct in6_addr in6; if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) { return true; } -#endif /* ENABLE_IPV6 */ +#endif /* USE_IPV6 */ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { return true; } @@ -373,7 +426,10 @@ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct rustls_connection *rconn = NULL; struct rustls_client_config_builder *config_builder = NULL; - struct rustls_root_cert_store *roots = NULL; + const struct rustls_root_cert_store *roots = NULL; + struct rustls_root_cert_store_builder *roots_builder = NULL; + struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL; + struct rustls_server_cert_verifier *server_cert_verifier = NULL; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; const char * const ssl_cafile = /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ @@ -414,54 +470,72 @@ hostname = "example.invalid"; } } - else if(ca_info_blob) { - roots = rustls_root_cert_store_new(); + else if(ca_info_blob || ssl_cafile) { + roots_builder = rustls_root_cert_store_builder_new(); - /* Enable strict parsing only if verification isn't disabled. */ - result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, - ca_info_blob->len, verifypeer); - if(result != RUSTLS_RESULT_OK) { - failf(data, "rustls: failed to parse trusted certificates from blob"); - rustls_root_cert_store_free(roots); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); - return CURLE_SSL_CACERT_BADFILE; + if(ca_info_blob) { + /* Enable strict parsing only if verification isn't disabled. */ + result = rustls_root_cert_store_builder_add_pem(roots_builder, + ca_info_blob->data, + ca_info_blob->len, + verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to parse trusted certificates from blob"); + rustls_root_cert_store_builder_free(roots_builder); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } + else if(ssl_cafile) { + /* Enable strict parsing only if verification isn't disabled. */ + result = rustls_root_cert_store_builder_load_roots_from_file( + roots_builder, ssl_cafile, verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to load trusted certificates"); + rustls_root_cert_store_builder_free(roots_builder); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } } - result = rustls_client_config_builder_use_roots(config_builder, roots); - rustls_root_cert_store_free(roots); + result = rustls_root_cert_store_builder_build(roots_builder, &roots); + rustls_root_cert_store_builder_free(roots_builder); if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to load trusted certificates"); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); return CURLE_SSL_CACERT_BADFILE; } - } - else if(ssl_cafile) { - result = rustls_client_config_builder_load_roots_from_file( - config_builder, ssl_cafile); + + verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots); + + result = rustls_web_pki_server_cert_verifier_builder_build( + verifier_builder, &server_cert_verifier); + rustls_web_pki_server_cert_verifier_builder_free(verifier_builder); if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to load trusted certificates"); + rustls_server_cert_verifier_free(server_cert_verifier); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); return CURLE_SSL_CACERT_BADFILE; } + + rustls_client_config_builder_set_server_verifier(config_builder, + server_cert_verifier); } backend->config = rustls_client_config_builder_build(config_builder); DEBUGASSERT(rconn == NULL); - { - /* rustls claims to manage ip address hostnames as well here. So, - * if we have an SNI, we use it, otherwise we pass the hostname */ - char *server = connssl->peer.sni? - connssl->peer.sni : connssl->peer.hostname; - result = rustls_client_connection_new(backend->config, server, &rconn); - } + result = rustls_client_connection_new(backend->config, + connssl->peer.hostname, &rconn); if(result != RUSTLS_RESULT_OK) { rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf); return CURLE_COULDNT_CONNECT; } + DEBUGASSERT(rconn); rustls_connection_set_userdata(rconn, backend); backend->conn = rconn; return CURLE_OK; @@ -510,9 +584,12 @@ DEBUGASSERT(backend); - if(ssl_connection_none == connssl->state) { + CURL_TRC_CF(data, cf, "cr_connect_common, 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_common, init backend -> %d", result); if(result != CURLE_OK) { return result; } @@ -529,21 +606,34 @@ */ if(!rustls_connection_is_handshaking(rconn)) { infof(data, "Done handshaking"); - /* Done with the handshake. Set up callbacks to send/receive data. */ - connssl->state = ssl_connection_complete; - + /* rustls claims it is no longer handshaking *before* it has + * send its FINISHED message off. We attempt to let it write + * one more time. Oh my. + */ cr_set_negotiated_alpn(cf, data, rconn); - + cr_send(cf, data, NULL, 0, &tmperr); + if(tmperr == CURLE_AGAIN) { + connssl->connecting_state = ssl_connect_2_writing; + return CURLE_OK; + } + else if(tmperr != CURLE_OK) { + return tmperr; + } + /* REALLY Done with the handshake. */ + connssl->state = ssl_connection_complete; *done = TRUE; return CURLE_OK; } wants_read = rustls_connection_wants_read(rconn); - wants_write = rustls_connection_wants_write(rconn); + wants_write = rustls_connection_wants_write(rconn) || + backend->plain_out_buffered; DEBUGASSERT(wants_read || wants_write); writefd = wants_write?sockfd:CURL_SOCKET_BAD; readfd = wants_read?sockfd:CURL_SOCKET_BAD; + connssl->connecting_state = wants_write? + ssl_connect_2_writing : ssl_connect_2_reading; /* check allowed time left */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -555,8 +645,8 @@ socket_check_timeout = blocking?timeout_ms:0; - what = Curl_socket_check( - readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout); + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, + socket_check_timeout); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -568,19 +658,18 @@ return CURLE_OPERATION_TIMEDOUT; } if(0 == what) { - infof(data, "Curl_socket_check: %s would block", + CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block", wants_read&&wants_write ? "writing and reading" : wants_write ? "writing" : "reading"); - *done = FALSE; return CURLE_OK; } /* socket is readable or writable */ if(wants_write) { - infof(data, "rustls_connection wants us to write_tls."); + CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls."); cr_send(cf, data, NULL, 0, &tmperr); if(tmperr == CURLE_AGAIN) { - infof(data, "writing would block"); + CURL_TRC_CF(data, cf, "writing would block"); /* fall through */ } else if(tmperr != CURLE_OK) { @@ -589,14 +678,13 @@ } if(wants_read) { - infof(data, "rustls_connection wants us to read_tls."); - + CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls."); if(tls_recv_more(cf, data, &tmperr) < 0) { if(tmperr == CURLE_AGAIN) { - infof(data, "reading would block"); + CURL_TRC_CF(data, cf, "reading would block"); /* fall through */ } - else if(tmperr == CURLE_READ_ERROR) { + else if(tmperr == CURLE_RECV_ERROR) { return CURLE_SSL_CONNECT_ERROR; } else { @@ -619,37 +707,12 @@ } static CURLcode -cr_connect_blocking(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM) +cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data) { bool done; /* unused */ return cr_connect_common(cf, data, true, &done); } -static void cr_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) -{ - if(!cf->connected) { - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = - (struct rustls_ssl_backend_data *)connssl->backend; - struct rustls_connection *rconn = NULL; - - (void)data; - DEBUGASSERT(backend); - rconn = backend->conn; - - if(rustls_connection_wants_write(rconn)) { - Curl_pollset_add_out(data, ps, sock); - } - if(rustls_connection_wants_read(rconn)) { - Curl_pollset_add_in(data, ps, sock); - } - } -} - static void * cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) @@ -670,8 +733,8 @@ ssize_t n = 0; DEBUGASSERT(backend); - - if(backend->conn) { + if(backend->conn && !connssl->peer_closed) { + CURL_TRC_CF(data, cf, "closing connection, send notify"); rustls_connection_send_close_notify(backend->conn); n = cr_send(cf, data, NULL, 0, &tmperr); if(n < 0) { @@ -696,7 +759,6 @@ const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, SSLSUPP_CAINFO_BLOB | /* supports */ - SSLSUPP_TLS13_CIPHERSUITES | SSLSUPP_HTTPS_PROXY, sizeof(struct rustls_ssl_backend_data), @@ -710,11 +772,10 @@ Curl_none_cert_status_request, /* cert_status_request */ cr_connect_blocking, /* connect */ cr_connect_nonblocking, /* connect_nonblocking */ - cr_adjust_pollset, /* adjust_pollset */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ cr_get_internals, /* get_internals */ cr_close, /* close_one */ Curl_none_close_all, /* close_all */ - Curl_none_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 45c3373..19cdc4b 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -1071,7 +1071,7 @@ DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - connssl->peer.hostname, connssl->port)); + connssl->peer.hostname, connssl->peer.port)); if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, VERSION_LESS_THAN_EQUAL)) { @@ -1129,7 +1129,8 @@ /* check for an existing reusable credential handle */ if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&old_cred, NULL)) { backend->cred = old_cred; DEBUGF(infof(data, "schannel: reusing existing credential handle")); @@ -1159,7 +1160,7 @@ } /* Warn if SNI is disabled due to use of an IP address */ - if(connssl->peer.is_ip_address) { + if(connssl->peer.type != CURL_SSL_PEER_DNS) { infof(data, "schannel: using IP address, SNI is not supported by OS."); } @@ -1335,7 +1336,7 @@ DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->peer.hostname, connssl->port)); + connssl->peer.hostname, connssl->peer.port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1569,9 +1570,13 @@ DEBUGF(infof(data, "schannel: SSL/TLS handshake complete")); } +#ifndef CURL_DISABLE_PROXY pubkey_ptr = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + pubkey_ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif if(pubkey_ptr) { result = schannel_pkp_pin_peer_pubkey(cf, data, pubkey_ptr); if(result) { @@ -1670,6 +1675,28 @@ return args->result == CURLE_OK; } +static void schannel_session_free(void *sessionid, size_t idsize) +{ + /* this is expected to be called under sessionid lock */ + struct Curl_schannel_cred *cred = sessionid; + + (void)idsize; + if(cred) { + cred->refcount--; + if(cred->refcount == 0) { + s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + curlx_unicodefree(cred->sni_hostname); +#ifdef HAS_CLIENT_CERT_PATH + if(cred->client_cert_store) { + CertCloseStore(cred->client_cert_store, 0); + cred->client_cert_store = NULL; + } +#endif + Curl_safefree(cred); + } + } +} + static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1689,7 +1716,7 @@ DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->peer.hostname, connssl->port)); + connssl->peer.hostname, connssl->peer.port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -1747,11 +1774,11 @@ /* save the current session data for possible reuse */ if(ssl_config->primary.sessionid) { bool incache; - bool added = FALSE; struct Curl_schannel_cred *old_cred = NULL; Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&old_cred, NULL)); if(incache) { if(old_cred != backend->cred) { DEBUGF(infof(data, @@ -1762,20 +1789,15 @@ } } if(!incache) { - result = Curl_ssl_addsessionid(cf, data, backend->cred, + /* Up ref count since call takes ownership */ + backend->cred->refcount++; + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, backend->cred, sizeof(struct Curl_schannel_cred), - &added); + schannel_session_free); if(result) { Curl_ssl_sessionid_unlock(data); - failf(data, "schannel: failed to store credential handle"); return result; } - else if(added) { - /* this cred session is now also referenced by sessionid cache */ - backend->cred->refcount++; - DEBUGF(infof(data, - "schannel: stored credential handle in session cache")); - } } Curl_ssl_sessionid_unlock(data); } @@ -2133,7 +2155,6 @@ infof(data, "schannel: server indicated shutdown in a prior call"); goto cleanup; } - /* It's debatable what to return when !len. Regardless we can't return immediately because there may be data to decrypt (in the case we want to decrypt all encrypted cached data) so handle !len later in cleanup. @@ -2317,10 +2338,10 @@ /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not returned so we have to work around that in cleanup. */ backend->recv_sspi_close_notify = true; - if(!backend->recv_connection_closed) { + if(!backend->recv_connection_closed) backend->recv_connection_closed = true; - infof(data, "schannel: server closed the connection"); - } + infof(data, + "schannel: server close notification received (close_notify)"); goto cleanup; } } @@ -2443,32 +2464,14 @@ if(backend->ctxt) /* SSL/TLS is in use */ return (backend->decdata_offset > 0 || - (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); + (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) || + backend->recv_connection_closed || + backend->recv_sspi_close_notify || + backend->recv_unrecoverable_err); else return FALSE; } -static void schannel_session_free(void *ptr) -{ - /* this is expected to be called under sessionid lock */ - struct Curl_schannel_cred *cred = ptr; - - if(cred) { - cred->refcount--; - if(cred->refcount == 0) { - s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); - curlx_unicodefree(cred->sni_hostname); -#ifdef HAS_CLIENT_CERT_PATH - if(cred->client_cert_store) { - CertCloseStore(cred->client_cert_store, 0); - cred->client_cert_store = NULL; - } -#endif - Curl_safefree(cred); - } - } -} - /* shut down the SSL connection and clean up related memory. this function can be called multiple times on the same connection including if the SSL connection failed (eg connection made but failed handshake). */ @@ -2487,7 +2490,7 @@ if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->peer.hostname, connssl->port); + connssl->peer.hostname, connssl->peer.port); } if(backend->cred && backend->ctxt) { @@ -2552,7 +2555,7 @@ /* free SSPI Schannel API credential handle */ if(backend->cred) { Curl_ssl_sessionid_lock(data); - schannel_session_free(backend->cred); + schannel_session_free(backend->cred, 0); Curl_ssl_sessionid_unlock(data); backend->cred = NULL; } @@ -2747,7 +2750,7 @@ const struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; struct schannel_multi_ssl_backend_data *mbackend; const struct ssl_general_config *cfg = &data->set.general_ssl; @@ -2816,7 +2819,7 @@ HCERTSTORE cert_store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; struct schannel_multi_ssl_backend_data *mbackend; unsigned char *CAinfo_blob_digest = NULL; @@ -2915,7 +2918,6 @@ schannel_get_internals, /* get_internals */ schannel_close, /* close_one */ Curl_none_close_all, /* close_all */ - schannel_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h index fe7450d..5e233a9 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_int.h +++ b/Utilities/cmcurl/lib/vtls/schannel_int.h
@@ -53,6 +53,16 @@ #define CERT_ALT_NAME_IP_ADDRESS 8 #endif +#if defined(_MSC_VER) && (_MSC_VER <= 1600) +/* Workaround for warning: + 'type cast' : conversion from 'int' to 'LPCSTR' of greater size */ +#undef CERT_STORE_PROV_MEMORY +#undef CERT_STORE_PROV_SYSTEM_A +#undef CERT_STORE_PROV_SYSTEM_W +#define CERT_STORE_PROV_MEMORY ((LPCSTR)(size_t)2) +#define CERT_STORE_PROV_SYSTEM_A ((LPCSTR)(size_t)9) +#define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)(size_t)10) +#endif #ifndef SCH_CREDENTIALS_VERSION
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 1f37305..f49db64 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -1453,7 +1453,7 @@ return true; } -static bool is_separator(char c) +static bool sectransp_is_separator(char c) { /* Return whether character is a cipher list separator. */ switch(c) { @@ -1547,7 +1547,7 @@ if(!ciphers) return CURLE_OK; - while(is_separator(*ciphers)) /* Skip initial separators. */ + while(sectransp_is_separator(*ciphers)) /* Skip initial separators. */ ciphers++; if(!*ciphers) return CURLE_OK; @@ -1561,14 +1561,14 @@ size_t i; /* Skip separators */ - while(is_separator(*cipher_start)) + while(sectransp_is_separator(*cipher_start)) cipher_start++; if(*cipher_start == '\0') { break; } /* Find last position of a cipher in the ciphers string */ cipher_end = cipher_start; - while(*cipher_end != '\0' && !is_separator(*cipher_end)) { + while(*cipher_end != '\0' && !sectransp_is_separator(*cipher_end)) { ++cipher_end; } @@ -1636,6 +1636,18 @@ return CURLE_OK; } +static void sectransp_session_free(void *sessionid, size_t idsize) +{ + /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a + cached session ID inside the Security framework. There is a private + function that does this, but I don't want to have to explain to you why I + got your application rejected from the App Store due to the use of a + private API, so the best we can do is free up our own char array that we + created way back in sectransp_connect_step1... */ + (void)idsize; + Curl_safefree(sessionid); +} + static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -2008,7 +2020,7 @@ return CURLE_SSL_CONNECT_ERROR; } - if(connssl->peer.is_ip_address) { + if(connssl->peer.type != CURL_SSL_PEER_DNS) { infof(data, "WARNING: using IP address, SNI is being disabled by " "the OS."); } @@ -2047,8 +2059,8 @@ size_t ssl_sessionid_len; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, (void **)&ssl_sessionid, - &ssl_sessionid_len)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + (void **)&ssl_sessionid, &ssl_sessionid_len)) { /* we got a session id, use it! */ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); Curl_ssl_sessionid_unlock(data); @@ -2067,7 +2079,7 @@ aprintf("%s:%d:%d:%s:%d", ssl_cafile ? ssl_cafile : "(blob memory)", verifypeer, conn_config->verifyhost, connssl->peer.hostname, - connssl->port); + connssl->peer.port); ssl_sessionid_len = strlen(ssl_sessionid); err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); @@ -2077,13 +2089,12 @@ return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_addsessionid(cf, data, ssl_sessionid, - ssl_sessionid_len, NULL); + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, ssl_sessionid, + ssl_sessionid_len, + sectransp_session_free); Curl_ssl_sessionid_unlock(data); - if(result) { - failf(data, "failed to store ssl session"); + if(result) return result; - } } } @@ -3225,17 +3236,6 @@ return rc; } -static void sectransp_session_free(void *ptr) -{ - /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a - cached session ID inside the Security framework. There is a private - function that does this, but I don't want to have to explain to you why I - got your application rejected from the App Store due to the use of a - private API, so the best we can do is free up our own char array that we - created way back in sectransp_connect_step1... */ - Curl_safefree(ptr); -} - static size_t sectransp_version(char *buffer, size_t size) { return msnprintf(buffer, size, "SecureTransport"); @@ -3469,7 +3469,6 @@ sectransp_get_internals, /* get_internals */ sectransp_close, /* close_one */ Curl_none_close_all, /* close_all */ - sectransp_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index e928ba5..570a10d 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -534,10 +534,10 @@ */ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct ssl_peer *peer, void **ssl_sessionid, size_t *idsize) /* set 0 if unknown */ { - struct ssl_connect_data *connssl = cf->ctx; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *check; @@ -567,14 +567,15 @@ if(!check->sessionid) /* not session ID means blank entry */ continue; - if(strcasecompare(connssl->peer.hostname, check->name) && + if(strcasecompare(peer->hostname, check->name) && ((!cf->conn->bits.conn_to_host && !check->conn_to_host) || (cf->conn->bits.conn_to_host && check->conn_to_host && strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) && ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) || (cf->conn->bits.conn_to_port && check->conn_to_port != -1 && cf->conn->conn_to_port == check->conn_to_port)) && - (connssl->port == check->remote_port) && + (peer->port == check->remote_port) && + (peer->transport == check->transport) && strcasecompare(cf->conn->handler->scheme, check->scheme) && match_ssl_primary_config(data, conn_config, &check->ssl_config)) { /* yes, we have a session ID! */ @@ -591,8 +592,7 @@ DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", no_match? "Didn't find": "Found", Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", - cf->conn->handler->scheme, connssl->peer.hostname, - connssl->port)); + cf->conn->handler->scheme, peer->hostname, peer->port)); return no_match; } @@ -605,9 +605,10 @@ /* defensive check */ /* free the ID the SSL-layer specific way */ - Curl_ssl->session_free(session->sessionid); + session->sessionid_free(session->sessionid, session->idsize); session->sessionid = NULL; + session->sessionid_free = NULL; session->age = 0; /* fresh */ Curl_free_primary_ssl_config(&session->ssl_config); @@ -642,45 +643,44 @@ */ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct ssl_peer *peer, void *ssl_sessionid, size_t idsize, - bool *added) + Curl_ssl_sessionid_dtor *sessionid_free_cb) { - struct ssl_connect_data *connssl = cf->ctx; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); size_t i; struct Curl_ssl_session *store; long oldest_age; - char *clone_host; - char *clone_conn_to_host; + char *clone_host = NULL; + char *clone_conn_to_host = NULL; int conn_to_port; long *general_age; + CURLcode result = CURLE_OUT_OF_MEMORY; - if(added) - *added = FALSE; + DEBUGASSERT(ssl_sessionid); + DEBUGASSERT(sessionid_free_cb); - if(!data->state.session) + if(!data->state.session) { + sessionid_free_cb(ssl_sessionid, idsize); return CURLE_OK; + } store = &data->state.session[0]; oldest_age = data->state.session[0].age; /* zero if unused */ - (void)ssl_config; DEBUGASSERT(ssl_config->primary.sessionid); + (void)ssl_config; - clone_host = strdup(connssl->peer.hostname); + clone_host = strdup(peer->hostname); if(!clone_host) - return CURLE_OUT_OF_MEMORY; /* bail out */ + goto out; if(cf->conn->bits.conn_to_host) { clone_conn_to_host = strdup(cf->conn->conn_to_host.name); - if(!clone_conn_to_host) { - free(clone_host); - return CURLE_OUT_OF_MEMORY; /* bail out */ - } + if(!clone_conn_to_host) + goto out; } - else - clone_conn_to_host = NULL; if(cf->conn->bits.conn_to_port) conn_to_port = cf->conn->conn_to_port; @@ -713,33 +713,43 @@ store = &data->state.session[i]; /* use this slot */ /* now init the session struct wisely */ + if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { + Curl_free_primary_ssl_config(&store->ssl_config); + store->sessionid = NULL; /* let caller free sessionid */ + goto out; + } store->sessionid = ssl_sessionid; store->idsize = idsize; + store->sessionid_free = sessionid_free_cb; store->age = *general_age; /* set current age */ /* free it if there's one already present */ free(store->name); free(store->conn_to_host); store->name = clone_host; /* clone host name */ + clone_host = NULL; store->conn_to_host = clone_conn_to_host; /* clone connect to host name */ + clone_conn_to_host = NULL; store->conn_to_port = conn_to_port; /* connect to port number */ /* port number */ - store->remote_port = connssl->port; + store->remote_port = peer->port; store->scheme = cf->conn->handler->scheme; + store->transport = peer->transport; - if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { - Curl_free_primary_ssl_config(&store->ssl_config); - store->sessionid = NULL; /* let caller free sessionid */ - free(clone_host); - free(clone_conn_to_host); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OK; + +out: + free(clone_host); + free(clone_conn_to_host); + if(result) { + failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]", + store->scheme, store->name, store->remote_port, + Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"); + sessionid_free_cb(ssl_sessionid, idsize); + return result; } - - if(added) - *added = TRUE; - - DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]", - store->scheme, store->name, store->remote_port, - Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server")); + CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]", + store->scheme, store->name, store->remote_port, + Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"); return CURLE_OK; } @@ -774,9 +784,13 @@ if(sock != CURL_SOCKET_BAD) { if(connssl->connecting_state == ssl_connect_2_writing) { Curl_pollset_set_out_only(data, ps, sock); + CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%" + CURL_FORMAT_SOCKET_T, sock); } else { Curl_pollset_set_in_only(data, ps, sock); + CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%" + CURL_FORMAT_SOCKET_T, sock); } } } @@ -1318,7 +1332,6 @@ multissl_get_internals, /* get_internals */ multissl_close, /* close_one */ Curl_none_close_all, /* close_all */ - Curl_none_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */ @@ -1512,7 +1525,7 @@ free(peer->sni); free(peer->hostname); peer->hostname = peer->sni = peer->dispname = NULL; - peer->is_ip_address = FALSE; + peer->type = CURL_SSL_PEER_DNS; } static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1526,23 +1539,28 @@ cf->connected = FALSE; } -static int is_ip_address(const char *hostname) +static ssl_peer_type get_peer_type(const char *hostname) { -#ifdef ENABLE_IPV6 - struct in6_addr addr; + if(hostname && hostname[0]) { +#ifdef USE_IPV6 + struct in6_addr addr; #else - struct in_addr addr; + struct in_addr addr; #endif - return (hostname && hostname[0] && (Curl_inet_pton(AF_INET, hostname, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &addr) + if(Curl_inet_pton(AF_INET, hostname, &addr)) + return CURL_SSL_PEER_IPV4; +#ifdef USE_IPV6 + else if(Curl_inet_pton(AF_INET6, hostname, &addr)) { + return CURL_SSL_PEER_IPV6; + } #endif - )); + } + return CURL_SSL_PEER_DNS; } -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf) +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, + int transport) { - struct ssl_connect_data *connssl = cf->ctx; const char *ehostname, *edispname; int eport; @@ -1566,6 +1584,7 @@ } /* change if ehostname changed */ + DEBUGASSERT(!ehostname || ehostname[0]); if(ehostname && (!peer->hostname || strcmp(ehostname, peer->hostname))) { Curl_ssl_peer_cleanup(peer); @@ -1584,9 +1603,8 @@ } } - peer->sni = NULL; - peer->is_ip_address = is_ip_address(peer->hostname)? TRUE : FALSE; - if(peer->hostname[0] && !peer->is_ip_address) { + peer->type = get_peer_type(peer->hostname); + if(peer->type == CURL_SSL_PEER_DNS && peer->hostname[0]) { /* not an IP address, normalize according to RCC 6066 ch. 3, * max len of SNI is 2^16-1, no trailing dot */ size_t len = strlen(peer->hostname); @@ -1604,7 +1622,8 @@ } } - connssl->port = eport; + peer->port = eport; + peer->transport = transport; return CURLE_OK; } @@ -1657,7 +1676,7 @@ goto out; *done = FALSE; - result = Curl_ssl_peer_init(&connssl->peer, cf); + result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP); if(result) goto out; @@ -1715,32 +1734,17 @@ { struct cf_call_data save; ssize_t nread; - size_t ntotal = 0; CF_DATA_SAVE(save, cf, data); *err = CURLE_OK; - /* Do receive until we fill the buffer somehwhat or EGAIN, error or EOF */ - while(!ntotal || (len - ntotal) > (4*1024)) { - *err = CURLE_OK; - nread = Curl_ssl->recv_plain(cf, data, buf + ntotal, len - ntotal, err); - if(nread < 0) { - if(*err == CURLE_AGAIN && ntotal > 0) { - /* we EAGAINed after having reed data, return the success amount */ - *err = CURLE_OK; - break; - } - /* we have a an error to report */ - goto out; - } - else if(nread == 0) { - /* eof */ - break; - } - ntotal += (size_t)nread; - DEBUGASSERT((size_t)ntotal <= len); + nread = Curl_ssl->recv_plain(cf, data, buf, len, err); + if(nread > 0) { + DEBUGASSERT((size_t)nread <= len); } - nread = (ssize_t)ntotal; -out: + else if(nread == 0) { + /* eof */ + *err = CURLE_OK; + } CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err); CF_DATA_RESTORE(cf, save); @@ -1862,7 +1866,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", - CF_TYPE_SSL, + CF_TYPE_SSL|CF_TYPE_PROXY, CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, @@ -2038,12 +2042,7 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) { -#ifndef CURL_DISABLE_PROXY - return (cf->cft == &Curl_cft_ssl_proxy); -#else - (void)cf; - return FALSE; -#endif + return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY); } struct ssl_config_data *
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 744bbf8..c40ff26 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -37,6 +37,7 @@ #define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */ #define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ #define SSLSUPP_CAINFO_BLOB (1<<6) +#define SSLSUPP_ECH (1<<7) #define ALPN_ACCEPTED "ALPN: server accepted " @@ -107,7 +108,8 @@ /** * Init SSL peer information for filter. Can be called repeatedly. */ -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf); +CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, + struct Curl_cfilter *cf, int transport); /** * Free all allocated data and reset peer information. */
diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index af7ae55..5259bab 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h
@@ -73,9 +73,8 @@ void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ - int port; /* remote port at origin */ BIT(use_alpn); /* if ALPN shall be used in handshake */ - BIT(reused_session); /* session-ID was reused for this */ + BIT(peer_closed); /* peer has closed connection */ }; @@ -124,7 +123,6 @@ void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data); void (*close_all)(struct Curl_easy *data); - void (*session_free)(void *ptr); CURLcode (*set_engine)(struct Curl_easy *data, const char *engine); CURLcode (*set_engine_default)(struct Curl_easy *data); @@ -180,18 +178,22 @@ */ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct ssl_peer *peer, void **ssl_sessionid, size_t *idsize); /* set 0 if unknown */ /* add a new session ID * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). * Caller must ensure that it has properly shared ownership of this sessionid * object with cache (e.g. incrementing refcount on success) + * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` + * to destroy it in case of failure or later removal. */ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, + const struct ssl_peer *peer, void *ssl_sessionid, size_t idsize, - bool *added); + Curl_ssl_sessionid_dtor *sessionid_free_cb); #include "openssl.h" /* OpenSSL versions */ #include "gtls.h" /* GnuTLS versions */
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index a3c017c..2c92f56 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -74,6 +74,14 @@ #include "curl_memory.h" #include "memdebug.h" +#ifdef USE_ECH +# include "curl_base64.h" +# define ECH_ENABLED(__data__) \ + (__data__->set.tls_ech && \ + !(__data__->set.tls_ech & CURLECH_DISABLE)\ + ) +#endif /* USE_ECH */ + /* 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 in wolfSSL's settings.h, and the latter two are build time symbols in @@ -232,7 +240,6 @@ static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio) { wolfSSL_BIO_set_shutdown(bio, 1); - wolfSSL_BIO_set_init(bio, 1); wolfSSL_BIO_set_data(bio, NULL); return 1; } @@ -321,6 +328,8 @@ wolfSSL_BIO_clear_retry_flags(bio); if(nread < 0 && CURLE_AGAIN == result) BIO_set_retry_read(bio); + else if(nread == 0) + connssl->peer_closed = TRUE; return (int)nread; } @@ -408,11 +417,11 @@ #if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS) req_method = TLSv1_client_method(); use_sni(TRUE); + break; #else failf(data, "wolfSSL does not support TLS 1.0"); return CURLE_NOT_BUILT_IN; #endif - break; case CURL_SSLVERSION_TLSv1_1: #ifndef NO_OLD_TLS req_method = TLSv1_1_client_method(); @@ -480,6 +489,7 @@ return CURLE_SSL_CONNECT_ERROR; } #endif + FALLTHROUGH(); default: break; } @@ -710,7 +720,8 @@ void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_delsessionid(data, ssl_sessionid); @@ -722,6 +733,82 @@ Curl_ssl_sessionid_unlock(data); } +#ifdef USE_ECH + 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"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech == CURLECH_GREASE) { + infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + 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(backend->handle, 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 { + struct Curl_dns_entry *dns = NULL; + + dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port); + 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; + + 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(backend->handle, 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 %ld", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + Curl_resolv_unlock(data, dns); + } + } + + if(trying_ech_now + && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) { + infof(data, "ECH: Can't force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + + } +#endif /* USE_ECH */ + #ifdef USE_BIO_CHAIN { WOLFSSL_BIO *bio; @@ -755,9 +842,13 @@ struct wolfssl_ssl_backend_data *backend = (struct wolfssl_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); +#ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif DEBUGASSERT(backend); @@ -852,6 +943,31 @@ } } #endif +#ifdef USE_ECH + else if(-1 == detail) { + /* try access a retry_config ECHConfigList for tracing */ + byte echConfigs[1000]; + word32 echConfigsLen = 1000; + int rv = 0; + + /* this currently doesn't produce the retry_configs */ + rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs, + &echConfigsLen); + if(rv != WOLFSSL_SUCCESS) { + infof(data, "Failed to get ECHConfigs"); + } + else { + char *b64str = NULL; + size_t blen = 0; + + rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen, + &b64str, &blen); + if(!rv && b64str) + infof(data, "ECH: (not yet) retry_configs %s", b64str); + free(b64str); + } + } +#endif else if(backend->io_result == CURLE_AGAIN) { return CURLE_OK; } @@ -897,6 +1013,7 @@ pinnedpubkey, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); + wolfSSL_FreeX509(x509); if(result) { failf(data, "SSL: public key does not match pinned public key"); return result; @@ -941,6 +1058,13 @@ } +static void wolfssl_session_free(void *sessionid, size_t idsize) +{ + (void)idsize; + wolfSSL_SESSION_free(sessionid); +} + + static CURLcode wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -954,40 +1078,27 @@ DEBUGASSERT(backend); if(ssl_config->primary.sessionid) { - bool incache; - bool added = FALSE; - void *old_ssl_sessionid = NULL; /* wolfSSL_get1_session allocates memory that has to be freed. */ WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); if(our_ssl_sessionid) { + void *old_ssl_sessionid = NULL; + bool incache; Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); + incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, + &old_ssl_sessionid, NULL)); if(incache) { - if(old_ssl_sessionid != our_ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } + Curl_ssl_delsessionid(data, old_ssl_sessionid); } - if(!incache) { - result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL); - if(result) { - Curl_ssl_sessionid_unlock(data); - wolfSSL_SESSION_free(our_ssl_sessionid); - failf(data, "failed to store ssl session"); - return result; - } - else { - added = TRUE; - } - } + /* call takes ownership of `our_ssl_sessionid` */ + result = Curl_ssl_addsessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + wolfssl_session_free); Curl_ssl_sessionid_unlock(data); - - if(!added) { - /* If the session info wasn't added to the cache, free our copy. */ - wolfSSL_SESSION_free(our_ssl_sessionid); + if(result) { + failf(data, "failed to store ssl session"); + return result; } } } @@ -1059,7 +1170,8 @@ /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ (void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)wolfSSL_shutdown(backend->handle); + if(!connssl->peer_closed) + (void)wolfSSL_shutdown(backend->handle); wolfSSL_free(backend->handle); backend->handle = NULL; } @@ -1120,12 +1232,6 @@ } -static void wolfssl_session_free(void *ptr) -{ - wolfSSL_SESSION_free(ptr); -} - - static size_t wolfssl_version(char *buffer, size_t size) { #if LIBWOLFSSL_VERSION_HEX >= 0x03006000 @@ -1384,6 +1490,9 @@ #endif SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | +#ifdef USE_ECH + SSLSUPP_ECH | +#endif SSLSUPP_SSL_CTX, sizeof(struct wolfssl_ssl_backend_data), @@ -1402,7 +1511,6 @@ wolfssl_get_internals, /* get_internals */ wolfssl_close, /* close_one */ Curl_none_close_all, /* close_all */ - wolfssl_session_free, /* session_free */ Curl_none_set_engine, /* set_engine */ Curl_none_set_engine_default, /* set_engine_default */ Curl_none_engines_list, /* engines_list */
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index da07936..4564ea9 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -160,6 +160,7 @@ { "2.16.840.1.101.3.4.2.1", "sha256" }, { "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 } }; @@ -467,6 +468,8 @@ const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf)); if(op) result = Curl_dyn_add(store, op->textoid); + else + result = CURLE_BAD_FUNCTION_ARGUMENT; Curl_dyn_free(&buf); } }
diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h index e5a02c8..6adf63a 100644 --- a/Utilities/cmcurl/lib/warnless.h +++ b/Utilities/cmcurl/lib/warnless.h
@@ -77,20 +77,6 @@ #endif /* _WIN32 */ -#if defined(__INTEL_COMPILER) && defined(__unix__) - -int curlx_FD_ISSET(int fd, fd_set *fdset); - -void curlx_FD_SET(int fd, fd_set *fdset); - -void curlx_FD_ZERO(fd_set *fdset); - -unsigned short curlx_htons(unsigned short usnum); - -unsigned short curlx_ntohs(unsigned short usnum); - -#endif /* __INTEL_COMPILER && __unix__ */ - #endif /* HEADER_CURL_WARNLESS_H */ #ifndef HEADER_CURL_WARNLESS_H_REDEFS
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index d976518..6ccf9e6 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c
@@ -114,23 +114,23 @@ case 0: break; case 1: - infof(data, "WS-DEC: %s [%s%s]", msg, - ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL"); + CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s]", msg, + ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL"); break; default: if(dec->head_len < dec->head_total) { - infof(data, "WS-DEC: %s [%s%s](%d/%d)", msg, - ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", - dec->head_len, dec->head_total); + CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s](%d/%d)", msg, + ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", + dec->head_len, dec->head_total); } else { - infof(data, "WS-DEC: %s [%s%s payload=%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "]", - msg, ws_frame_name_of_op(dec->head[0]), - (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", - dec->payload_offset, dec->payload_len); + CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%" + CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "]", + msg, ws_frame_name_of_op(dec->head[0]), + (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", + dec->payload_offset, dec->payload_len); } break; } @@ -277,9 +277,8 @@ Curl_bufq_skip(inraw, (size_t)nwritten); dec->payload_offset += (curl_off_t)nwritten; remain = dec->payload_len - dec->payload_offset; - /* infof(data, "WS-DEC: passed %zd bytes payload, %" - CURL_FORMAT_CURL_OFF_T " remain", - nwritten, remain); */ + CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %" + CURL_FORMAT_CURL_OFF_T " remain", nwritten, remain); } return remain? CURLE_AGAIN : CURLE_OK; @@ -363,7 +362,7 @@ static CURLcode ws_cw_init(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer; + struct ws_cw_ctx *ctx = writer->ctx; (void)data; Curl_bufq_init2(&ctx->buf, WS_CHUNK_SIZE, 1, BUFQ_OPT_SOFT_LIMIT); return CURLE_OK; @@ -371,7 +370,7 @@ static void ws_cw_close(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer; + struct ws_cw_ctx *ctx = writer->ctx; (void) data; Curl_bufq_free(&ctx->buf); } @@ -423,7 +422,7 @@ struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer; + struct ws_cw_ctx *ctx = writer->ctx; struct websocket *ws; CURLcode result; @@ -454,10 +453,12 @@ pass_ctx.cw_type = type; result = ws_dec_pass(&ws->dec, data, &ctx->buf, ws_cw_dec_next, &pass_ctx); - if(result == CURLE_AGAIN) + if(result == CURLE_AGAIN) { /* insufficient amount of data, keep it for later. * we pretend to have written all since we have a copy */ + CURL_TRC_WRITE(data, "websocket, buffered incomplete frame head"); return CURLE_OK; + } else if(result) { infof(data, "WS: decode error %d", (int)result); return result; @@ -717,8 +718,10 @@ if(result) return result; DEBUGASSERT(randlen < sizeof(keyval)); - if(randlen >= sizeof(keyval)) + if(randlen >= sizeof(keyval)) { + free(randstr); return CURLE_FAILED_INIT; + } strcpy(keyval, randstr); free(randstr); for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) { @@ -754,13 +757,26 @@ DEBUGASSERT(data->conn); ws = data->conn->proto.ws; if(!ws) { + size_t chunk_size = WS_CHUNK_SIZE; ws = calloc(1, sizeof(*ws)); if(!ws) return CURLE_OUT_OF_MEMORY; data->conn->proto.ws = ws; - Curl_bufq_init2(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT, +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_WS_CHUNK_SIZE"); + if(p) { + long l = strtol(p, NULL, 10); + if(l > 0 && l <= (1*1024*1024)) { + chunk_size = (size_t)l; + } + } + } +#endif + DEBUGF(infof(data, "WS, using chunk size %zu", chunk_size)); + Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT, BUFQ_OPT_SOFT_LIMIT); - Curl_bufq_init2(&ws->sendbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT, + Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT, BUFQ_OPT_SOFT_LIMIT); ws_dec_init(&ws->dec); ws_enc_init(&ws->enc); @@ -834,7 +850,7 @@ struct ws_collect { struct Curl_easy *data; - void *buffer; + unsigned char *buffer; size_t buflen; size_t bufidx; int frame_age; @@ -886,7 +902,7 @@ return -1; } *err = CURLE_OK; - memcpy(ctx->buffer, buf, nwritten); + memcpy(ctx->buffer + ctx->bufidx, buf, nwritten); ctx->bufidx += nwritten; } return nwritten; @@ -936,10 +952,6 @@ *nread = 0; *metap = NULL; - /* get a download buffer */ - result = Curl_preconnect(data); - if(result) - return result; memset(&ctx, 0, sizeof(ctx)); ctx.data = data; @@ -1001,14 +1013,17 @@ if(!Curl_bufq_is_empty(&ws->sendbuf)) { CURLcode result; const unsigned char *out; - size_t outlen; - ssize_t n; + size_t outlen, n; while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { if(data->set.connect_only) result = Curl_senddata(data, out, outlen, &n); - else - result = Curl_write(data, data->conn->writesockfd, out, outlen, &n); + else { + result = Curl_xfer_send(data, out, outlen, &n); + if(!result && !n && outlen) + result = CURLE_AGAIN; + } + if(result) { if(result == CURLE_AGAIN) { if(!complete) { @@ -1027,8 +1042,8 @@ } } else { - infof(data, "WS: flushed %zu bytes", (size_t)n); - Curl_bufq_skip(&ws->sendbuf, (size_t)n); + infof(data, "WS: flushed %zu bytes", n); + Curl_bufq_skip(&ws->sendbuf, n); } } } @@ -1041,8 +1056,8 @@ unsigned int flags) { struct websocket *ws; - ssize_t nwritten, n; - size_t space; + ssize_t n; + size_t nwritten, space; CURLcode result; *sent = 0; @@ -1073,15 +1088,14 @@ /* raw mode sends exactly what was requested, and this is from within the write callback */ if(Curl_is_in_callback(data)) { - result = Curl_write(data, data->conn->writesockfd, buffer, buflen, - &nwritten); + result = Curl_xfer_send(data, buffer, buflen, &nwritten); } else result = Curl_senddata(data, buffer, buflen, &nwritten); infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", buflen, nwritten); - *sent = (nwritten >= 0)? (size_t)nwritten : 0; + *sent = nwritten; return result; } @@ -1091,7 +1105,7 @@ return result; /* TODO: the current design does not allow partial writes, afaict. - * It is not clear who the application is supposed to react. */ + * It is not clear how the application is supposed to react. */ space = Curl_bufq_space(&ws->sendbuf); DEBUGF(infof(data, "curl_ws_send(len=%zu), sendbuf len=%zu space %zu", buflen, Curl_bufq_len(&ws->sendbuf), space)); @@ -1148,11 +1162,6 @@ } -void Curl_ws_done(struct Curl_easy *data) -{ - (void)data; -} - static CURLcode ws_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead_connection) @@ -1188,6 +1197,7 @@ ZERO_NULL, /* perform_getsock */ ws_disconnect, /* disconnect */ Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_HTTP, /* defport */ @@ -1213,6 +1223,7 @@ ZERO_NULL, /* perform_getsock */ ws_disconnect, /* disconnect */ Curl_http_write_resp, /* write_resp */ + Curl_http_write_resp_hd, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ PORT_HTTPS, /* defport */
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h index 5f40d45..baa77b4 100644 --- a/Utilities/cmcurl/lib/ws.h +++ b/Utilities/cmcurl/lib/ws.h
@@ -75,7 +75,6 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); -void Curl_ws_done(struct Curl_easy *data); extern const struct Curl_handler Curl_handler_ws; #ifdef USE_SSL @@ -85,7 +84,6 @@ #else #define Curl_ws_request(x,y) CURLE_OK -#define Curl_ws_done(x) Curl_nop_stmt #define Curl_ws_free(x) Curl_nop_stmt #endif
diff --git a/Utilities/cmlibuv/src/idna.c b/Utilities/cmlibuv/src/idna.c index 93d982c..858b19d 100644 --- a/Utilities/cmlibuv/src/idna.c +++ b/Utilities/cmlibuv/src/idna.c
@@ -274,6 +274,9 @@ char* ds; int rc; + if (s == se) + return UV_EINVAL; + ds = d; si = s; @@ -308,8 +311,9 @@ return rc; } - if (d < de) - *d++ = '\0'; + if (d >= de) + return UV_EINVAL; + *d++ = '\0'; return d - ds; /* Number of bytes written. */ }
diff --git a/Utilities/cmzlib/Copyright.txt b/Utilities/cmzlib/Copyright.txt index 85de4a1..7df3d0a 100644 --- a/Utilities/cmzlib/Copyright.txt +++ b/Utilities/cmzlib/Copyright.txt
@@ -1,7 +1,7 @@ 'zlib' general purpose compression library -version 1.2.13, October 13th, 2022 +version 1.3.1, January 22nd, 2024 -Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages
diff --git a/Utilities/cmzlib/README b/Utilities/cmzlib/README index ba34d18..c5f9175 100644 --- a/Utilities/cmzlib/README +++ b/Utilities/cmzlib/README
@@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.13 is a general purpose data compression library. All the code is +zlib 1.3.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and @@ -29,18 +29,17 @@ Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at -http://marknelson.us/1997/01/01/zlib-engine/ . +https://marknelson.us/posts/1997/01/01/zlib-engine.html . -The changes made in version 1.2.13 are documented in the file ChangeLog. +The changes made in version 1.3.1 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . -zlib is available in Java using the java.util.zip package, documented at -http://java.sun.com/developer/technicalArticles/Programming/compression/ . +zlib is available in Java using the java.util.zip package. Follow the API +Documentation link at: https://docs.oracle.com/search/?q=java.util.zip . -A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is available -at CPAN (Comprehensive Perl Archive Network) sites, including -http://search.cpan.org/~pmqs/IO-Compress-Zlib/ . +A Perl interface to zlib and bzip2 written by Paul Marquess <pmqs@cpan.org> +can be found at https://github.com/pmqs/IO-Compress . A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is available in Python 1.5 and later versions, see @@ -64,7 +63,7 @@ - zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works when compiled with cc. -- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is +- On Digital Unix 4.0D (formerly OSF/1) on AlphaServer, the cc option -std1 is necessary to get gzprintf working correctly. This is done by configure. - zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with @@ -84,7 +83,7 @@ Copyright notice: - (C) 1995-2022 Jean-loup Gailly and Mark Adler + (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages
diff --git a/Utilities/cmzlib/adler32.c b/Utilities/cmzlib/adler32.c index d0be438..04b81d2 100644 --- a/Utilities/cmzlib/adler32.c +++ b/Utilities/cmzlib/adler32.c
@@ -7,8 +7,6 @@ #include "zutil.h" -local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); - #define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -60,11 +58,7 @@ #endif /* ========================================================================= */ -uLong ZEXPORT adler32_z(adler, buf, len) - uLong adler; - const Bytef *buf; - z_size_t len; -{ +uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) { unsigned long sum2; unsigned n; @@ -131,20 +125,12 @@ } /* ========================================================================= */ -uLong ZEXPORT adler32(adler, buf, len) - uLong adler; - const Bytef *buf; - uInt len; -{ +uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) { return adler32_z(adler, buf, len); } /* ========================================================================= */ -local uLong adler32_combine_(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off64_t len2; -{ +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) { unsigned long sum1; unsigned long sum2; unsigned rem; @@ -169,18 +155,10 @@ } /* ========================================================================= */ -uLong ZEXPORT adler32_combine(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off_t len2; -{ +uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) { return adler32_combine_(adler1, adler2, len2); } -uLong ZEXPORT adler32_combine64(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off64_t len2; -{ +uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) { return adler32_combine_(adler1, adler2, len2); }
diff --git a/Utilities/cmzlib/compress.c b/Utilities/cmzlib/compress.c index 2ad5326..f43bacf 100644 --- a/Utilities/cmzlib/compress.c +++ b/Utilities/cmzlib/compress.c
@@ -19,13 +19,8 @@ memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ -int ZEXPORT compress2(dest, destLen, source, sourceLen, level) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; - int level; -{ +int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen, int level) { z_stream stream; int err; const uInt max = (uInt)-1; @@ -65,12 +60,8 @@ /* =========================================================================== */ -int ZEXPORT compress(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; -{ +int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); } @@ -78,9 +69,7 @@ If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ -uLong ZEXPORT compressBound(sourceLen) - uLong sourceLen; -{ +uLong ZEXPORT compressBound(uLong sourceLen) { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; }
diff --git a/Utilities/cmzlib/crc32.c b/Utilities/cmzlib/crc32.c index f8357b0..6c38f5c 100644 --- a/Utilities/cmzlib/crc32.c +++ b/Utilities/cmzlib/crc32.c
@@ -103,19 +103,6 @@ # define ARMCRC32 #endif -/* Local functions. */ -local z_crc_t multmodp OF((z_crc_t a, z_crc_t b)); -local z_crc_t x2nmodp OF((z_off64_t n, unsigned k)); - -#if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) - local z_word_t byte_swap OF((z_word_t word)); -#endif - -#if defined(W) && !defined(ARMCRC32) - local z_crc_t crc_word OF((z_word_t data)); - local z_word_t crc_word_big OF((z_word_t data)); -#endif - #if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) /* Swap the bytes in a z_word_t to convert between little and big endian. Any @@ -123,9 +110,7 @@ instruction, if one is available. This assumes that word_t is either 32 bits or 64 bits. */ -local z_word_t byte_swap(word) - z_word_t word; -{ +local z_word_t byte_swap(z_word_t word) { # if W == 8 return (word & 0xff00000000000000) >> 56 | @@ -146,24 +131,77 @@ } #endif +#ifdef DYNAMIC_CRC_TABLE +/* ========================================================================= + * Table of powers of x for combining CRC-32s, filled in by make_crc_table() + * below. + */ + local z_crc_t FAR x2n_table[32]; +#else +/* ========================================================================= + * Tables for byte-wise and braided CRC-32 calculations, and a table of powers + * of x for combining CRC-32s, all made by make_crc_table(). + */ +# include "crc32.h" +#endif + /* CRC polynomial. */ #define POLY 0xedb88320 /* p(x) reflected, with x^32 implied */ -#ifdef DYNAMIC_CRC_TABLE +/* + Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, + reflected. For speed, this requires that a not be zero. + */ +local z_crc_t multmodp(z_crc_t a, z_crc_t b) { + z_crc_t m, p; + m = (z_crc_t)1 << 31; + p = 0; + for (;;) { + if (a & m) { + p ^= b; + if ((a & (m - 1)) == 0) + break; + } + m >>= 1; + b = b & 1 ? (b >> 1) ^ POLY : b >> 1; + } + return p; +} + +/* + Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been + initialized. + */ +local z_crc_t x2nmodp(z_off64_t n, unsigned k) { + z_crc_t p; + + p = (z_crc_t)1 << 31; /* x^0 == 1 */ + while (n) { + if (n & 1) + p = multmodp(x2n_table[k & 31], p); + n >>= 1; + k++; + } + return p; +} + +#ifdef DYNAMIC_CRC_TABLE +/* ========================================================================= + * Build the tables for byte-wise and braided CRC-32 calculations, and a table + * of powers of x for combining CRC-32s. + */ local z_crc_t FAR crc_table[256]; -local z_crc_t FAR x2n_table[32]; -local void make_crc_table OF((void)); #ifdef W local z_word_t FAR crc_big_table[256]; local z_crc_t FAR crc_braid_table[W][256]; local z_word_t FAR crc_braid_big_table[W][256]; - local void braid OF((z_crc_t [][256], z_word_t [][256], int, int)); + local void braid(z_crc_t [][256], z_word_t [][256], int, int); #endif #ifdef MAKECRCH - local void write_table OF((FILE *, const z_crc_t FAR *, int)); - local void write_table32hi OF((FILE *, const z_word_t FAR *, int)); - local void write_table64 OF((FILE *, const z_word_t FAR *, int)); + local void write_table(FILE *, const z_crc_t FAR *, int); + local void write_table32hi(FILE *, const z_word_t FAR *, int); + local void write_table64(FILE *, const z_word_t FAR *, int); #endif /* MAKECRCH */ /* @@ -176,7 +214,6 @@ /* Definition of once functionality. */ typedef struct once_s once_t; -local void once OF((once_t *, void (*)(void))); /* Check for the availability of atomics. */ #if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \ @@ -196,10 +233,7 @@ invoke once() at the same time. The state must be a once_t initialized with ONCE_INIT. */ -local void once(state, init) - once_t *state; - void (*init)(void); -{ +local void once(once_t *state, void (*init)(void)) { if (!atomic_load(&state->done)) { if (atomic_flag_test_and_set(&state->begun)) while (!atomic_load(&state->done)) @@ -222,10 +256,7 @@ /* Test and set. Alas, not atomic, but tries to minimize the period of vulnerability. */ -local int test_and_set OF((int volatile *)); -local int test_and_set(flag) - int volatile *flag; -{ +local int test_and_set(int volatile *flag) { int was; was = *flag; @@ -234,10 +265,7 @@ } /* Run the provided init() function once. This is not thread-safe. */ -local void once(state, init) - once_t *state; - void (*init)(void); -{ +local void once(once_t *state, void (*init)(void)) { if (!state->done) { if (test_and_set(&state->begun)) while (!state->done) @@ -279,8 +307,7 @@ combinations of CRC register values and incoming bytes. */ -local void make_crc_table() -{ +local void make_crc_table(void) { unsigned i, j, n; z_crc_t p; @@ -447,11 +474,7 @@ Write the 32-bit values in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ -local void write_table(out, table, k) - FILE *out; - const z_crc_t FAR *table; - int k; -{ +local void write_table(FILE *out, const z_crc_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -464,11 +487,7 @@ Write the high 32-bits of each value in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ -local void write_table32hi(out, table, k) -FILE *out; -const z_word_t FAR *table; -int k; -{ +local void write_table32hi(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -484,11 +503,7 @@ bits. If not, then the type cast and format string can be adjusted accordingly. */ -local void write_table64(out, table, k) - FILE *out; - const z_word_t FAR *table; - int k; -{ +local void write_table64(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) @@ -498,8 +513,7 @@ } /* Actually do the deed. */ -int main() -{ +int main(void) { make_crc_table(); return 0; } @@ -511,12 +525,7 @@ Generate the little and big-endian braid tables for the given n and z_word_t size w. Each array must have room for w blocks of 256 elements. */ -local void braid(ltl, big, n, w) - z_crc_t ltl[][256]; - z_word_t big[][256]; - int n; - int w; -{ +local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) { int k; z_crc_t i, p, q; for (k = 0; k < w; k++) { @@ -531,69 +540,13 @@ } #endif -#else /* !DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Tables for byte-wise and braided CRC-32 calculations, and a table of powers - * of x for combining CRC-32s, all made by make_crc_table(). - */ -#include "crc32.h" #endif /* DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Routines used for CRC calculation. Some are also required for the table - * generation above. - */ - -/* - Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, - reflected. For speed, this requires that a not be zero. - */ -local z_crc_t multmodp(a, b) - z_crc_t a; - z_crc_t b; -{ - z_crc_t m, p; - - m = (z_crc_t)1 << 31; - p = 0; - for (;;) { - if (a & m) { - p ^= b; - if ((a & (m - 1)) == 0) - break; - } - m >>= 1; - b = b & 1 ? (b >> 1) ^ POLY : b >> 1; - } - return p; -} - -/* - Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been - initialized. - */ -local z_crc_t x2nmodp(n, k) - z_off64_t n; - unsigned k; -{ - z_crc_t p; - - p = (z_crc_t)1 << 31; /* x^0 == 1 */ - while (n) { - if (n & 1) - p = multmodp(x2n_table[k & 31], p); - n >>= 1; - k++; - } - return p; -} - /* ========================================================================= * This function can be used by asm versions of crc32(), and to force the * generation of the CRC tables in a threaded application. */ -const z_crc_t FAR * ZEXPORT get_crc_table() -{ +const z_crc_t FAR * ZEXPORT get_crc_table(void) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -619,11 +572,8 @@ #define Z_BATCH_ZEROS 0xa10d3d0c /* computed from Z_BATCH = 3990 */ #define Z_BATCH_MIN 800 /* fewest words in a final batch */ -unsigned long ZEXPORT crc32_z(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - z_size_t len; -{ +unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, + z_size_t len) { z_crc_t val; z_word_t crc1, crc2; const z_word_t *word; @@ -723,18 +673,14 @@ least-significant byte of the word as the first byte of data, without any pre or post conditioning. This is used to combine the CRCs of each braid. */ -local z_crc_t crc_word(data) - z_word_t data; -{ +local z_crc_t crc_word(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data >> 8) ^ crc_table[data & 0xff]; return (z_crc_t)data; } -local z_word_t crc_word_big(data) - z_word_t data; -{ +local z_word_t crc_word_big(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data << 8) ^ @@ -745,11 +691,8 @@ #endif /* ========================================================================= */ -unsigned long ZEXPORT crc32_z(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - z_size_t len; -{ +unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, + z_size_t len) { /* Return initial CRC, if requested. */ if (buf == Z_NULL) return 0; @@ -781,8 +724,8 @@ words = (z_word_t const *)buf; /* Do endian check at execution time instead of compile time, since ARM - processors can change the endianess at execution time. If the - compiler knows what the endianess will be, it can optimize out the + processors can change the endianness at execution time. If the + compiler knows what the endianness will be, it can optimize out the check and the unused branch. */ endian = 1; if (*(unsigned char *)&endian) { @@ -1069,20 +1012,13 @@ #endif /* ========================================================================= */ -unsigned long ZEXPORT crc32(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - uInt len; -{ +unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, + uInt len) { return crc32_z(crc, buf, len); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine64(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off64_t len2; -{ +uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -1090,18 +1026,12 @@ } /* ========================================================================= */ -uLong ZEXPORT crc32_combine(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off_t len2; -{ +uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) { return crc32_combine64(crc1, crc2, (z_off64_t)len2); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen64(len2) - z_off64_t len2; -{ +uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ @@ -1109,17 +1039,11 @@ } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_gen(len2) - z_off_t len2; -{ +uLong ZEXPORT crc32_combine_gen(z_off_t len2) { return crc32_combine_gen64((z_off64_t)len2); } /* ========================================================================= */ -uLong ZEXPORT crc32_combine_op(crc1, crc2, op) - uLong crc1; - uLong crc2; - uLong op; -{ +uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) { return multmodp(op, crc1) ^ (crc2 & 0xffffffff); }
diff --git a/Utilities/cmzlib/deflate.c b/Utilities/cmzlib/deflate.c index 92ded86..14aa1af 100644 --- a/Utilities/cmzlib/deflate.c +++ b/Utilities/cmzlib/deflate.c
@@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.13 Copyright 1995-2022 Jean-loup Gailly and Mark Adler "; + " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -60,9 +60,6 @@ copyright string in the executable of your product. */ -/* =========================================================================== - * Function prototypes. - */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ @@ -70,29 +67,16 @@ finish_done /* finish done, accept no more input or output */ } block_state; -typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +typedef block_state (*compress_func)(deflate_state *s, int flush); /* Compression function. Returns the block state after the call. */ -local int deflateStateCheck OF((z_streamp strm)); -local void slide_hash OF((deflate_state *s)); -local void fill_window OF((deflate_state *s)); -local block_state deflate_stored OF((deflate_state *s, int flush)); -local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_stored(deflate_state *s, int flush); +local block_state deflate_fast(deflate_state *s, int flush); #ifndef FASTEST -local block_state deflate_slow OF((deflate_state *s, int flush)); +local block_state deflate_slow(deflate_state *s, int flush); #endif -local block_state deflate_rle OF((deflate_state *s, int flush)); -local block_state deflate_huff OF((deflate_state *s, int flush)); -local void lm_init OF((deflate_state *s)); -local void putShortMSB OF((deflate_state *s, uInt b)); -local void flush_pending OF((z_streamp strm)); -local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); -local uInt longest_match OF((deflate_state *s, IPos cur_match)); - -#ifdef ZLIB_DEBUG -local void check_match OF((deflate_state *s, IPos start, IPos match, - int length)); -#endif +local block_state deflate_rle(deflate_state *s, int flush); +local block_state deflate_huff(deflate_state *s, int flush); /* =========================================================================== * Local data @@ -195,9 +179,12 @@ * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ -local void slide_hash(s) - deflate_state *s; -{ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) + __attribute__((no_sanitize("memory"))) +# endif +#endif +local void slide_hash(deflate_state *s) { unsigned n, m; Posf *p; uInt wsize = s->w_size; @@ -221,30 +208,177 @@ #endif } +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local unsigned read_buf(z_streamp strm, Bytef *buf, unsigned size) { + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return len; +} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(deflate_state *s) { + unsigned n; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize + MAX_DIST(s)) { + + zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + if (s->insert > s->strstart) + s->insert = s->strstart; + slide_hash(s); + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + /* ========================================================================= */ -int ZEXPORT deflateInit_(strm, level, version, stream_size) - z_streamp strm; - int level; - const char *version; - int stream_size; -{ +int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, + int stream_size) { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ -int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, - version, stream_size) - z_streamp strm; - int level; - int method; - int windowBits; - int memLevel; - int strategy; - const char *version; - int stream_size; -{ +int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, int strategy, + const char *version, int stream_size) { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; @@ -366,7 +500,7 @@ * symbols from which it is being constructed. */ - s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 4); + s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, LIT_BUFS); s->pending_buf_size = (ulg)s->lit_bufsize * 4; if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || @@ -376,8 +510,14 @@ deflateEnd (strm); return Z_MEM_ERROR; } +#ifdef LIT_MEM + s->d_buf = (ushf *)(s->pending_buf + (s->lit_bufsize << 1)); + s->l_buf = s->pending_buf + (s->lit_bufsize << 2); + s->sym_end = s->lit_bufsize - 1; +#else s->sym_buf = s->pending_buf + s->lit_bufsize; s->sym_end = (s->lit_bufsize - 1) * 3; +#endif /* We avoid equality with lit_bufsize*3 because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. @@ -393,9 +533,7 @@ /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ -local int deflateStateCheck(strm) - z_streamp strm; -{ +local int deflateStateCheck(z_streamp strm) { deflate_state *s; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) @@ -416,11 +554,8 @@ } /* ========================================================================= */ -int ZEXPORT deflateSetDictionary(strm, dictionary, dictLength) - z_streamp strm; - const Bytef *dictionary; - uInt dictLength; -{ +int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary, + uInt dictLength) { deflate_state *s; uInt str, n; int wrap; @@ -485,11 +620,8 @@ } /* ========================================================================= */ -int ZEXPORT deflateGetDictionary(strm, dictionary, dictLength) - z_streamp strm; - Bytef *dictionary; - uInt *dictLength; -{ +int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary, + uInt *dictLength) { deflate_state *s; uInt len; @@ -507,9 +639,7 @@ } /* ========================================================================= */ -int ZEXPORT deflateResetKeep(strm) - z_streamp strm; -{ +int ZEXPORT deflateResetKeep(z_streamp strm) { deflate_state *s; if (deflateStateCheck(strm)) { @@ -544,10 +674,32 @@ return Z_OK; } +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init(deflate_state *s) { + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +} + /* ========================================================================= */ -int ZEXPORT deflateReset(strm) - z_streamp strm; -{ +int ZEXPORT deflateReset(z_streamp strm) { int ret; ret = deflateResetKeep(strm); @@ -557,10 +709,7 @@ } /* ========================================================================= */ -int ZEXPORT deflateSetHeader(strm, head) - z_streamp strm; - gz_headerp head; -{ +int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) { if (deflateStateCheck(strm) || strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; @@ -568,11 +717,7 @@ } /* ========================================================================= */ -int ZEXPORT deflatePending(strm, pending, bits) - unsigned *pending; - int *bits; - z_streamp strm; -{ +int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; @@ -582,19 +727,21 @@ } /* ========================================================================= */ -int ZEXPORT deflatePrime(strm, bits, value) - z_streamp strm; - int bits; - int value; -{ +int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) { deflate_state *s; int put; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; +#ifdef LIT_MEM + if (bits < 0 || bits > 16 || + (uchf *)s->d_buf < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; +#else if (bits < 0 || bits > 16 || s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; +#endif do { put = Buf_size - s->bi_valid; if (put > bits) @@ -609,11 +756,7 @@ } /* ========================================================================= */ -int ZEXPORT deflateParams(strm, level, strategy) - z_streamp strm; - int level; - int strategy; -{ +int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) { deflate_state *s; compress_func func; @@ -658,13 +801,8 @@ } /* ========================================================================= */ -int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) - z_streamp strm; - int good_length; - int max_lazy; - int nice_length; - int max_chain; -{ +int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, + int nice_length, int max_chain) { deflate_state *s; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -700,10 +838,7 @@ * * Shifts are used to approximate divisions, for speed. */ -uLong ZEXPORT deflateBound(strm, sourceLen) - z_streamp strm; - uLong sourceLen; -{ +uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) { deflate_state *s; uLong fixedlen, storelen, wraplen; @@ -759,7 +894,8 @@ /* if not default parameters, return one of the conservative bounds */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return (s->w_bits <= s->hash_bits ? fixedlen : storelen) + wraplen; + return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) + + wraplen; /* default settings: return tight bound for that case -- ~0.03% overhead plus a small constant */ @@ -772,10 +908,7 @@ * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ -local void putShortMSB(s, b) - deflate_state *s; - uInt b; -{ +local void putShortMSB(deflate_state *s, uInt b) { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } @@ -786,9 +919,7 @@ * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ -local void flush_pending(strm) - z_streamp strm; -{ +local void flush_pending(z_streamp strm) { unsigned len; deflate_state *s = strm->state; @@ -819,10 +950,7 @@ } while (0) /* ========================================================================= */ -int ZEXPORT deflate(strm, flush) - z_streamp strm; - int flush; -{ +int ZEXPORT deflate(z_streamp strm, int flush) { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; @@ -1134,9 +1262,7 @@ } /* ========================================================================= */ -int ZEXPORT deflateEnd(strm) - z_streamp strm; -{ +int ZEXPORT deflateEnd(z_streamp strm) { int status; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1160,11 +1286,10 @@ * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ -int ZEXPORT deflateCopy(dest, source) - z_streamp dest; - z_streamp source; -{ +int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { #ifdef MAXSEG_64K + (void)dest; + (void)source; return Z_STREAM_ERROR; #else deflate_state *ds; @@ -1188,7 +1313,7 @@ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); - ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, 4); + ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, LIT_BUFS); if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { @@ -1199,10 +1324,15 @@ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); - zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); +#ifdef LIT_MEM + ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1)); + ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2); +#else ds->sym_buf = ds->pending_buf + ds->lit_bufsize; +#endif ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; @@ -1212,66 +1342,6 @@ #endif /* MAXSEG_64K */ } -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->next_in buffer and copying from it. - * (See also flush_pending()). - */ -local unsigned read_buf(strm, buf, size) - z_streamp strm; - Bytef *buf; - unsigned size; -{ - unsigned len = strm->avail_in; - - if (len > size) len = size; - if (len == 0) return 0; - - strm->avail_in -= len; - - zmemcpy(buf, strm->next_in, len); - if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, buf, len); - } -#ifdef GZIP - else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, buf, len); - } -#endif - strm->next_in += len; - strm->total_in += len; - - return len; -} - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -local void lm_init(s) - deflate_state *s; -{ - s->window_size = (ulg)2L*s->w_size; - - CLEAR_HASH(s); - - /* Set the default configuration parameters: - */ - s->max_lazy_match = configuration_table[s->level].max_lazy; - s->good_match = configuration_table[s->level].good_length; - s->nice_match = configuration_table[s->level].nice_length; - s->max_chain_length = configuration_table[s->level].max_chain; - - s->strstart = 0; - s->block_start = 0L; - s->lookahead = 0; - s->insert = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - s->ins_h = 0; -} - #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and @@ -1282,10 +1352,7 @@ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ +local uInt longest_match(deflate_state *s, IPos cur_match) { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ @@ -1433,10 +1500,7 @@ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ +local uInt longest_match(deflate_state *s, IPos cur_match) { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ @@ -1497,19 +1561,23 @@ /* =========================================================================== * Check that the match at match_start is indeed a match. */ -local void check_match(s, start, match, length) - deflate_state *s; - IPos start, match; - int length; -{ +local void check_match(deflate_state *s, IPos start, IPos match, int length) { /* check that the match is indeed a match */ - if (zmemcmp(s->window + match, - s->window + start, length) != EQUAL) { - fprintf(stderr, " start %u, match %u, length %d\n", - start, match, length); + Bytef *back = s->window + (int)match, *here = s->window + start; + IPos len = length; + if (match == (IPos)-1) { + /* match starts one byte before the current window -- just compare the + subsequent length-1 bytes */ + back++; + here++; + len--; + } + if (zmemcmp(back, here, len) != EQUAL) { + fprintf(stderr, " start %u, match %d, length %d\n", + start, (int)match, length); do { - fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); - } while (--length != 0); + fprintf(stderr, "(%02x %02x)", *back++, *here++); + } while (--len != 0); z_error("invalid match"); } if (z_verbose > 1) { @@ -1522,137 +1590,6 @@ #endif /* ZLIB_DEBUG */ /* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -local void fill_window(s) - deflate_state *s; -{ - unsigned n; - unsigned more; /* Amount of free space at the end of the window. */ - uInt wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s->strstart >= wsize + MAX_DIST(s)) { - - zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); - s->match_start -= wsize; - s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ - s->block_start -= (long) wsize; - if (s->insert > s->strstart) - s->insert = s->strstart; - slide_hash(s); - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= MIN_MATCH) { - uInt str = s->strstart - s->insert; - s->ins_h = s->window[str]; - UPDATE_HASH(s, s->ins_h, s->window[str + 1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - while (s->insert) { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < MIN_MATCH) - break; - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - ulg curr = s->strstart + (ulg)(s->lookahead); - ulg init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (ulg)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (ulg)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - -/* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ @@ -1694,10 +1631,7 @@ * copied. It is most efficient with large input and output buffers, which * maximizes the opportunities to have a single copy from next_in to next_out. */ -local block_state deflate_stored(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_stored(deflate_state *s, int flush) { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. @@ -1881,10 +1815,7 @@ * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ -local block_state deflate_fast(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_fast(deflate_state *s, int flush) { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ @@ -1983,10 +1914,7 @@ * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ -local block_state deflate_slow(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_slow(deflate_state *s, int flush) { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ @@ -2114,10 +2042,7 @@ * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ -local block_state deflate_rle(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_rle(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ @@ -2188,10 +2113,7 @@ * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ -local block_state deflate_huff(s, flush) - deflate_state *s; - int flush; -{ +local block_state deflate_huff(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ for (;;) {
diff --git a/Utilities/cmzlib/deflate.h b/Utilities/cmzlib/deflate.h index 1a06cd5..300c6ad 100644 --- a/Utilities/cmzlib/deflate.h +++ b/Utilities/cmzlib/deflate.h
@@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2018 Jean-loup Gailly + * Copyright (C) 1995-2024 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -23,6 +23,10 @@ # define GZIP #endif +/* define LIT_MEM to slightly increase the speed of deflate (order 1% to 2%) at + the cost of a larger memory footprint */ +/* #define LIT_MEM */ + /* =========================================================================== * Internal compression state. */ @@ -217,7 +221,14 @@ /* Depth of each subtree used as tie breaker for trees of equal frequency */ +#ifdef LIT_MEM +# define LIT_BUFS 5 + ushf *d_buf; /* buffer for distances */ + uchf *l_buf; /* buffer for literals/lengths */ +#else +# define LIT_BUFS 4 uchf *sym_buf; /* buffer for distances and literals/lengths */ +#endif uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for @@ -239,7 +250,7 @@ * - I can't count above 4 */ - uInt sym_next; /* running index in sym_buf */ + uInt sym_next; /* running index in symbol buffer */ uInt sym_end; /* symbol table full when sym_next reaches this */ ulg opt_len; /* bit length of current block with optimal trees */ @@ -291,14 +302,14 @@ memory checker errors from longest match routines */ /* in trees.c */ -void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); -int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); -void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); -void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_init(deflate_state *s); +int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc); +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, + ulg stored_len, int last); +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s); +void ZLIB_INTERNAL _tr_align(deflate_state *s); +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) @@ -318,6 +329,25 @@ extern const uch ZLIB_INTERNAL _dist_code[]; #endif +#ifdef LIT_MEM +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->sym_next] = 0; \ + s->l_buf[s->sym_next++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->sym_next == s->sym_end); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ + s->d_buf[s->sym_next] = dist; \ + s->l_buf[s->sym_next++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->sym_next == s->sym_end); \ + } +#else # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->sym_buf[s->sym_next++] = 0; \ @@ -337,6 +367,7 @@ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->sym_next == s->sym_end); \ } +#endif #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \
diff --git a/Utilities/cmzlib/gzclose.c b/Utilities/cmzlib/gzclose.c index caeb99a..48d6a86 100644 --- a/Utilities/cmzlib/gzclose.c +++ b/Utilities/cmzlib/gzclose.c
@@ -8,9 +8,7 @@ /* gzclose() is in a separate file so that it is linked in only if it is used. That way the other gzclose functions can be used instead to avoid linking in unneeded compression or decompression routines. */ -int ZEXPORT gzclose(file) - gzFile file; -{ +int ZEXPORT gzclose(gzFile file) { #ifndef NO_GZCOMPRESS gz_statep state;
diff --git a/Utilities/cmzlib/gzguts.h b/Utilities/cmzlib/gzguts.h index 57faf37..eba7208 100644 --- a/Utilities/cmzlib/gzguts.h +++ b/Utilities/cmzlib/gzguts.h
@@ -1,5 +1,5 @@ /* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004-2019 Mark Adler + * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -7,9 +7,8 @@ # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif -# ifdef _FILE_OFFSET_BITS -# undef _FILE_OFFSET_BITS -# endif +# undef _FILE_OFFSET_BITS +# undef _TIME_BITS #endif #ifdef HAVE_HIDDEN @@ -119,8 +118,8 @@ /* gz* functions always use library allocation functions */ #ifndef STDC - extern voidp malloc OF((uInt size)); - extern void free OF((voidpf ptr)); + extern voidp malloc(uInt size); + extern void free(voidpf ptr); #endif /* get errno and strerror definition */ @@ -138,10 +137,10 @@ /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); #endif /* default memLevel */ @@ -203,17 +202,13 @@ typedef gz_state FAR *gz_statep; /* shared functions */ -void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +void ZLIB_INTERNAL gz_error(gz_statep, int, const char *); #if defined UNDER_CE -char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +char ZLIB_INTERNAL *gz_strwinerror(DWORD error); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ -#ifdef INT_MAX -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) -#else -unsigned ZLIB_INTERNAL gz_intmax OF((void)); -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) -#endif +unsigned ZLIB_INTERNAL gz_intmax(void); +#define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
diff --git a/Utilities/cmzlib/gzlib.c b/Utilities/cmzlib/gzlib.c index 55da46a..983153c 100644 --- a/Utilities/cmzlib/gzlib.c +++ b/Utilities/cmzlib/gzlib.c
@@ -1,5 +1,5 @@ /* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004-2019 Mark Adler + * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -15,10 +15,6 @@ #endif #endif -/* Local functions */ -local void gz_reset OF((gz_statep)); -local gzFile gz_open OF((const void *, int, const char *)); - #if defined UNDER_CE /* Map the Windows error number in ERROR to a locale-dependent error message @@ -30,9 +26,7 @@ The gz_strwinerror function does not change the current setting of GetLastError. */ -char ZLIB_INTERNAL *gz_strwinerror(error) - DWORD error; -{ +char ZLIB_INTERNAL *gz_strwinerror(DWORD error) { static char buf[1024]; wchar_t *msgbuf; @@ -72,9 +66,7 @@ #endif /* UNDER_CE */ /* Reset gzip file state */ -local void gz_reset(state) - gz_statep state; -{ +local void gz_reset(gz_statep state) { state->x.have = 0; /* no output data available */ if (state->mode == GZ_READ) { /* for reading ... */ state->eof = 0; /* not at end of file */ @@ -90,11 +82,7 @@ } /* Open a gzip file either by name or file descriptor. */ -local gzFile gz_open(path, fd, mode) - const void *path; - int fd; - const char *mode; -{ +local gzFile gz_open(const void *path, int fd, const char *mode) { gz_statep state; z_size_t len; int oflag; @@ -269,26 +257,17 @@ } /* -- see zlib.h -- */ -gzFile ZEXPORT gzopen(path, mode) - const char *path; - const char *mode; -{ +gzFile ZEXPORT gzopen(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ -gzFile ZEXPORT gzopen64(path, mode) - const char *path; - const char *mode; -{ +gzFile ZEXPORT gzopen64(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ -gzFile ZEXPORT gzdopen(fd, mode) - int fd; - const char *mode; -{ +gzFile ZEXPORT gzdopen(int fd, const char *mode) { char *path; /* identifier for error messages */ gzFile gz; @@ -306,19 +285,13 @@ /* -- see zlib.h -- */ #ifdef WIDECHAR -gzFile ZEXPORT gzopen_w(path, mode) - const wchar_t *path; - const char *mode; -{ +gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) { return gz_open(path, -2, mode); } #endif /* -- see zlib.h -- */ -int ZEXPORT gzbuffer(file, size) - gzFile file; - unsigned size; -{ +int ZEXPORT gzbuffer(gzFile file, unsigned size) { gz_statep state; /* get internal structure and check integrity */ @@ -335,16 +308,14 @@ /* check and set requested size */ if ((size << 1) < size) return -1; /* need to be able to double it */ - if (size < 2) - size = 2; /* need two bytes to check magic header */ + if (size < 8) + size = 8; /* needed to behave well with flushing */ state->want = size; return 0; } /* -- see zlib.h -- */ -int ZEXPORT gzrewind(file) - gzFile file; -{ +int ZEXPORT gzrewind(gzFile file) { gz_statep state; /* get internal structure */ @@ -365,11 +336,7 @@ } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gzseek64(file, offset, whence) - gzFile file; - z_off64_t offset; - int whence; -{ +z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) { unsigned n; z_off64_t ret; gz_statep state; @@ -442,11 +409,7 @@ } /* -- see zlib.h -- */ -z_off_t ZEXPORT gzseek(file, offset, whence) - gzFile file; - z_off_t offset; - int whence; -{ +z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) { z_off64_t ret; ret = gzseek64(file, (z_off64_t)offset, whence); @@ -454,9 +417,7 @@ } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gztell64(file) - gzFile file; -{ +z_off64_t ZEXPORT gztell64(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -471,9 +432,7 @@ } /* -- see zlib.h -- */ -z_off_t ZEXPORT gztell(file) - gzFile file; -{ +z_off_t ZEXPORT gztell(gzFile file) { z_off64_t ret; ret = gztell64(file); @@ -481,9 +440,7 @@ } /* -- see zlib.h -- */ -z_off64_t ZEXPORT gzoffset64(file) - gzFile file; -{ +z_off64_t ZEXPORT gzoffset64(gzFile file) { z_off64_t offset; gz_statep state; @@ -504,9 +461,7 @@ } /* -- see zlib.h -- */ -z_off_t ZEXPORT gzoffset(file) - gzFile file; -{ +z_off_t ZEXPORT gzoffset(gzFile file) { z_off64_t ret; ret = gzoffset64(file); @@ -514,9 +469,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzeof(file) - gzFile file; -{ +int ZEXPORT gzeof(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -531,10 +484,7 @@ } /* -- see zlib.h -- */ -const char * ZEXPORT gzerror(file, errnum) - gzFile file; - int *errnum; -{ +const char * ZEXPORT gzerror(gzFile file, int *errnum) { gz_statep state; /* get internal structure and check integrity */ @@ -552,9 +502,7 @@ } /* -- see zlib.h -- */ -void ZEXPORT gzclearerr(file) - gzFile file; -{ +void ZEXPORT gzclearerr(gzFile file) { gz_statep state; /* get internal structure and check integrity */ @@ -578,11 +526,7 @@ memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to out of memory. */ -void ZLIB_INTERNAL gz_error(state, err, msg) - gz_statep state; - int err; - const char *msg; -{ +void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) { /* free previously allocated message and clear */ if (state->msg != NULL) { if (state->err != Z_MEM_ERROR) @@ -619,21 +563,20 @@ #endif } -#ifndef INT_MAX /* portably return maximum value for an int (when limits.h presumed not available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned ZLIB_INTERNAL gz_intmax() -{ - unsigned p, q; - - p = 1; +unsigned ZLIB_INTERNAL gz_intmax(void) { +#ifdef INT_MAX + return INT_MAX; +#else + unsigned p = 1, q; do { q = p; p <<= 1; p++; } while (p > q); return q >> 1; -} #endif +}
diff --git a/Utilities/cmzlib/gzread.c b/Utilities/cmzlib/gzread.c index eaa5523..e1a8a8d 100644 --- a/Utilities/cmzlib/gzread.c +++ b/Utilities/cmzlib/gzread.c
@@ -5,25 +5,12 @@ #include "gzguts.h" -/* Local functions */ -local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); -local int gz_avail OF((gz_statep)); -local int gz_look OF((gz_statep)); -local int gz_decomp OF((gz_statep)); -local int gz_fetch OF((gz_statep)); -local int gz_skip OF((gz_statep, z_off64_t)); -local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); - /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ -local int gz_load(state, buf, len, have) - gz_statep state; - unsigned char *buf; - unsigned len; - unsigned *have; -{ +local int gz_load(gz_statep state, unsigned char *buf, unsigned len, + unsigned *have) { int ret; unsigned get, max = ((unsigned)-1 >> 2) + 1; @@ -53,9 +40,7 @@ If strm->avail_in != 0, then the current data is moved to the beginning of the input buffer, and then the remainder of the buffer is loaded with the available data from the input file. */ -local int gz_avail(state) - gz_statep state; -{ +local int gz_avail(gz_statep state) { unsigned got; z_streamp strm = &(state->strm); @@ -88,9 +73,7 @@ case, all further file reads will be directly to either the output buffer or a user buffer. If decompressing, the inflate state will be initialized. gz_look() will return 0 on success or -1 on failure. */ -local int gz_look(state) - gz_statep state; -{ +local int gz_look(gz_statep state) { z_streamp strm = &(state->strm); /* allocate read buffers and inflate memory */ @@ -170,9 +153,7 @@ data. If the gzip stream completes, state->how is reset to LOOK to look for the next gzip stream or raw data, once state->x.have is depleted. Returns 0 on success, -1 on failure. */ -local int gz_decomp(state) - gz_statep state; -{ +local int gz_decomp(gz_statep state) { int ret = Z_OK; unsigned had; z_streamp strm = &(state->strm); @@ -224,9 +205,7 @@ looked for to determine whether to copy or decompress. Returns -1 on error, otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ -local int gz_fetch(state) - gz_statep state; -{ +local int gz_fetch(gz_statep state) { z_streamp strm = &(state->strm); do { @@ -254,10 +233,7 @@ } /* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ -local int gz_skip(state, len) - gz_statep state; - z_off64_t len; -{ +local int gz_skip(gz_statep state, z_off64_t len) { unsigned n; /* skip over len bytes or reach end-of-file, whichever comes first */ @@ -289,11 +265,7 @@ input. Return the number of bytes read. If zero is returned, either the end of file was reached, or there was an error. state->err must be consulted in that case to determine which. */ -local z_size_t gz_read(state, buf, len) - gz_statep state; - voidp buf; - z_size_t len; -{ +local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { z_size_t got; unsigned n; @@ -370,11 +342,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; - voidp buf; - unsigned len; -{ +int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) { gz_statep state; /* get internal structure */ @@ -406,12 +374,7 @@ } /* -- see zlib.h -- */ -z_size_t ZEXPORT gzfread(buf, size, nitems, file) - voidp buf; - z_size_t size; - z_size_t nitems; - gzFile file; -{ +z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) { z_size_t len; gz_statep state; @@ -450,9 +413,7 @@ #else # undef gzgetc #endif -int ZEXPORT gzgetc(file) - gzFile file; -{ +int ZEXPORT gzgetc(gzFile file) { unsigned char buf[1]; gz_statep state; @@ -477,17 +438,12 @@ return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } -int ZEXPORT gzgetc_(file) -gzFile file; -{ +int ZEXPORT gzgetc_(gzFile file) { return gzgetc(file); } /* -- see zlib.h -- */ -int ZEXPORT gzungetc(c, file) - int c; - gzFile file; -{ +int ZEXPORT gzungetc(int c, gzFile file) { gz_statep state; /* get internal structure */ @@ -495,6 +451,10 @@ return -1; state = (gz_statep)file; + /* in case this was just opened, set up the input buffer */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) @@ -544,11 +504,7 @@ } /* -- see zlib.h -- */ -char * ZEXPORT gzgets(file, buf, len) - gzFile file; - char *buf; - int len; -{ +char * ZEXPORT gzgets(gzFile file, char *buf, int len) { unsigned left, n; char *str; unsigned char *eol; @@ -608,9 +564,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzdirect(file) - gzFile file; -{ +int ZEXPORT gzdirect(gzFile file) { gz_statep state; /* get internal structure */ @@ -628,9 +582,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzclose_r(file) - gzFile file; -{ +int ZEXPORT gzclose_r(gzFile file) { int ret, err; gz_statep state;
diff --git a/Utilities/cmzlib/gzwrite.c b/Utilities/cmzlib/gzwrite.c index 16d4a22..6ac0de2 100644 --- a/Utilities/cmzlib/gzwrite.c +++ b/Utilities/cmzlib/gzwrite.c
@@ -5,18 +5,10 @@ #include "gzguts.h" -/* Local functions */ -local int gz_init OF((gz_statep)); -local int gz_comp OF((gz_statep, int)); -local int gz_zero OF((gz_statep, z_off64_t)); -local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); - /* Initialize state for writing a gzip file. Mark initialization by setting state->size to non-zero. Return -1 on a memory allocation failure, or 0 on success. */ -local int gz_init(state) - gz_statep state; -{ +local int gz_init(gz_statep state) { int ret; z_streamp strm = &(state->strm); @@ -70,10 +62,7 @@ deflate() flush value. If flush is Z_FINISH, then the deflate() state is reset to start a new gzip stream. If gz->direct is true, then simply write to the output file without compressing, and ignore flush. */ -local int gz_comp(state, flush) - gz_statep state; - int flush; -{ +local int gz_comp(gz_statep state, int flush) { int ret, writ; unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state->strm); @@ -151,10 +140,7 @@ /* Compress len zeros to output. Return -1 on a write error or memory allocation failure by gz_comp(), or 0 on success. */ -local int gz_zero(state, len) - gz_statep state; - z_off64_t len; -{ +local int gz_zero(gz_statep state, z_off64_t len) { int first; unsigned n; z_streamp strm = &(state->strm); @@ -184,11 +170,7 @@ /* Write len bytes from buf to file. Return the number of bytes written. If the returned value is less than len, then there was an error. */ -local z_size_t gz_write(state, buf, len) - gz_statep state; - voidpc buf; - z_size_t len; -{ +local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) { z_size_t put = len; /* if len is zero, avoid unnecessary operations */ @@ -252,11 +234,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; - voidpc buf; - unsigned len; -{ +int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) { gz_statep state; /* get internal structure */ @@ -280,12 +258,8 @@ } /* -- see zlib.h -- */ -z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) - voidpc buf; - z_size_t size; - z_size_t nitems; - gzFile file; -{ +z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems, + gzFile file) { z_size_t len; gz_statep state; @@ -316,10 +290,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzputc(file, c) - gzFile file; - int c; -{ +int ZEXPORT gzputc(gzFile file, int c) { unsigned have; unsigned char buf[1]; gz_statep state; @@ -364,10 +335,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzputs(file, s) - gzFile file; - const char *s; -{ +int ZEXPORT gzputs(gzFile file, const char *s) { z_size_t len, put; gz_statep state; @@ -394,8 +362,7 @@ #include <stdarg.h> /* -- see zlib.h -- */ -int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) -{ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { int len; unsigned left; char *next; @@ -466,8 +433,7 @@ return len; } -int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) -{ +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { va_list va; int ret; @@ -480,13 +446,10 @@ #else /* !STDC && !Z_HAVE_STDARG_H */ /* -- see zlib.h -- */ -int ZEXPORTVA gzprintf(file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) - gzFile file; - const char *format; - int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; -{ +int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3, + int a4, int a5, int a6, int a7, int a8, int a9, int a10, + int a11, int a12, int a13, int a14, int a15, int a16, + int a17, int a18, int a19, int a20) { unsigned len, left; char *next; gz_statep state; @@ -568,10 +531,7 @@ #endif /* -- see zlib.h -- */ -int ZEXPORT gzflush(file, flush) - gzFile file; - int flush; -{ +int ZEXPORT gzflush(gzFile file, int flush) { gz_statep state; /* get internal structure */ @@ -600,11 +560,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzsetparams(file, level, strategy) - gzFile file; - int level; - int strategy; -{ +int ZEXPORT gzsetparams(gzFile file, int level, int strategy) { gz_statep state; z_streamp strm; @@ -615,7 +571,7 @@ strm = &(state->strm); /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) + if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ @@ -642,9 +598,7 @@ } /* -- see zlib.h -- */ -int ZEXPORT gzclose_w(file) - gzFile file; -{ +int ZEXPORT gzclose_w(gzFile file) { int ret = Z_OK; gz_statep state;
diff --git a/Utilities/cmzlib/inffast.c b/Utilities/cmzlib/inffast.c index 1fec7f3..9354676 100644 --- a/Utilities/cmzlib/inffast.c +++ b/Utilities/cmzlib/inffast.c
@@ -47,10 +47,7 @@ requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ -void ZLIB_INTERNAL inflate_fast(strm, start) -z_streamp strm; -unsigned start; /* inflate()'s starting value for strm->avail_out */ -{ +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */
diff --git a/Utilities/cmzlib/inffast.h b/Utilities/cmzlib/inffast.h index e5c1aa4..49c6d15 100644 --- a/Utilities/cmzlib/inffast.h +++ b/Utilities/cmzlib/inffast.h
@@ -8,4 +8,4 @@ subject to change. Applications should only use zlib.h. */ -void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start);
diff --git a/Utilities/cmzlib/inflate.c b/Utilities/cmzlib/inflate.c index 8acbef4..94ecff0 100644 --- a/Utilities/cmzlib/inflate.c +++ b/Utilities/cmzlib/inflate.c
@@ -91,20 +91,7 @@ # endif #endif -/* function prototypes */ -local int inflateStateCheck OF((z_streamp strm)); -local void fixedtables OF((struct inflate_state FAR *state)); -local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, - unsigned copy)); -#ifdef BUILDFIXED - void makefixed OF((void)); -#endif -local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, - unsigned len)); - -local int inflateStateCheck(strm) -z_streamp strm; -{ +local int inflateStateCheck(z_streamp strm) { struct inflate_state FAR *state; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) @@ -116,9 +103,7 @@ return 0; } -int ZEXPORT inflateResetKeep(strm) -z_streamp strm; -{ +int ZEXPORT inflateResetKeep(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -142,9 +127,7 @@ return Z_OK; } -int ZEXPORT inflateReset(strm) -z_streamp strm; -{ +int ZEXPORT inflateReset(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -155,10 +138,7 @@ return inflateResetKeep(strm); } -int ZEXPORT inflateReset2(strm, windowBits) -z_streamp strm; -int windowBits; -{ +int ZEXPORT inflateReset2(z_streamp strm, int windowBits) { int wrap; struct inflate_state FAR *state; @@ -195,12 +175,8 @@ return inflateReset(strm); } -int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) -z_streamp strm; -int windowBits; -const char *version; -int stream_size; -{ +int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size) { int ret; struct inflate_state FAR *state; @@ -239,22 +215,17 @@ return ret; } -int ZEXPORT inflateInit_(strm, version, stream_size) -z_streamp strm; -const char *version; -int stream_size; -{ +int ZEXPORT inflateInit_(z_streamp strm, const char *version, + int stream_size) { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } -int ZEXPORT inflatePrime(strm, bits, value) -z_streamp strm; -int bits; -int value; -{ +int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + if (bits == 0) + return Z_OK; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; @@ -278,9 +249,7 @@ used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ -local void fixedtables(state) -struct inflate_state FAR *state; -{ +local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; @@ -342,7 +311,7 @@ a.out > inffixed.h */ -void makefixed() +void makefixed(void) { unsigned low, size; struct inflate_state state; @@ -396,11 +365,7 @@ output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ -local int updatewindow(strm, end, copy) -z_streamp strm; -const Bytef *end; -unsigned copy; -{ +local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) { struct inflate_state FAR *state; unsigned dist; @@ -622,10 +587,7 @@ will return Z_BUF_ERROR if it has not reached the end of the stream. */ -int ZEXPORT inflate(strm, flush) -z_streamp strm; -int flush; -{ +int ZEXPORT inflate(z_streamp strm, int flush) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ @@ -1301,9 +1263,7 @@ return ret; } -int ZEXPORT inflateEnd(strm) -z_streamp strm; -{ +int ZEXPORT inflateEnd(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1315,11 +1275,8 @@ return Z_OK; } -int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) -z_streamp strm; -Bytef *dictionary; -uInt *dictLength; -{ +int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, + uInt *dictLength) { struct inflate_state FAR *state; /* check state */ @@ -1338,11 +1295,8 @@ return Z_OK; } -int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) -z_streamp strm; -const Bytef *dictionary; -uInt dictLength; -{ +int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, + uInt dictLength) { struct inflate_state FAR *state; unsigned long dictid; int ret; @@ -1373,10 +1327,7 @@ return Z_OK; } -int ZEXPORT inflateGetHeader(strm, head) -z_streamp strm; -gz_headerp head; -{ +int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) { struct inflate_state FAR *state; /* check state */ @@ -1401,11 +1352,8 @@ called again with more data and the *have state. *have is initialized to zero for the first call. */ -local unsigned syncsearch(have, buf, len) -unsigned FAR *have; -const unsigned char FAR *buf; -unsigned len; -{ +local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, + unsigned len) { unsigned got; unsigned next; @@ -1424,9 +1372,7 @@ return next; } -int ZEXPORT inflateSync(strm) -z_streamp strm; -{ +int ZEXPORT inflateSync(z_streamp strm) { unsigned len; /* number of bytes to look at or looked at */ int flags; /* temporary to save header status */ unsigned long in, out; /* temporary to save total_in and total_out */ @@ -1441,7 +1387,7 @@ /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; - state->hold <<= state->bits & 7; + state->hold >>= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { @@ -1482,9 +1428,7 @@ block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ -int ZEXPORT inflateSyncPoint(strm) -z_streamp strm; -{ +int ZEXPORT inflateSyncPoint(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1492,10 +1436,7 @@ return state->mode == STORED && state->bits == 0; } -int ZEXPORT inflateCopy(dest, source) -z_streamp dest; -z_streamp source; -{ +int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; @@ -1539,10 +1480,7 @@ return Z_OK; } -int ZEXPORT inflateUndermine(strm, subvert) -z_streamp strm; -int subvert; -{ +int ZEXPORT inflateUndermine(z_streamp strm, int subvert) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1557,10 +1495,7 @@ #endif } -int ZEXPORT inflateValidate(strm, check) -z_streamp strm; -int check; -{ +int ZEXPORT inflateValidate(z_streamp strm, int check) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; @@ -1572,9 +1507,7 @@ return Z_OK; } -long ZEXPORT inflateMark(strm) -z_streamp strm; -{ +long ZEXPORT inflateMark(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) @@ -1585,9 +1518,7 @@ (state->mode == MATCH ? state->was - state->length : 0)); } -unsigned long ZEXPORT inflateCodesUsed(strm) -z_streamp strm; -{ +unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return (unsigned long)-1; state = (struct inflate_state FAR *)strm->state;
diff --git a/Utilities/cmzlib/inftrees.c b/Utilities/cmzlib/inftrees.c index 57d2793..98cfe16 100644 --- a/Utilities/cmzlib/inftrees.c +++ b/Utilities/cmzlib/inftrees.c
@@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2022 Mark Adler + * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.13 Copyright 1995-2022 Mark Adler "; + " inflate 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -29,14 +29,9 @@ table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ -int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) -codetype type; -unsigned short FAR *lens; -unsigned codes; -code FAR * FAR *table; -unsigned FAR *bits; -unsigned short FAR *work; -{ +int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work) { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ @@ -62,7 +57,7 @@ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 194, 65}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
diff --git a/Utilities/cmzlib/inftrees.h b/Utilities/cmzlib/inftrees.h index f536653..396f74b 100644 --- a/Utilities/cmzlib/inftrees.h +++ b/Utilities/cmzlib/inftrees.h
@@ -41,8 +41,8 @@ examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes - returns returns 852, and "enough 30 6 15" for distance codes returns 592. - The initial root table size (9 or 6) is found in the fifth argument of the + returns 852, and "enough 30 6 15" for distance codes returns 592. The + initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ @@ -57,6 +57,6 @@ DISTS } codetype; -int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, - unsigned codes, code FAR * FAR *table, - unsigned FAR *bits, unsigned short FAR *work)); +int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work);
diff --git a/Utilities/cmzlib/trees.c b/Utilities/cmzlib/trees.c index 5f305c4..6a523ef 100644 --- a/Utilities/cmzlib/trees.c +++ b/Utilities/cmzlib/trees.c
@@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2021 Jean-loup Gailly + * Copyright (C) 1995-2024 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -122,39 +122,116 @@ int max_length; /* max bit length for the codes */ }; -local const static_tree_desc static_l_desc = +#ifdef NO_INIT_GLOBAL_POINTERS +# define TCONST +#else +# define TCONST const +#endif + +local TCONST static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; -local const static_tree_desc static_d_desc = +local TCONST static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; -local const static_tree_desc static_bl_desc = +local TCONST static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== - * Local (static) routines in this file. + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} -local void tr_static_init OF((void)); -local void init_block OF((deflate_state *s)); -local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); -local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); -local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); -local void build_tree OF((deflate_state *s, tree_desc *desc)); -local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local int build_bl_tree OF((deflate_state *s)); -local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, - int blcodes)); -local void compress_block OF((deflate_state *s, const ct_data *ltree, - const ct_data *dtree)); -local int detect_data_type OF((deflate_state *s)); -local unsigned bi_reverse OF((unsigned code, int len)); -local void bi_windup OF((deflate_state *s)); -local void bi_flush OF((deflate_state *s)); +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(unsigned code, int len) { + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(deflate_state *s) { + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(deflate_state *s) { + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef ZLIB_DEBUG + s->bits_sent = (s->bits_sent + 7) & ~7; +#endif +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes(ct_data *tree, int max_code, ushf *bl_count) { + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + unsigned code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + code = (code + bl_count[bits - 1]) << 1; + next_code[bits] = (ush)code; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = (ush)bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); + } +} #ifdef GEN_TREES_H -local void gen_trees_header OF((void)); +local void gen_trees_header(void); #endif #ifndef ZLIB_DEBUG @@ -168,26 +245,11 @@ #endif /* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -#define put_short(s, w) { \ - put_byte(s, (uch)((w) & 0xff)); \ - put_byte(s, (uch)((ush)(w) >> 8)); \ -} - -/* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef ZLIB_DEBUG -local void send_bits OF((deflate_state *s, int value, int length)); - -local void send_bits(s, value, length) - deflate_state *s; - int value; /* value to send */ - int length; /* number of bits */ -{ +local void send_bits(deflate_state *s, int value, int length) { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; @@ -229,8 +291,7 @@ /* =========================================================================== * Initialize the various 'constant' tables. */ -local void tr_static_init() -{ +local void tr_static_init(void) { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ @@ -323,8 +384,7 @@ ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width) - 1 ? ",\n" : ", ")) -void gen_trees_header() -{ +void gen_trees_header(void) { FILE *header = fopen("trees.h", "w"); int i; @@ -374,11 +434,25 @@ #endif /* GEN_TREES_H */ /* =========================================================================== + * Initialize a new block. + */ +local void init_block(deflate_state *s) { + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->sym_next = s->matches = 0; +} + +/* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ -void ZLIB_INTERNAL _tr_init(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_init(deflate_state *s) { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; @@ -401,24 +475,6 @@ init_block(s); } -/* =========================================================================== - * Initialize a new block. - */ -local void init_block(s) - deflate_state *s; -{ - int n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; - for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; - for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; - - s->dyn_ltree[END_BLOCK].Freq = 1; - s->opt_len = s->static_len = 0L; - s->sym_next = s->matches = 0; -} - #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ @@ -448,11 +504,7 @@ * when the heap property is re-established (each father smaller than its * two sons). */ -local void pqdownheap(s, tree, k) - deflate_state *s; - ct_data *tree; /* the tree to restore */ - int k; /* node to move down */ -{ +local void pqdownheap(deflate_state *s, ct_data *tree, int k) { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { @@ -483,10 +535,7 @@ * The length opt_len is updated; static_len is also updated if stree is * not null. */ -local void gen_bitlen(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ +local void gen_bitlen(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; @@ -561,48 +610,9 @@ } } -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -local void gen_codes(tree, max_code, bl_count) - ct_data *tree; /* the tree to decorate */ - int max_code; /* largest code with non zero frequency */ - ushf *bl_count; /* number of codes at each bit length */ -{ - ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - unsigned code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - code = (code + bl_count[bits - 1]) << 1; - next_code[bits] = (ush)code; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, - "inconsistent bit counts"); - Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); - - for (n = 0; n <= max_code; n++) { - int len = tree[n].Len; - if (len == 0) continue; - /* Now reverse the bits */ - tree[n].Code = (ush)bi_reverse(next_code[len]++, len); - - Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", - n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); - } -} +#ifdef DUMP_BL_TREE +# include <stdio.h> +#endif /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. @@ -612,10 +622,7 @@ * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ -local void build_tree(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ +local void build_tree(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; @@ -700,11 +707,7 @@ * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ -local void scan_tree(s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ +local void scan_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ @@ -745,11 +748,7 @@ * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ -local void send_tree(s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ +local void send_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ @@ -796,9 +795,7 @@ * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ -local int build_bl_tree(s) - deflate_state *s; -{ +local int build_bl_tree(deflate_state *s) { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ @@ -831,10 +828,8 @@ * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ -local void send_all_trees(s, lcodes, dcodes, blcodes) - deflate_state *s; - int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ +local void send_all_trees(deflate_state *s, int lcodes, int dcodes, + int blcodes) { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); @@ -860,12 +855,8 @@ /* =========================================================================== * Send a stored block */ -void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) - deflate_state *s; - charf *buf; /* input block */ - ulg stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last) { send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); @@ -884,9 +875,7 @@ /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ -void ZLIB_INTERNAL _tr_flush_bits(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) { bi_flush(s); } @@ -894,9 +883,7 @@ * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ -void ZLIB_INTERNAL _tr_align(s) - deflate_state *s; -{ +void ZLIB_INTERNAL _tr_align(deflate_state *s) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef ZLIB_DEBUG @@ -906,15 +893,107 @@ } /* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(deflate_state *s, const ct_data *ltree, + const ct_data *dtree) { + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned sx = 0; /* running index in symbol buffers */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->sym_next != 0) do { +#ifdef LIT_MEM + dist = s->d_buf[sx]; + lc = s->l_buf[sx++]; +#else + dist = s->sym_buf[sx++] & 0xff; + dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; + lc = s->sym_buf[sx++]; +#endif + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code + LITERALS + 1, ltree); /* send length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= (unsigned)base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check for no overlay of pending_buf on needed symbols */ +#ifdef LIT_MEM + Assert(s->pending < 2 * (s->lit_bufsize + sx), "pendingBuf overflow"); +#else + Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); +#endif + + } while (sx < s->sym_next); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "block list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(deflate_state *s) { + /* block_mask is the bit mask of block-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long block_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("block-listed") bytes. */ + for (n = 0; n <= 31; n++, block_mask >>= 1) + if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("allow-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "block-listed" or "allow-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ -void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) - deflate_state *s; - charf *buf; /* input block, or NULL if too old */ - ulg stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, + ulg stored_len, int last) { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ @@ -1011,14 +1090,15 @@ * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ -int ZLIB_INTERNAL _tr_tally(s, dist, lc) - deflate_state *s; - unsigned dist; /* distance of matched string */ - unsigned lc; /* match length - MIN_MATCH or unmatched char (dist==0) */ -{ +int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) { +#ifdef LIT_MEM + s->d_buf[s->sym_next] = (ush)dist; + s->l_buf[s->sym_next++] = (uch)lc; +#else s->sym_buf[s->sym_next++] = (uch)dist; s->sym_buf[s->sym_next++] = (uch)(dist >> 8); s->sym_buf[s->sym_next++] = (uch)lc; +#endif if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; @@ -1035,147 +1115,3 @@ } return (s->sym_next == s->sym_end); } - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -local void compress_block(s, ltree, dtree) - deflate_state *s; - const ct_data *ltree; /* literal tree */ - const ct_data *dtree; /* distance tree */ -{ - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned sx = 0; /* running index in sym_buf */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - if (s->sym_next != 0) do { - dist = s->sym_buf[sx++] & 0xff; - dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; - lc = s->sym_buf[sx++]; - if (dist == 0) { - send_code(s, lc, ltree); /* send a literal byte */ - Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send length code */ - extra = extra_lbits[code]; - if (extra != 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra != 0) { - dist -= (unsigned)base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and sym_buf is ok: */ - Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); - - } while (sx < s->sym_next); - - send_code(s, END_BLOCK, ltree); -} - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "block list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -local int detect_data_type(s) - deflate_state *s; -{ - /* block_mask is the bit mask of block-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long block_mask = 0xf3ffc07fUL; - int n; - - /* Check for non-textual ("block-listed") bytes. */ - for (n = 0; n <= 31; n++, block_mask >>= 1) - if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) - return Z_BINARY; - - /* Check for textual ("allow-listed") bytes. */ - if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 - || s->dyn_ltree[13].Freq != 0) - return Z_TEXT; - for (n = 32; n < LITERALS; n++) - if (s->dyn_ltree[n].Freq != 0) - return Z_TEXT; - - /* There are no "block-listed" or "allow-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -local unsigned bi_reverse(code, len) - unsigned code; /* the value to invert */ - int len; /* its bit length */ -{ - register unsigned res = 0; - do { - res |= code & 1; - code >>= 1, res <<= 1; - } while (--len > 0); - return res >> 1; -} - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -local void bi_flush(s) - deflate_state *s; -{ - if (s->bi_valid == 16) { - put_short(s, s->bi_buf); - s->bi_buf = 0; - s->bi_valid = 0; - } else if (s->bi_valid >= 8) { - put_byte(s, (Byte)s->bi_buf); - s->bi_buf >>= 8; - s->bi_valid -= 8; - } -} - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -local void bi_windup(s) - deflate_state *s; -{ - if (s->bi_valid > 8) { - put_short(s, s->bi_buf); - } else if (s->bi_valid > 0) { - put_byte(s, (Byte)s->bi_buf); - } - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef ZLIB_DEBUG - s->bits_sent = (s->bits_sent + 7) & ~7; -#endif -}
diff --git a/Utilities/cmzlib/uncompr.c b/Utilities/cmzlib/uncompr.c index f9532f4..5e25666 100644 --- a/Utilities/cmzlib/uncompr.c +++ b/Utilities/cmzlib/uncompr.c
@@ -24,12 +24,8 @@ Z_DATA_ERROR if the input data was corrupted, including if the input data is an incomplete zlib stream. */ -int ZEXPORT uncompress2(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong *sourceLen; -{ +int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong *sourceLen) { z_stream stream; int err; const uInt max = (uInt)-1; @@ -83,11 +79,7 @@ err; } -int ZEXPORT uncompress(dest, destLen, source, sourceLen) - Bytef *dest; - uLongf *destLen; - const Bytef *source; - uLong sourceLen; -{ +int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, + uLong sourceLen) { return uncompress2(dest, destLen, source, &sourceLen); }
diff --git a/Utilities/cmzlib/zconf.h b/Utilities/cmzlib/zconf.h index fe4ec59..3247b08 100644 --- a/Utilities/cmzlib/zconf.h +++ b/Utilities/cmzlib/zconf.h
@@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -243,7 +243,11 @@ #endif #ifdef Z_SOLO - typedef unsigned long z_size_t; +# ifdef _WIN64 + typedef unsigned long long z_size_t; +# else + typedef unsigned long z_size_t; +# endif #else # define z_longlong long long # if defined(NO_SIZE_T) @@ -298,14 +302,6 @@ # endif #endif -#ifndef Z_ARG /* function prototypes for stdarg */ -# if defined(STDC) || defined(Z_HAVE_STDARG_H) -# define Z_ARG(args) args -# else -# define Z_ARG(args) () -# endif -#endif - /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have @@ -522,7 +518,7 @@ #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else -# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t
diff --git a/Utilities/cmzlib/zlib.h b/Utilities/cmzlib/zlib.h index 808ae68..43c5f2f 100644 --- a/Utilities/cmzlib/zlib.h +++ b/Utilities/cmzlib/zlib.h
@@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.13, October 13th, 2022 + version 1.3.1, January 22nd, 2024 - Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.13" -#define ZLIB_VERNUM 0x12d0 +#define ZLIB_VERSION "1.3.1" +#define ZLIB_VERNUM 0x1310 #define ZLIB_VER_MAJOR 1 -#define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 13 +#define ZLIB_VER_MINOR 3 +#define ZLIB_VER_REVISION 1 #define ZLIB_VER_SUBREVISION 0 /* @@ -78,8 +78,8 @@ even in the case of corrupted input. */ -typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); -typedef void (*free_func) OF((voidpf opaque, voidpf address)); +typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size); +typedef void (*free_func)(voidpf opaque, voidpf address); struct internal_state; @@ -217,7 +217,7 @@ /* basic functions */ -ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +ZEXTERN const char * ZEXPORT zlibVersion(void); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check @@ -225,12 +225,12 @@ */ /* -ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); +ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default - allocation functions. + allocation functions. total_in, total_out, adler, and msg are initialized. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all @@ -247,7 +247,7 @@ */ -ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce @@ -320,8 +320,8 @@ with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six to avoid repeated flush markers due to - avail_out == 0 on return. + avail_out is greater than six when the flush marker begins, in order to avoid + repeated flush markers upon calling deflate() again when avail_out == 0. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was @@ -360,7 +360,7 @@ */ -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT deflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending @@ -375,7 +375,7 @@ /* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateInit(z_streamp strm); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by @@ -383,7 +383,8 @@ read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates - them to use default allocation functions. + them to use default allocation functions. total_in, total_out, adler, and + msg are initialized. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the @@ -397,7 +398,7 @@ */ -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce @@ -517,7 +518,7 @@ */ -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending @@ -535,12 +536,12 @@ */ /* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); +ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy); This is another version of deflateInit with more compression options. The fields zalloc, zfree and opaque must be initialized before by the caller. @@ -607,9 +608,9 @@ compression: this will be done by deflate(). */ -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); +ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this @@ -651,9 +652,9 @@ not perform any compression: this will be done by deflate(). */ -ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); +ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -673,8 +674,8 @@ stream state is inconsistent. */ -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); +ZEXTERN int ZEXPORT deflateCopy(z_streamp dest, + z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -691,20 +692,20 @@ destination. */ -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +ZEXTERN int ZEXPORT deflateReset(z_streamp strm); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been - set unchanged. + set unchanged. total_in, total_out, adler, and msg are initialized. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); +ZEXTERN int ZEXPORT deflateParams(z_streamp strm, + int level, + int strategy); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be @@ -729,7 +730,7 @@ Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be - applied to the the data compressed after deflateParams(). + applied to the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if @@ -740,11 +741,11 @@ retried with more output space. */ -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); +ZEXTERN int ZEXPORT deflateTune(z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for @@ -757,8 +758,8 @@ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); +ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, + uLong sourceLen); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or @@ -772,9 +773,9 @@ than Z_FINISH or Z_NO_FLUSH are used. */ -ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, - unsigned *pending, - int *bits)); +ZEXTERN int ZEXPORT deflatePending(z_streamp strm, + unsigned *pending, + int *bits); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not @@ -787,9 +788,9 @@ stream state was inconsistent. */ -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); +ZEXTERN int ZEXPORT deflatePrime(z_streamp strm, + int bits, + int value); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits @@ -804,8 +805,8 @@ source stream state was inconsistent. */ -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); +ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm, + gz_headerp head); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called @@ -821,16 +822,17 @@ gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). + the time set to zero, and os set to the current operating system, with no + extra, name, or comment fields. The gzip header is returned to the default + state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); +ZEXTERN int ZEXPORT inflateInit2(z_streamp strm, + int windowBits); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized @@ -883,9 +885,9 @@ deferred until inflate() is called. */ -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); +ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, @@ -906,9 +908,9 @@ inflate(). */ -ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); +ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm, + Bytef *dictionary, + uInt *dictLength); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -921,7 +923,7 @@ stream state is inconsistent. */ -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateSync(z_streamp strm); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all @@ -934,14 +936,14 @@ inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. - In the success case, the application may save the current current value of - total_in which indicates where valid compressed data was found. In the - error case, the application may repeatedly call inflateSync, providing more - input each time, until success or end of the input data. + In the success case, the application may save the current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. */ -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); +ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, + z_streamp source); /* Sets the destination stream as a complete copy of the source stream. @@ -956,18 +958,19 @@ destination. */ -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateReset(z_streamp strm); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. + total_in, total_out, adler, and msg are initialized. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ -ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, - int windowBits)); +ZEXTERN int ZEXPORT inflateReset2(z_streamp strm, + int windowBits); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted @@ -980,9 +983,9 @@ the windowBits parameter is invalid. */ -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); +ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, + int bits, + int value); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the @@ -1001,7 +1004,7 @@ stream state was inconsistent. */ -ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +ZEXTERN long ZEXPORT inflateMark(z_streamp strm); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the @@ -1029,8 +1032,8 @@ source stream state was inconsistent. */ -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); +ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, + gz_headerp head); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after @@ -1070,8 +1073,8 @@ */ /* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); +ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits, + unsigned char FAR *window); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized @@ -1091,13 +1094,13 @@ the version of the header file. */ -typedef unsigned (*in_func) OF((void FAR *, - z_const unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); +typedef unsigned (*in_func)(void FAR *, + z_const unsigned char FAR * FAR *); +typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned); -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); +ZEXTERN int ZEXPORT inflateBack(z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than @@ -1165,7 +1168,7 @@ cannot return Z_OK. */ -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm); /* All memory allocated by inflateBackInit() is freed. @@ -1173,7 +1176,7 @@ state was inconsistent. */ -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +ZEXTERN uLong ZEXPORT zlibCompileFlags(void); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: @@ -1226,8 +1229,8 @@ you need special options. */ -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); +ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1241,9 +1244,9 @@ buffer. */ -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); +ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte @@ -1257,15 +1260,15 @@ Z_STREAM_ERROR if the level parameter is invalid. */ -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); +ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1282,8 +1285,8 @@ buffer with the uncompressed data up to that point. */ -ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong *sourceLen)); +ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of @@ -1302,7 +1305,7 @@ typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); Open the gzip (.gz) file at path for reading and decompressing, or compressing and writing. The mode parameter is as in fopen ("rb" or "wb") @@ -1339,7 +1342,7 @@ file could not be opened. */ -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); /* Associate a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has @@ -1362,7 +1365,7 @@ will not detect if fd is invalid (unless fd is -1). */ -ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size); /* Set the internal buffer size used by this library's functions for file to size. The default buffer size is 8192 bytes. This function must be called @@ -1378,7 +1381,7 @@ too late. */ -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy); /* Dynamically update the compression level and strategy for file. See the description of deflateInit2 for the meaning of these parameters. Previously @@ -1389,7 +1392,7 @@ or Z_MEM_ERROR if there is a memory allocation error. */ -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); /* Read and decompress up to len uncompressed bytes from file into buf. If the input file is not in gzip format, gzread copies the given number of @@ -1419,8 +1422,8 @@ Z_STREAM_ERROR. */ -ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, - gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, + gzFile file); /* Read and decompress up to nitems items of size size from file into buf, otherwise operating as gzread() does. This duplicates the interface of @@ -1445,14 +1448,14 @@ file, resetting and retrying on end-of-file, when size is not 1. */ -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len)); +ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); /* Compress and write the len uncompressed bytes at buf to file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ -ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, - z_size_t nitems, gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, + z_size_t nitems, gzFile file); /* Compress and write nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If @@ -1465,7 +1468,7 @@ is returned, and the error state is set to Z_STREAM_ERROR. */ -ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); /* Convert, format, compress, and write the arguments (...) to file under control of the string format, as in fprintf. gzprintf returns the number of @@ -1480,7 +1483,7 @@ This can be determined using zlibCompileFlags(). */ -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); /* Compress and write the given null-terminated string s to file, excluding the terminating null character. @@ -1488,7 +1491,7 @@ gzputs returns the number of characters written, or -1 in case of error. */ -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); /* Read and decompress bytes from file into buf, until len-1 characters are read, or until a newline character is read and transferred to buf, or an @@ -1502,13 +1505,13 @@ buf are indeterminate. */ -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +ZEXTERN int ZEXPORT gzputc(gzFile file, int c); /* Compress and write c, converted to an unsigned char, into file. gzputc returns the value that was written, or -1 in case of error. */ -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +ZEXTERN int ZEXPORT gzgetc(gzFile file); /* Read and decompress one byte from file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. @@ -1517,7 +1520,7 @@ points to has been clobbered or not. */ -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); /* Push c back onto the stream for file to be read as the first character on the next read. At least one character of push-back is always allowed. @@ -1529,7 +1532,7 @@ gzseek() or gzrewind(). */ -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); /* Flush all pending output to file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function @@ -1545,8 +1548,8 @@ */ /* -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); +ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, + z_off_t offset, int whence); Set the starting position to offset relative to whence for the next gzread or gzwrite on file. The offset represents a number of bytes in the @@ -1564,7 +1567,7 @@ would be before the current position. */ -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +ZEXTERN int ZEXPORT gzrewind(gzFile file); /* Rewind file. This function is supported only for reading. @@ -1572,7 +1575,7 @@ */ /* -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +ZEXTERN z_off_t ZEXPORT gztell(gzFile file); Return the starting position for the next gzread or gzwrite on file. This position represents a number of bytes in the uncompressed data stream, @@ -1583,7 +1586,7 @@ */ /* -ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); +ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file); Return the current compressed (actual) read or write offset of file. This offset includes the count of bytes that precede the gzip stream, for example @@ -1592,7 +1595,7 @@ be used for a progress indicator. On error, gzoffset() returns -1. */ -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +ZEXTERN int ZEXPORT gzeof(gzFile file); /* Return true (1) if the end-of-file indicator for file has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set @@ -1607,7 +1610,7 @@ has grown since the previous end of file was detected. */ -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +ZEXTERN int ZEXPORT gzdirect(gzFile file); /* Return true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. @@ -1628,7 +1631,7 @@ gzip file reading and decompression, which may not be desired.) */ -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose(gzFile file); /* Flush all pending output for file, if necessary, close file and deallocate the (de)compression state. Note that once file is closed, you @@ -1641,8 +1644,8 @@ last read ended in the middle of a gzip stream, or Z_OK on success. */ -ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); -ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_r(gzFile file); +ZEXTERN int ZEXPORT gzclose_w(gzFile file); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to @@ -1653,7 +1656,7 @@ zlib library. */ -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); /* Return the error message for the last error which occurred on file. errnum is set to zlib error number. If an error occurred in the file system @@ -1669,7 +1672,7 @@ functions above that do not distinguish those cases in their return values. */ -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +ZEXTERN void ZEXPORT gzclearerr(gzFile file); /* Clear the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip @@ -1686,7 +1689,7 @@ library. */ -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. An Adler-32 value is in the range of a 32-bit @@ -1706,15 +1709,15 @@ if (adler != original_adler) error(); */ -ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, - z_size_t len)); +ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, + z_size_t len); /* Same as adler32(), but with a size_t length. */ /* -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); +ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, + z_off_t len2); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for @@ -1724,7 +1727,7 @@ negative, the result has no meaning or utility. */ -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. @@ -1742,30 +1745,30 @@ if (crc != original_crc) error(); */ -ZEXTERN uLong ZEXPORT crc32_z OF((uLong crc, const Bytef *buf, - z_size_t len)); +ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, + z_size_t len); /* Same as crc32(), but with a size_t length. */ /* -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); +ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. + len2. len2 must be non-negative. */ /* -ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t len2)); +ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); Return the operator corresponding to length len2, to be used with - crc32_combine_op(). + crc32_combine_op(). len2 must be non-negative. */ -ZEXTERN uLong ZEXPORT crc32_combine_op OF((uLong crc1, uLong crc2, uLong op)); +ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); /* Give the same result as crc32_combine(), using op in place of len2. op is is generated from len2 by crc32_combine_gen(). This will be faster than @@ -1778,20 +1781,20 @@ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); +ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateInit_(z_streamp strm, + const char *version, int stream_size); +ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size); +ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size); +ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) @@ -1850,7 +1853,7 @@ unsigned char *next; z_off64_t pos; }; -ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ @@ -1871,13 +1874,13 @@ * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off64_t)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); + ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) @@ -1899,50 +1902,50 @@ # define crc32_combine_gen crc32_combine_gen64 # endif # ifndef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); + ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell64(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); # endif #else - ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t)); + ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); + ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int); + ZEXTERN z_off_t ZEXPORT gztell(gzFile); + ZEXTERN z_off_t ZEXPORT gzoffset(gzFile); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif #else /* Z_SOLO */ - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen OF((z_off_t)); + ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif /* !Z_SOLO */ /* undocumented functions */ -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); -ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); -ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); -ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); -ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF((z_streamp)); -ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); -ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +ZEXTERN const char * ZEXPORT zError(int); +ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void); +ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int); +ZEXTERN int ZEXPORT inflateValidate(z_streamp, int); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp); +ZEXTERN int ZEXPORT inflateResetKeep(z_streamp); +ZEXTERN int ZEXPORT deflateResetKeep(z_streamp); #if defined(_WIN32) && !defined(Z_SOLO) -ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, - const char *mode)); +ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path, + const char *mode); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO -ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, - const char *format, - va_list va)); +ZEXTERN int ZEXPORTVA gzvprintf(gzFile file, + const char *format, + va_list va); # endif #endif
diff --git a/Utilities/cmzlib/zutil.c b/Utilities/cmzlib/zutil.c index 9543ae8..b1c5d2d 100644 --- a/Utilities/cmzlib/zutil.c +++ b/Utilities/cmzlib/zutil.c
@@ -24,13 +24,11 @@ }; -const char * ZEXPORT zlibVersion() -{ +const char * ZEXPORT zlibVersion(void) { return ZLIB_VERSION; } -uLong ZEXPORT zlibCompileFlags() -{ +uLong ZEXPORT zlibCompileFlags(void) { uLong flags; flags = 0; @@ -121,9 +119,7 @@ # endif int ZLIB_INTERNAL z_verbose = verbose; -void ZLIB_INTERNAL z_error(m) - char *m; -{ +void ZLIB_INTERNAL z_error(char *m) { fprintf(stderr, "%s\n", m); exit(1); } @@ -132,9 +128,7 @@ /* exported to allow conversion of error code to string for compress() and * uncompress() */ -const char * ZEXPORT zError(err) - int err; -{ +const char * ZEXPORT zError(int err) { return ERR_MSG(err); } @@ -148,22 +142,14 @@ #ifndef HAVE_MEMCPY -void ZLIB_INTERNAL zmemcpy(dest, source, len) - Bytef* dest; - const Bytef* source; - uInt len; -{ +void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } -int ZLIB_INTERNAL zmemcmp(s1, s2, len) - const Bytef* s1; - const Bytef* s2; - uInt len; -{ +int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) { uInt j; for (j = 0; j < len; j++) { @@ -172,10 +158,7 @@ return 0; } -void ZLIB_INTERNAL zmemzero(dest, len) - Bytef* dest; - uInt len; -{ +void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ @@ -216,8 +199,7 @@ * a protected system like OS/2. Use Microsoft C instead. */ -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; @@ -242,8 +224,7 @@ return buf; } -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { int n; (void)opaque; @@ -279,14 +260,12 @@ # define _hfree hfree #endif -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); } @@ -299,25 +278,18 @@ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC -extern voidp malloc OF((uInt size)); -extern voidp calloc OF((uInt items, uInt size)); -extern void free OF((voidpf ptr)); +extern voidp malloc(uInt size); +extern voidp calloc(uInt items, uInt size); +extern void free(voidpf ptr); #endif -voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) - voidpf opaque; - unsigned items; - unsigned size; -{ +voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } -void ZLIB_INTERNAL zcfree(opaque, ptr) - voidpf opaque; - voidpf ptr; -{ +void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; free(ptr); }
diff --git a/Utilities/cmzlib/zutil.h b/Utilities/cmzlib/zutil.h index 8939d1d..6cf07e7 100644 --- a/Utilities/cmzlib/zutil.h +++ b/Utilities/cmzlib/zutil.h
@@ -1,5 +1,5 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2022 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -56,7 +56,7 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ -#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] +#define ERR_MSG(err) z_errmsg[(err) < -6 || (err) > 2 ? 9 : 2 - (err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) @@ -137,17 +137,8 @@ # endif #endif -#if defined(MACOS) || defined(TARGET_OS_MAC) +#if defined(MACOS) # define OS_CODE 7 -# ifndef Z_SOLO -# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os -# include <unix.h> /* for fdopen */ -# else -# ifndef fdopen -# define fdopen(fd,mode) NULL /* No fdopen() */ -# endif -# endif -# endif #endif #ifdef __acorn @@ -170,18 +161,6 @@ # define OS_CODE 19 #endif -#if defined(_BEOS_) || defined(RISCOS) -# define fdopen(fd,mode) NULL /* No fdopen() */ -#endif - -#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX -# if defined(_WIN32_WCE) -# define fdopen(fd,mode) NULL /* No fdopen() */ -# else -# define fdopen(fd,type) _fdopen(fd,type) -# endif -#endif - #if defined(_MSC_VER) #pragma warning ( disable : 4127 ) /* cond expr is constant */ #pragma warning ( disable : 4131 ) /* old style declaration */ @@ -197,9 +176,9 @@ /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine_gen64 OF((z_off_t)); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); #endif /* common defaults */ @@ -238,16 +217,16 @@ # define zmemzero(dest, len) memset(dest, 0, len) # endif #else - void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); - int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); - void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); + void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len); + int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len); + void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len); #endif /* Diagnostic functions */ #ifdef ZLIB_DEBUG # include <stdio.h> extern int ZLIB_INTERNAL z_verbose; - extern void ZLIB_INTERNAL z_error OF((char *m)); + extern void ZLIB_INTERNAL z_error(char *m); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} @@ -264,9 +243,9 @@ #endif #ifndef Z_SOLO - voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, - unsigned size)); - void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, + unsigned size); + void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr); #endif #define ZALLOC(strm, items, size) \
diff --git a/Utilities/cmzstd/CMakeLists.txt b/Utilities/cmzstd/CMakeLists.txt index 981e3d5..e8bee76 100644 --- a/Utilities/cmzstd/CMakeLists.txt +++ b/Utilities/cmzstd/CMakeLists.txt
@@ -44,7 +44,12 @@ lib/dictBuilder/zdict.c ) -# BMI2 instructions are not supported in older environments. -set_property(TARGET cmzstd PROPERTY COMPILE_DEFINITIONS DYNAMIC_BMI2=0) +target_compile_definitions(cmzstd PRIVATE + # BMI2 instructions are not supported in older environments. + DYNAMIC_BMI2=0 + # Explicitly disable ASM build to work with more compilers. + # Our vendored zstd does not include the assembly language file. + ZSTD_DISABLE_ASM=1 + ) install(FILES LICENSE DESTINATION ${CMAKE_DOC_DIR}/cmzstd)
diff --git a/Utilities/cmzstd/LICENSE b/Utilities/cmzstd/LICENSE index a793a80..7580028 100644 --- a/Utilities/cmzstd/LICENSE +++ b/Utilities/cmzstd/LICENSE
@@ -2,7 +2,7 @@ For Zstandard software -Copyright (c) 2016-present, Facebook, Inc. All rights reserved. +Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -14,9 +14,9 @@ this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. + * Neither the name Facebook, nor Meta, 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
diff --git a/Utilities/cmzstd/README.md b/Utilities/cmzstd/README.md index dcca766..f91e68f 100644 --- a/Utilities/cmzstd/README.md +++ b/Utilities/cmzstd/README.md
@@ -4,23 +4,21 @@ targeting real-time compression scenarios at zlib-level and better compression ratios. It's backed by a very fast entropy stage, provided by [Huff0 and FSE library](https://github.com/Cyan4973/FiniteStateEntropy). -The project is provided as an open-source dual [BSD](LICENSE) and [GPLv2](COPYING) licensed **C** library, +Zstandard's format is stable and documented in [RFC8878](https://datatracker.ietf.org/doc/html/rfc8878). Multiple independent implementations are already available. +This repository represents the reference implementation, provided as an open-source dual [BSD](LICENSE) and [GPLv2](COPYING) licensed **C** library, and a command line utility producing and decoding `.zst`, `.gz`, `.xz` and `.lz4` files. Should your project require another programming language, -a list of known ports and bindings is provided on [Zstandard homepage](http://www.zstd.net/#other-languages). +a list of known ports and bindings is provided on [Zstandard homepage](https://facebook.github.io/zstd/#other-languages). **Development branch status:** [![Build Status][travisDevBadge]][travisLink] -[![Build status][AppveyorDevBadge]][AppveyorLink] [![Build status][CircleDevBadge]][CircleLink] [![Build status][CirrusDevBadge]][CirrusLink] [![Fuzzing Status][OSSFuzzBadge]][OSSFuzzLink] -[travisDevBadge]: https://travis-ci.org/facebook/zstd.svg?branch=dev "Continuous Integration test suite" -[travisLink]: https://travis-ci.org/facebook/zstd -[AppveyorDevBadge]: https://ci.appveyor.com/api/projects/status/xt38wbdxjk5mrbem/branch/dev?svg=true "Windows test suite" -[AppveyorLink]: https://ci.appveyor.com/project/YannCollet/zstd-p0yf0 +[travisDevBadge]: https://api.travis-ci.com/facebook/zstd.svg?branch=dev "Continuous Integration test suite" +[travisLink]: https://travis-ci.com/facebook/zstd [CircleDevBadge]: https://circleci.com/gh/facebook/zstd/tree/dev.svg?style=shield "Short test suite" [CircleLink]: https://circleci.com/gh/facebook/zstd [CirrusDevBadge]: https://api.cirrus-ci.com/github/facebook/zstd.svg?branch=dev @@ -31,37 +29,36 @@ ## Benchmarks For reference, several fast compression algorithms were tested and compared -on a server running Arch Linux (`Linux version 5.5.11-arch1-1`), -with a Core i9-9900K CPU @ 5.0GHz, +on a desktop running Ubuntu 20.04 (`Linux 5.11.0-41-generic`), +with a Core i7-9700K CPU @ 4.9GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with [gcc] 9.3.0, on the [Silesia compression corpus]. [lzbench]: https://github.com/inikep/lzbench -[Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia +[Silesia compression corpus]: https://sun.aei.polsl.pl//~sdeor/index.php?page=silesia [gcc]: https://gcc.gnu.org/ | Compressor name | Ratio | Compression| Decompress.| | --------------- | ------| -----------| ---------- | -| **zstd 1.4.5 -1** | 2.884 | 500 MB/s | 1660 MB/s | -| zlib 1.2.11 -1 | 2.743 | 90 MB/s | 400 MB/s | -| brotli 1.0.7 -0 | 2.703 | 400 MB/s | 450 MB/s | -| **zstd 1.4.5 --fast=1** | 2.434 | 570 MB/s | 2200 MB/s | -| **zstd 1.4.5 --fast=3** | 2.312 | 640 MB/s | 2300 MB/s | -| quicklz 1.5.0 -1 | 2.238 | 560 MB/s | 710 MB/s | -| **zstd 1.4.5 --fast=5** | 2.178 | 700 MB/s | 2420 MB/s | -| lzo1x 2.10 -1 | 2.106 | 690 MB/s | 820 MB/s | -| lz4 1.9.2 | 2.101 | 740 MB/s | 4530 MB/s | -| **zstd 1.4.5 --fast=7** | 2.096 | 750 MB/s | 2480 MB/s | -| lzf 3.6 -1 | 2.077 | 410 MB/s | 860 MB/s | -| snappy 1.1.8 | 2.073 | 560 MB/s | 1790 MB/s | +| **zstd 1.5.1 -1** | 2.887 | 530 MB/s | 1700 MB/s | +| [zlib] 1.2.11 -1 | 2.743 | 95 MB/s | 400 MB/s | +| brotli 1.0.9 -0 | 2.702 | 395 MB/s | 450 MB/s | +| **zstd 1.5.1 --fast=1** | 2.437 | 600 MB/s | 2150 MB/s | +| **zstd 1.5.1 --fast=3** | 2.239 | 670 MB/s | 2250 MB/s | +| quicklz 1.5.0 -1 | 2.238 | 540 MB/s | 760 MB/s | +| **zstd 1.5.1 --fast=4** | 2.148 | 710 MB/s | 2300 MB/s | +| lzo1x 2.10 -1 | 2.106 | 660 MB/s | 845 MB/s | +| [lz4] 1.9.3 | 2.101 | 740 MB/s | 4500 MB/s | +| lzf 3.6 -1 | 2.077 | 410 MB/s | 830 MB/s | +| snappy 1.1.9 | 2.073 | 550 MB/s | 1750 MB/s | -[zlib]: http://www.zlib.net/ -[LZ4]: http://www.lz4.org/ +[zlib]: https://www.zlib.net/ +[lz4]: https://lz4.github.io/lz4/ The negative compression levels, specified with `--fast=#`, -offer faster compression and decompression speed in exchange for some loss in -compression ratio compared to level 1, as seen in the table above. +offer faster compression and decompression speed +at the cost of compression ratio (compared to level 1). Zstd can also offer stronger compression ratios at the cost of compression speed. Speed vs Compression trade-off is configurable by small increments. @@ -124,14 +121,27 @@ ## Build instructions +`make` is the officially maintained build system of this project. +All other build systems are "compatible" and 3rd-party maintained, +they may feature small differences in advanced options. +When your system allows it, prefer using `make` to build `zstd` and `libzstd`. + ### Makefile If your system is compatible with standard `make` (or `gmake`), invoking `make` in root directory will generate `zstd` cli in root directory. +It will also create `libzstd` into `lib/`. Other available options include: - `make install` : create and install zstd cli, library and man pages -- `make check` : create and run `zstd`, tests its behavior on local platform +- `make check` : create and run `zstd`, test its behavior on local platform + +The `Makefile` follows the [GNU Standard Makefile conventions](https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html), +allowing staged install, standard flags, directory variables and command variables. + +For advanced use cases, specialized compilation flags which control binary generation +are documented in [`lib/README.md`](lib/README.md#modular-build) for the `libzstd` library +and in [`programs/README.md`](programs/README.md#compilation-variables) for the `zstd` CLI. ### cmake @@ -141,6 +151,18 @@ By default, `CMAKE_BUILD_TYPE` is set to `Release`. +#### Support for Fat (Universal2) Output + +`zstd` can be built and installed with support for both Apple Silicon (M1/M2) as well as Intel by using CMake's Universal2 support. +To perform a Fat/Universal2 build and install use the following commands: + +```bash +cmake -B build-cmake-debug -S build/cmake -G Ninja -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h;arm64" +cd build-cmake-debug +ninja +sudo ninja install +``` + ### Meson A Meson project is provided within [`build/meson`](build/meson). Follow @@ -178,13 +200,15 @@ ## Testing -You can run quick local smoke tests by executing the `playTest.sh` script from the `src/tests` directory. -Two env variables `$ZSTD_BIN` and `$DATAGEN_BIN` are needed for the test script to locate the zstd and datagen binary. -For information on CI testing, please refer to TESTING.md +You can run quick local smoke tests by running `make check`. +If you can't use `make`, execute the `playTest.sh` script from the `src/tests` directory. +Two env variables `$ZSTD_BIN` and `$DATAGEN_BIN` are needed for the test script to locate the `zstd` and `datagen` binary. +For information on CI testing, please refer to `TESTING.md`. ## Status -Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases. +Zstandard is currently deployed within Facebook and many other large cloud infrastructures. +It is run continuously to compress large amounts of data in multiple formats and use cases. Zstandard is considered safe for production environments. ## License
diff --git a/Utilities/cmzstd/lib/common/allocations.h b/Utilities/cmzstd/lib/common/allocations.h new file mode 100644 index 0000000..a3153c4 --- /dev/null +++ b/Utilities/cmzstd/lib/common/allocations.h
@@ -0,0 +1,55 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +/* This file provides custom allocation primitives + */ + +#define ZSTD_DEPS_NEED_MALLOC +#include "zstd_deps.h" /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */ + +#include "mem.h" /* MEM_STATIC */ +#define ZSTD_STATIC_LINKING_ONLY +#include "../zstd.h" /* ZSTD_customMem */ + +#ifndef ZSTD_ALLOCATIONS_H +#define ZSTD_ALLOCATIONS_H + +/* custom memory allocation functions */ + +MEM_STATIC void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem) +{ + if (customMem.customAlloc) + return customMem.customAlloc(customMem.opaque, size); + return ZSTD_malloc(size); +} + +MEM_STATIC void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem) +{ + if (customMem.customAlloc) { + /* calloc implemented as malloc+memset; + * not as efficient as calloc, but next best guess for custom malloc */ + void* const ptr = customMem.customAlloc(customMem.opaque, size); + ZSTD_memset(ptr, 0, size); + return ptr; + } + return ZSTD_calloc(1, size); +} + +MEM_STATIC void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) +{ + if (ptr!=NULL) { + if (customMem.customFree) + customMem.customFree(customMem.opaque, ptr); + else + ZSTD_free(ptr); + } +} + +#endif /* ZSTD_ALLOCATIONS_H */
diff --git a/Utilities/cmzstd/lib/common/bits.h b/Utilities/cmzstd/lib/common/bits.h new file mode 100644 index 0000000..def56c4 --- /dev/null +++ b/Utilities/cmzstd/lib/common/bits.h
@@ -0,0 +1,200 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#ifndef ZSTD_BITS_H +#define ZSTD_BITS_H + +#include "mem.h" + +MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val) +{ + assert(val != 0); + { + static const U32 DeBruijnBytePos[32] = {0, 1, 28, 2, 29, 14, 24, 3, + 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, + 26, 12, 18, 6, 11, 5, 10, 9}; + return DeBruijnBytePos[((U32) ((val & -(S32) val) * 0x077CB531U)) >> 27]; + } +} + +MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val) +{ + assert(val != 0); +# if defined(_MSC_VER) +# if STATIC_BMI2 == 1 + return (unsigned)_tzcnt_u32(val); +# else + if (val != 0) { + unsigned long r; + _BitScanForward(&r, val); + return (unsigned)r; + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) + return (unsigned)__builtin_ctz(val); +# else + return ZSTD_countTrailingZeros32_fallback(val); +# endif +} + +MEM_STATIC unsigned ZSTD_countLeadingZeros32_fallback(U32 val) { + assert(val != 0); + { + static const U32 DeBruijnClz[32] = {0, 9, 1, 10, 13, 21, 2, 29, + 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, + 19, 27, 23, 6, 26, 5, 4, 31}; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + return 31 - DeBruijnClz[(val * 0x07C4ACDDU) >> 27]; + } +} + +MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val) +{ + assert(val != 0); +# if defined(_MSC_VER) +# if STATIC_BMI2 == 1 + return (unsigned)_lzcnt_u32(val); +# else + if (val != 0) { + unsigned long r; + _BitScanReverse(&r, val); + return (unsigned)(31 - r); + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) + return (unsigned)__builtin_clz(val); +# else + return ZSTD_countLeadingZeros32_fallback(val); +# endif +} + +MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val) +{ + assert(val != 0); +# if defined(_MSC_VER) && defined(_WIN64) +# if STATIC_BMI2 == 1 + return (unsigned)_tzcnt_u64(val); +# else + if (val != 0) { + unsigned long r; + _BitScanForward64(&r, val); + return (unsigned)r; + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(__LP64__) + return (unsigned)__builtin_ctzll(val); +# else + { + U32 mostSignificantWord = (U32)(val >> 32); + U32 leastSignificantWord = (U32)val; + if (leastSignificantWord == 0) { + return 32 + ZSTD_countTrailingZeros32(mostSignificantWord); + } else { + return ZSTD_countTrailingZeros32(leastSignificantWord); + } + } +# endif +} + +MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val) +{ + assert(val != 0); +# if defined(_MSC_VER) && defined(_WIN64) +# if STATIC_BMI2 == 1 + return (unsigned)_lzcnt_u64(val); +# else + if (val != 0) { + unsigned long r; + _BitScanReverse64(&r, val); + return (unsigned)(63 - r); + } else { + /* Should not reach this code path */ + __assume(0); + } +# endif +# elif defined(__GNUC__) && (__GNUC__ >= 4) + return (unsigned)(__builtin_clzll(val)); +# else + { + U32 mostSignificantWord = (U32)(val >> 32); + U32 leastSignificantWord = (U32)val; + if (mostSignificantWord == 0) { + return 32 + ZSTD_countLeadingZeros32(leastSignificantWord); + } else { + return ZSTD_countLeadingZeros32(mostSignificantWord); + } + } +# endif +} + +MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val) +{ + if (MEM_isLittleEndian()) { + if (MEM_64bits()) { + return ZSTD_countTrailingZeros64((U64)val) >> 3; + } else { + return ZSTD_countTrailingZeros32((U32)val) >> 3; + } + } else { /* Big Endian CPU */ + if (MEM_64bits()) { + return ZSTD_countLeadingZeros64((U64)val) >> 3; + } else { + return ZSTD_countLeadingZeros32((U32)val) >> 3; + } + } +} + +MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */ +{ + assert(val != 0); + return 31 - ZSTD_countLeadingZeros32(val); +} + +/* ZSTD_rotateRight_*(): + * Rotates a bitfield to the right by "count" bits. + * https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts + */ +MEM_STATIC +U64 ZSTD_rotateRight_U64(U64 const value, U32 count) { + assert(count < 64); + count &= 0x3F; /* for fickle pattern recognition */ + return (value >> count) | (U64)(value << ((0U - count) & 0x3F)); +} + +MEM_STATIC +U32 ZSTD_rotateRight_U32(U32 const value, U32 count) { + assert(count < 32); + count &= 0x1F; /* for fickle pattern recognition */ + return (value >> count) | (U32)(value << ((0U - count) & 0x1F)); +} + +MEM_STATIC +U16 ZSTD_rotateRight_U16(U16 const value, U32 count) { + assert(count < 16); + count &= 0x0F; /* for fickle pattern recognition */ + return (value >> count) | (U16)(value << ((0U - count) & 0x0F)); +} + +#endif /* ZSTD_BITS_H */
diff --git a/Utilities/cmzstd/lib/common/bitstream.h b/Utilities/cmzstd/lib/common/bitstream.h index 136a188..31feb39 100644 --- a/Utilities/cmzstd/lib/common/bitstream.h +++ b/Utilities/cmzstd/lib/common/bitstream.h
@@ -1,7 +1,7 @@ /* ****************************************************************** * bitstream * Part of FSE library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -32,14 +32,15 @@ #include "compiler.h" /* UNLIKELY() */ #include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */ #include "error_private.h" /* error codes and messages */ +#include "bits.h" /* ZSTD_highbit32 */ /*========================================= * Target specific =========================================*/ #ifndef ZSTD_NO_INTRINSICS -# if defined(__BMI__) && defined(__GNUC__) -# include <immintrin.h> /* support for bextr (experimental) */ +# if (defined(__BMI__) || defined(__BMI2__)) && defined(__GNUC__) +# include <immintrin.h> /* support for bextr (experimental)/bzhi */ # elif defined(__ICCARM__) # include <intrinsics.h> # endif @@ -134,42 +135,6 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /* faster, but works only if nbBits >= 1 */ - - -/*-************************************************************** -* Internal functions -****************************************************************/ -MEM_STATIC unsigned BIT_highbit32 (U32 val) -{ - assert(val != 0); - { -# if defined(_MSC_VER) /* Visual */ -# if STATIC_BMI2 == 1 - return _lzcnt_u32(val) ^ 31; -# else - unsigned long r = 0; - return _BitScanReverse(&r, val) ? (unsigned)r : 0; -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */ - return __builtin_clz (val) ^ 31; -# elif defined(__ICCARM__) /* IAR Intrinsic */ - return 31 - __CLZ(val); -# else /* Software version */ - static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, - 11, 14, 16, 18, 22, 25, 3, 30, - 8, 12, 20, 28, 15, 17, 24, 7, - 19, 27, 23, 6, 26, 5, 4, 31 }; - U32 v = val; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27]; -# endif - } -} - /*===== Local Constants =====*/ static const unsigned BIT_mask[] = { 0, 1, 3, 7, 0xF, 0x1F, @@ -199,6 +164,16 @@ return 0; } +MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) +{ +#if defined(STATIC_BMI2) && STATIC_BMI2 == 1 && !defined(ZSTD_NO_INTRINSICS) + return _bzhi_u64(bitContainer, nbBits); +#else + assert(nbBits < BIT_MASK_SIZE); + return bitContainer & BIT_mask[nbBits]; +#endif +} + /*! BIT_addBits() : * can add up to 31 bits into `bitC`. * Note : does not check for register overflow ! */ @@ -208,7 +183,7 @@ DEBUG_STATIC_ASSERT(BIT_MASK_SIZE == 32); assert(nbBits < BIT_MASK_SIZE); assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8); - bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; + bitC->bitContainer |= BIT_getLowerBits(value, nbBits) << bitC->bitPos; bitC->bitPos += nbBits; } @@ -287,7 +262,7 @@ bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer); bitD->bitContainer = MEM_readLEST(bitD->ptr); { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; - bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ + bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ } } else { bitD->ptr = bitD->start; @@ -295,27 +270,27 @@ switch(srcSize) { case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16); - /* fall-through */ + ZSTD_FALLTHROUGH; case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24); - /* fall-through */ + ZSTD_FALLTHROUGH; case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32); - /* fall-through */ + ZSTD_FALLTHROUGH; case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24; - /* fall-through */ + ZSTD_FALLTHROUGH; case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16; - /* fall-through */ + ZSTD_FALLTHROUGH; case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8; - /* fall-through */ + ZSTD_FALLTHROUGH; default: break; } { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1]; - bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; + bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */ } bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8; @@ -334,16 +309,15 @@ U32 const regMask = sizeof(bitContainer)*8 - 1; /* if start > regMask, bitstream is corrupted, and result is undefined */ assert(nbBits < BIT_MASK_SIZE); - return (bitContainer >> (start & regMask)) & BIT_mask[nbBits]; -} - -MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) -{ -#if defined(STATIC_BMI2) && STATIC_BMI2 == 1 - return _bzhi_u64(bitContainer, nbBits); + /* x86 transform & ((1 << nbBits) - 1) to bzhi instruction, it is better + * than accessing memory. When bmi2 instruction is not present, we consider + * such cpus old (pre-Haswell, 2013) and their performance is not of that + * importance. + */ +#if defined(__x86_64__) || defined(_M_X86) + return (bitContainer >> (start & regMask)) & ((((U64)1) << nbBits) - 1); #else - assert(nbBits < BIT_MASK_SIZE); - return bitContainer & BIT_mask[nbBits]; + return (bitContainer >> (start & regMask)) & BIT_mask[nbBits]; #endif } @@ -393,7 +367,7 @@ } /*! BIT_readBitsFast() : - * unsafe version; only works only if nbBits >= 1 */ + * unsafe version; only works if nbBits >= 1 */ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits) { size_t const value = BIT_lookBitsFast(bitD, nbBits); @@ -424,7 +398,7 @@ * This function is safe, it guarantees it will not read beyond src buffer. * @return : status of `BIT_DStream_t` internal register. * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */ -MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) +MEM_STATIC FORCE_INLINE_ATTR BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */ return BIT_DStream_overflow;
diff --git a/Utilities/cmzstd/lib/common/compiler.h b/Utilities/cmzstd/lib/common/compiler.h index f717855..d07e189 100644 --- a/Utilities/cmzstd/lib/common/compiler.h +++ b/Utilities/cmzstd/lib/common/compiler.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,6 +11,8 @@ #ifndef ZSTD_COMPILER_H #define ZSTD_COMPILER_H +#include "portability_macros.h" + /*-******************************************************* * Compiler specifics *********************************************************/ @@ -40,7 +42,7 @@ /** On MSVC qsort requires that functions passed into it use the __cdecl calling conversion(CC). - This explictly marks such functions as __cdecl so that the code will still compile + This explicitly marks such functions as __cdecl so that the code will still compile if a CC other than __cdecl has been made the default. */ #if defined(_MSC_VER) @@ -92,29 +94,17 @@ /* target attribute */ -#ifndef __has_attribute - #define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ -#endif #if defined(__GNUC__) || defined(__ICCARM__) # define TARGET_ATTRIBUTE(target) __attribute__((__target__(target))) #else # define TARGET_ATTRIBUTE(target) #endif -/* Enable runtime BMI2 dispatch based on the CPU. - * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default. +/* Target attribute for BMI2 dynamic dispatch. + * Enable lzcnt, bmi, and bmi2. + * We test for bmi1 & bmi2. lzcnt is included in bmi1. */ -#ifndef DYNAMIC_BMI2 - #if ((defined(__clang__) && __has_attribute(__target__)) \ - || (defined(__GNUC__) \ - && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \ - && (defined(__x86_64__) || defined(_M_X86)) \ - && !defined(__BMI2__) - # define DYNAMIC_BMI2 1 - #else - # define DYNAMIC_BMI2 0 - #endif -#endif +#define BMI2_TARGET_ATTRIBUTE TARGET_ATTRIBUTE("lzcnt,bmi,bmi2") /* prefetch * can be disabled, by declaring NO_PREFETCH build macro */ @@ -150,8 +140,9 @@ } /* vectorization - * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax */ -#if !defined(__INTEL_COMPILER) && !defined(__clang__) && !defined(__LCC__) && defined(__GNUC__) + * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax, + * and some compilers, like Intel ICC and MCST LCC, do not support it at all. */ +#if !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUC__) && !defined(__LCC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || (__GNUC__ >= 5) # define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize"))) # else @@ -174,6 +165,12 @@ #define UNLIKELY(x) (x) #endif +#if __has_builtin(__builtin_unreachable) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))) +# define ZSTD_UNREACHABLE { assert(0), __builtin_unreachable(); } +#else +# define ZSTD_UNREACHABLE { assert(0); } +#endif + /* disable warnings */ #ifdef _MSC_VER /* Visual Studio */ # include <intrin.h> /* For Visual 2005 */ @@ -190,6 +187,8 @@ # ifdef __AVX2__ //MSVC does not have a BMI2 specific flag, but every CPU that supports AVX2 also supports BMI2 # define STATIC_BMI2 1 # endif +# elif defined(__BMI2__) && defined(__x86_64__) && defined(__GNUC__) +# define STATIC_BMI2 1 # endif #endif @@ -197,26 +196,107 @@ #define STATIC_BMI2 0 #endif -/* compat. with non-clang compilers */ -#ifndef __has_builtin -# define __has_builtin(x) 0 -#endif - -/* compat. with non-clang compilers */ -#ifndef __has_feature -# define __has_feature(x) 0 -#endif - -/* detects whether we are being compiled under msan */ -#ifndef ZSTD_MEMORY_SANITIZER -# if __has_feature(memory_sanitizer) -# define ZSTD_MEMORY_SANITIZER 1 -# else -# define ZSTD_MEMORY_SANITIZER 0 +/* compile time determination of SIMD support */ +#if !defined(ZSTD_NO_INTRINSICS) +# if defined(__SSE2__) || defined(_M_AMD64) || (defined (_M_IX86) && defined(_M_IX86_FP) && (_M_IX86_FP >= 2)) +# define ZSTD_ARCH_X86_SSE2 +# endif +# if defined(__ARM_NEON) || defined(_M_ARM64) +# define ZSTD_ARCH_ARM_NEON +# endif +# +# if defined(ZSTD_ARCH_X86_SSE2) +# include <emmintrin.h> +# elif defined(ZSTD_ARCH_ARM_NEON) +# include <arm_neon.h> # endif #endif -#if ZSTD_MEMORY_SANITIZER +/* C-language Attributes are added in C23. */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201710L) && defined(__has_c_attribute) +# define ZSTD_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) +#else +# define ZSTD_HAS_C_ATTRIBUTE(x) 0 +#endif + +/* Only use C++ attributes in C++. Some compilers report support for C++ + * attributes when compiling with C. + */ +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define ZSTD_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define ZSTD_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +/* Define ZSTD_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute. + * - C23: https://en.cppreference.com/w/c/language/attributes/fallthrough + * - CPP17: https://en.cppreference.com/w/cpp/language/attributes/fallthrough + * - Else: __attribute__((__fallthrough__)) + */ +#ifndef ZSTD_FALLTHROUGH +# if ZSTD_HAS_C_ATTRIBUTE(fallthrough) +# define ZSTD_FALLTHROUGH [[fallthrough]] +# elif ZSTD_HAS_CPP_ATTRIBUTE(fallthrough) +# define ZSTD_FALLTHROUGH [[fallthrough]] +# elif __has_attribute(__fallthrough__) +/* Leading semicolon is to satisfy gcc-11 with -pedantic. Without the semicolon + * gcc complains about: a label can only be part of a statement and a declaration is not a statement. + */ +# define ZSTD_FALLTHROUGH ; __attribute__((__fallthrough__)) +# else +# define ZSTD_FALLTHROUGH +# endif +#endif + +/*-************************************************************** +* Alignment check +*****************************************************************/ + +/* this test was initially positioned in mem.h, + * but this file is removed (or replaced) for linux kernel + * so it's now hosted in compiler.h, + * which remains valid for both user & kernel spaces. + */ + +#ifndef ZSTD_ALIGNOF +# if defined(__GNUC__) || defined(_MSC_VER) +/* covers gcc, clang & MSVC */ +/* note : this section must come first, before C11, + * due to a limitation in the kernel source generator */ +# define ZSTD_ALIGNOF(T) __alignof(T) + +# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) +/* Oracle Studio */ +# define ZSTD_ALIGNOF(T) _Alignof(T) + +# elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +/* C11 support */ +# include <stdalign.h> +# define ZSTD_ALIGNOF(T) alignof(T) + +# else +/* No known support for alignof() - imperfect backup */ +# define ZSTD_ALIGNOF(T) (sizeof(void*) < sizeof(T) ? sizeof(void*) : sizeof(T)) + +# endif +#endif /* ZSTD_ALIGNOF */ + +/*-************************************************************** +* Sanitizer +*****************************************************************/ + +/* Issue #3240 reports an ASAN failure on an llvm-mingw build. Out of an + * abundance of caution, disable our custom poisoning on mingw. */ +#ifdef __MINGW32__ +#ifndef ZSTD_ASAN_DONT_POISON_WORKSPACE +#define ZSTD_ASAN_DONT_POISON_WORKSPACE 1 +#endif +#ifndef ZSTD_MSAN_DONT_POISON_WORKSPACE +#define ZSTD_MSAN_DONT_POISON_WORKSPACE 1 +#endif +#endif + +#if ZSTD_MEMORY_SANITIZER && !defined(ZSTD_MSAN_DONT_POISON_WORKSPACE) /* Not all platforms that support msan provide sanitizers/msan_interface.h. * We therefore declare the functions we need ourselves, rather than trying to * include the header file... */ @@ -235,20 +315,13 @@ /* Returns the offset of the first (at least partially) poisoned byte in the memory range, or -1 if the whole range is good. */ intptr_t __msan_test_shadow(const volatile void *x, size_t size); + +/* Print shadow and origin for the memory range to stderr in a human-readable + format. */ +void __msan_print_shadow(const volatile void *x, size_t size); #endif -/* detects whether we are being compiled under asan */ -#ifndef ZSTD_ADDRESS_SANITIZER -# if __has_feature(address_sanitizer) -# define ZSTD_ADDRESS_SANITIZER 1 -# elif defined(__SANITIZE_ADDRESS__) -# define ZSTD_ADDRESS_SANITIZER 1 -# else -# define ZSTD_ADDRESS_SANITIZER 0 -# endif -#endif - -#if ZSTD_ADDRESS_SANITIZER +#if ZSTD_ADDRESS_SANITIZER && !defined(ZSTD_ASAN_DONT_POISON_WORKSPACE) /* Not all platforms that support asan provide sanitizers/asan_interface.h. * We therefore declare the functions we need ourselves, rather than trying to * include the header file... */
diff --git a/Utilities/cmzstd/lib/common/cpu.h b/Utilities/cmzstd/lib/common/cpu.h index 8acd33b..8bc34a3 100644 --- a/Utilities/cmzstd/lib/common/cpu.h +++ b/Utilities/cmzstd/lib/common/cpu.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/common/debug.c b/Utilities/cmzstd/lib/common/debug.c index bb863c9..ebf7bfc 100644 --- a/Utilities/cmzstd/lib/common/debug.c +++ b/Utilities/cmzstd/lib/common/debug.c
@@ -1,7 +1,7 @@ /* ****************************************************************** * debug * Part of FSE library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/common/debug.h b/Utilities/cmzstd/lib/common/debug.h index 3b2a320..0e9817e 100644 --- a/Utilities/cmzstd/lib/common/debug.h +++ b/Utilities/cmzstd/lib/common/debug.h
@@ -1,7 +1,7 @@ /* ****************************************************************** * debug * Part of FSE library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/common/entropy_common.c b/Utilities/cmzstd/lib/common/entropy_common.c index 41cd695..e2173af 100644 --- a/Utilities/cmzstd/lib/common/entropy_common.c +++ b/Utilities/cmzstd/lib/common/entropy_common.c
@@ -1,6 +1,6 @@ /* ****************************************************************** * Common functions of New Generation Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -19,8 +19,8 @@ #include "error_private.h" /* ERR_*, ERROR */ #define FSE_STATIC_LINKING_ONLY /* FSE_MIN_TABLELOG */ #include "fse.h" -#define HUF_STATIC_LINKING_ONLY /* HUF_TABLELOG_ABSOLUTEMAX */ #include "huf.h" +#include "bits.h" /* ZSDT_highbit32, ZSTD_countTrailingZeros32 */ /*=== Version ===*/ @@ -38,28 +38,6 @@ /*-************************************************************** * FSE NCount encoding-decoding ****************************************************************/ -static U32 FSE_ctz(U32 val) -{ - assert(val != 0); - { -# if defined(_MSC_VER) /* Visual */ - unsigned long r=0; - return _BitScanForward(&r, val) ? (unsigned)r : 0; -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ - return __builtin_ctz(val); -# elif defined(__ICCARM__) /* IAR Intrinsic */ - return __CTZ(val); -# else /* Software version */ - U32 count = 0; - while ((val & 1) == 0) { - val >>= 1; - ++count; - } - return count; -# endif - } -} - FORCE_INLINE_TEMPLATE size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr, const void* headerBuffer, size_t hbSize) @@ -107,7 +85,7 @@ * repeat. * Avoid UB by setting the high bit to 1. */ - int repeats = FSE_ctz(~bitStream | 0x80000000) >> 1; + int repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1; while (repeats >= 12) { charnum += 3 * 12; if (LIKELY(ip <= iend-7)) { @@ -118,7 +96,7 @@ ip = iend - 4; } bitStream = MEM_readLE32(ip) >> bitCount; - repeats = FSE_ctz(~bitStream | 0x80000000) >> 1; + repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1; } charnum += 3 * repeats; bitStream >>= 2 * repeats; @@ -183,7 +161,7 @@ * know that threshold > 1. */ if (remaining <= 1) break; - nbBits = BIT_highbit32(remaining) + 1; + nbBits = ZSTD_highbit32(remaining) + 1; threshold = 1 << (nbBits - 1); } if (charnum >= maxSV1) break; @@ -217,7 +195,7 @@ } #if DYNAMIC_BMI2 -TARGET_ATTRIBUTE("bmi2") static size_t FSE_readNCount_body_bmi2( +BMI2_TARGET_ATTRIBUTE static size_t FSE_readNCount_body_bmi2( short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr, const void* headerBuffer, size_t hbSize) { @@ -258,7 +236,7 @@ const void* src, size_t srcSize) { U32 wksp[HUF_READ_STATS_WORKSPACE_SIZE_U32]; - return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* bmi2 */ 0); + return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* flags */ 0); } FORCE_INLINE_TEMPLATE size_t @@ -299,21 +277,21 @@ ZSTD_memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32)); weightTotal = 0; { U32 n; for (n=0; n<oSize; n++) { - if (huffWeight[n] >= HUF_TABLELOG_MAX) return ERROR(corruption_detected); + if (huffWeight[n] > HUF_TABLELOG_MAX) return ERROR(corruption_detected); rankStats[huffWeight[n]]++; weightTotal += (1 << huffWeight[n]) >> 1; } } if (weightTotal == 0) return ERROR(corruption_detected); /* get last non-null symbol weight (implied, total must be 2^n) */ - { U32 const tableLog = BIT_highbit32(weightTotal) + 1; + { U32 const tableLog = ZSTD_highbit32(weightTotal) + 1; if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected); *tableLogPtr = tableLog; /* determine last weight */ { U32 const total = 1 << tableLog; U32 const rest = total - weightTotal; - U32 const verif = 1 << BIT_highbit32(rest); - U32 const lastWeight = BIT_highbit32(rest) + 1; + U32 const verif = 1 << ZSTD_highbit32(rest); + U32 const lastWeight = ZSTD_highbit32(rest) + 1; if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */ huffWeight[oSize] = (BYTE)lastWeight; rankStats[lastWeight]++; @@ -337,7 +315,7 @@ } #if DYNAMIC_BMI2 -static TARGET_ATTRIBUTE("bmi2") size_t HUF_readStats_body_bmi2(BYTE* huffWeight, size_t hwSize, U32* rankStats, +static BMI2_TARGET_ATTRIBUTE size_t HUF_readStats_body_bmi2(BYTE* huffWeight, size_t hwSize, U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) @@ -350,13 +328,13 @@ U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, - int bmi2) + int flags) { #if DYNAMIC_BMI2 - if (bmi2) { + if (flags & HUF_flags_bmi2) { return HUF_readStats_body_bmi2(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize); } #endif - (void)bmi2; + (void)flags; return HUF_readStats_body_default(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize); }
diff --git a/Utilities/cmzstd/lib/common/error_private.c b/Utilities/cmzstd/lib/common/error_private.c index 6d1135f..075fc5e 100644 --- a/Utilities/cmzstd/lib/common/error_private.c +++ b/Utilities/cmzstd/lib/common/error_private.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -27,9 +27,11 @@ case PREFIX(version_unsupported): return "Version not supported"; case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter"; case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding"; - case PREFIX(corruption_detected): return "Corrupted block detected"; + case PREFIX(corruption_detected): return "Data corruption detected"; case PREFIX(checksum_wrong): return "Restored data doesn't match checksum"; + case PREFIX(literals_headerWrong): return "Header of Literals' block doesn't respect format specification"; case PREFIX(parameter_unsupported): return "Unsupported parameter"; + case PREFIX(parameter_combination_unsupported): return "Unsupported combination of parameters"; case PREFIX(parameter_outOfBound): return "Parameter is out of bound"; case PREFIX(init_missing): return "Context should be init first"; case PREFIX(memory_allocation): return "Allocation error : not enough memory"; @@ -38,17 +40,22 @@ case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported"; case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large"; case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small"; + case PREFIX(stabilityCondition_notRespected): return "pledged buffer stability condition is not respected"; case PREFIX(dictionary_corrupted): return "Dictionary is corrupted"; case PREFIX(dictionary_wrong): return "Dictionary mismatch"; case PREFIX(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples"; case PREFIX(dstSize_tooSmall): return "Destination buffer is too small"; case PREFIX(srcSize_wrong): return "Src size is incorrect"; case PREFIX(dstBuffer_null): return "Operation on NULL destination buffer"; + case PREFIX(noForwardProgress_destFull): return "Operation made no progress over multiple calls, due to output buffer being full"; + case PREFIX(noForwardProgress_inputEmpty): return "Operation made no progress over multiple calls, due to input being empty"; /* following error codes are not stable and may be removed or changed in a future version */ case PREFIX(frameIndex_tooLarge): return "Frame index is too large"; case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking"; case PREFIX(dstBuffer_wrong): return "Destination buffer is wrong"; case PREFIX(srcBuffer_wrong): return "Source buffer is wrong"; + case PREFIX(sequenceProducer_failed): return "Block-level external sequence producer returned an error code"; + case PREFIX(externalSequences_invalid): return "External sequences are not valid"; case PREFIX(maxCode): default: return notErrorCode; }
diff --git a/Utilities/cmzstd/lib/common/error_private.h b/Utilities/cmzstd/lib/common/error_private.h index 6d8b9f7..325daad 100644 --- a/Utilities/cmzstd/lib/common/error_private.h +++ b/Utilities/cmzstd/lib/common/error_private.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -22,6 +22,8 @@ * Dependencies ******************************************/ #include "../zstd_errors.h" /* enum list */ +#include "compiler.h" +#include "debug.h" #include "zstd_deps.h" /* size_t */ @@ -73,6 +75,83 @@ return ERR_getErrorString(ERR_getErrorCode(code)); } +/** + * Ignore: this is an internal helper. + * + * This is a helper function to help force C99-correctness during compilation. + * Under strict compilation modes, variadic macro arguments can't be empty. + * However, variadic function arguments can be. Using a function therefore lets + * us statically check that at least one (string) argument was passed, + * independent of the compilation flags. + */ +static INLINE_KEYWORD UNUSED_ATTR +void _force_has_format_string(const char *format, ...) { + (void)format; +} + +/** + * Ignore: this is an internal helper. + * + * We want to force this function invocation to be syntactically correct, but + * we don't want to force runtime evaluation of its arguments. + */ +#define _FORCE_HAS_FORMAT_STRING(...) \ + if (0) { \ + _force_has_format_string(__VA_ARGS__); \ + } + +#define ERR_QUOTE(str) #str + +/** + * Return the specified error if the condition evaluates to true. + * + * In debug modes, prints additional information. + * In order to do that (particularly, printing the conditional that failed), + * this can't just wrap RETURN_ERROR(). + */ +#define RETURN_ERROR_IF(cond, err, ...) \ + if (cond) { \ + RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \ + __FILE__, __LINE__, ERR_QUOTE(cond), ERR_QUOTE(ERROR(err))); \ + _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ + RAWLOG(3, ": " __VA_ARGS__); \ + RAWLOG(3, "\n"); \ + return ERROR(err); \ + } + +/** + * Unconditionally return the specified error. + * + * In debug modes, prints additional information. + */ +#define RETURN_ERROR(err, ...) \ + do { \ + RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", \ + __FILE__, __LINE__, ERR_QUOTE(ERROR(err))); \ + _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ + RAWLOG(3, ": " __VA_ARGS__); \ + RAWLOG(3, "\n"); \ + return ERROR(err); \ + } while(0); + +/** + * If the provided expression evaluates to an error code, returns that error code. + * + * In debug modes, prints additional information. + */ +#define FORWARD_IF_ERROR(err, ...) \ + do { \ + size_t const err_code = (err); \ + if (ERR_isError(err_code)) { \ + RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", \ + __FILE__, __LINE__, ERR_QUOTE(err), ERR_getErrorName(err_code)); \ + _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ + RAWLOG(3, ": " __VA_ARGS__); \ + RAWLOG(3, "\n"); \ + return err_code; \ + } \ + } while(0); + #if defined (__cplusplus) } #endif
diff --git a/Utilities/cmzstd/lib/common/fse.h b/Utilities/cmzstd/lib/common/fse.h index 19dd4fe..02a1f0b 100644 --- a/Utilities/cmzstd/lib/common/fse.h +++ b/Utilities/cmzstd/lib/common/fse.h
@@ -1,7 +1,7 @@ /* ****************************************************************** * FSE : Finite State Entropy codec * Public Prototypes declaration - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -53,34 +53,6 @@ FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ -/*-**************************************** -* FSE simple functions -******************************************/ -/*! FSE_compress() : - Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'. - 'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize). - @return : size of compressed data (<= dstCapacity). - Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! - if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead. - if FSE_isError(return), compression failed (more details using FSE_getErrorName()) -*/ -FSE_PUBLIC_API size_t FSE_compress(void* dst, size_t dstCapacity, - const void* src, size_t srcSize); - -/*! FSE_decompress(): - Decompress FSE data from buffer 'cSrc', of size 'cSrcSize', - into already allocated destination buffer 'dst', of size 'dstCapacity'. - @return : size of regenerated data (<= maxDstSize), - or an error code, which can be tested using FSE_isError() . - - ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!! - Why ? : making this distinction requires a header. - Header management is intentionally delegated to the user layer, which can better manage special cases. -*/ -FSE_PUBLIC_API size_t FSE_decompress(void* dst, size_t dstCapacity, - const void* cSrc, size_t cSrcSize); - - /*-***************************************** * Tool functions ******************************************/ @@ -92,20 +64,6 @@ /*-***************************************** -* FSE advanced functions -******************************************/ -/*! FSE_compress2() : - Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog' - Both parameters can be defined as '0' to mean : use default value - @return : size of compressed data - Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!! - if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression. - if FSE_isError(return), it's an error code. -*/ -FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); - - -/*-***************************************** * FSE detailed API ******************************************/ /*! @@ -164,8 +122,6 @@ /*! Constructor and Destructor of FSE_CTable. Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ -FSE_PUBLIC_API FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog); -FSE_PUBLIC_API void FSE_freeCTable (FSE_CTable* ct); /*! FSE_buildCTable(): Builds `ct`, which must be already allocated, using FSE_createCTable(). @@ -241,23 +197,7 @@ unsigned* maxSymbolValuePtr, unsigned* tableLogPtr, const void* rBuffer, size_t rBuffSize, int bmi2); -/*! Constructor and Destructor of FSE_DTable. - Note that its size depends on 'tableLog' */ typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ -FSE_PUBLIC_API FSE_DTable* FSE_createDTable(unsigned tableLog); -FSE_PUBLIC_API void FSE_freeDTable(FSE_DTable* dt); - -/*! FSE_buildDTable(): - Builds 'dt', which must be already allocated, using FSE_createDTable(). - return : 0, or an errorCode, which can be tested using FSE_isError() */ -FSE_PUBLIC_API size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); - -/*! FSE_decompress_usingDTable(): - Decompress compressed source `cSrc` of size `cSrcSize` using `dt` - into `dst` which must be already allocated. - @return : size of regenerated data (necessarily <= `dstCapacity`), - or an errorCode, which can be tested using FSE_isError() */ -FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt); /*! Tutorial : @@ -320,24 +260,15 @@ unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); /**< same as FSE_optimalTableLog(), which used `minus==2` */ -/* FSE_compress_wksp() : - * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`). - * FSE_COMPRESS_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable. - */ -#define FSE_COMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) ) -size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); - -size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits); -/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ - size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue); /**< build a fake FSE_CTable, designed to compress always the same symbolValue */ /* FSE_buildCTable_wksp() : * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). * `wkspSize` must be >= `FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog)` of `unsigned`. + * See FSE_buildCTable_wksp() for breakdown of workspace usage. */ -#define FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog) (maxSymbolValue + 2 + (1ull << (tableLog - 2))) +#define FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog) (((maxSymbolValue + 2) + (1ull << (tableLog)))/2 + sizeof(U64)/sizeof(U32) /* additional 8 bytes for potential table overwrite */) #define FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog) (sizeof(unsigned) * FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog)) size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); @@ -346,19 +277,11 @@ FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< Same as FSE_buildDTable(), using an externally allocated `workspace` produced with `FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxSymbolValue)` */ -size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits); -/**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */ - -size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue); -/**< build a fake FSE_DTable, designed to always generate the same symbolValue */ - -#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1) +#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + 1 + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1) #define FSE_DECOMPRESS_WKSP_SIZE(maxTableLog, maxSymbolValue) (FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(unsigned)) -size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize); -/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)` */ - size_t FSE_decompress_wksp_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize, int bmi2); -/**< Same as FSE_decompress_wksp() but with dynamic BMI2 support. Pass 1 if your CPU supports BMI2 or 0 if it doesn't. */ +/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)`. + * Set bmi2 to 1 if your CPU supports BMI2 or 0 if it doesn't */ typedef enum { FSE_repeat_none, /**< Cannot use the previous table */ @@ -554,7 +477,7 @@ /* FSE_getMaxNbBits() : * Approximate maximum cost of a symbol, in bits. - * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) + * Fractional get rounded up (i.e. a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) * note 1 : assume symbolValue is valid (<= maxSymbolValue) * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue)
diff --git a/Utilities/cmzstd/lib/common/fse_decompress.c b/Utilities/cmzstd/lib/common/fse_decompress.c index f4ff58f..1e1c9f9 100644 --- a/Utilities/cmzstd/lib/common/fse_decompress.c +++ b/Utilities/cmzstd/lib/common/fse_decompress.c
@@ -1,6 +1,6 @@ /* ****************************************************************** * FSE : Finite State Entropy decoder - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -24,6 +24,7 @@ #include "error_private.h" #define ZSTD_DEPS_NEED_MALLOC #include "zstd_deps.h" +#include "bits.h" /* ZSTD_highbit32 */ /* ************************************************************** @@ -55,19 +56,6 @@ #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y) #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y) - -/* Function templates */ -FSE_DTable* FSE_createDTable (unsigned tableLog) -{ - if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; - return (FSE_DTable*)ZSTD_malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) ); -} - -void FSE_freeDTable (FSE_DTable* dt) -{ - ZSTD_free(dt); -} - static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize) { void* const tdPtr = dt+1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ @@ -127,10 +115,10 @@ } } /* Now we spread those positions across the table. - * The benefit of doing it in two stages is that we avoid the the + * The benefit of doing it in two stages is that we avoid the * variable size inner loop, which caused lots of branch misses. * Now we can run through all the positions without any branch misses. - * We unroll the loop twice, since that is what emperically worked best. + * We unroll the loop twice, since that is what empirically worked best. */ { size_t position = 0; @@ -166,7 +154,7 @@ for (u=0; u<tableSize; u++) { FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol); U32 const nextState = symbolNext[symbol]++; - tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) ); + tableDecode[u].nbBits = (BYTE) (tableLog - ZSTD_highbit32(nextState) ); tableDecode[u].newState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize); } } @@ -184,49 +172,6 @@ /*-******************************************************* * Decompression (Byte symbols) *********************************************************/ -size_t FSE_buildDTable_rle (FSE_DTable* dt, BYTE symbolValue) -{ - void* ptr = dt; - FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; - void* dPtr = dt + 1; - FSE_decode_t* const cell = (FSE_decode_t*)dPtr; - - DTableH->tableLog = 0; - DTableH->fastMode = 0; - - cell->newState = 0; - cell->symbol = symbolValue; - cell->nbBits = 0; - - return 0; -} - - -size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits) -{ - void* ptr = dt; - FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr; - void* dPtr = dt + 1; - FSE_decode_t* const dinfo = (FSE_decode_t*)dPtr; - const unsigned tableSize = 1 << nbBits; - const unsigned tableMask = tableSize - 1; - const unsigned maxSV1 = tableMask+1; - unsigned s; - - /* Sanity checks */ - if (nbBits < 1) return ERROR(GENERIC); /* min size */ - - /* Build Decoding Table */ - DTableH->tableLog = (U16)nbBits; - DTableH->fastMode = 1; - for (s=0; s<maxSV1; s++) { - dinfo[s].newState = 0; - dinfo[s].symbol = (BYTE)s; - dinfo[s].nbBits = (BYTE)nbBits; - } - - return 0; -} FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic( void* dst, size_t maxDstSize, @@ -290,26 +235,6 @@ return op-ostart; } - -size_t FSE_decompress_usingDTable(void* dst, size_t originalSize, - const void* cSrc, size_t cSrcSize, - const FSE_DTable* dt) -{ - const void* ptr = dt; - const FSE_DTableHeader* DTableH = (const FSE_DTableHeader*)ptr; - const U32 fastMode = DTableH->fastMode; - - /* select fast mode (static) */ - if (fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); - return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); -} - - -size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize) -{ - return FSE_decompress_wksp_bmi2(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, /* bmi2 */ 0); -} - typedef struct { short ncount[FSE_MAX_SYMBOL_VALUE + 1]; FSE_DTable dtable[1]; /* Dynamically sized */ @@ -342,7 +267,8 @@ } if (FSE_DECOMPRESS_WKSP_SIZE(tableLog, maxSymbolValue) > wkspSize) return ERROR(tableLog_tooLarge); - workSpace = wksp->dtable + FSE_DTABLE_SIZE_U32(tableLog); + assert(sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog) <= wkspSize); + workSpace = (BYTE*)workSpace + sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog); wkspSize -= sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog); CHECK_F( FSE_buildDTable_internal(wksp->dtable, wksp->ncount, maxSymbolValue, tableLog, workSpace, wkspSize) ); @@ -365,7 +291,7 @@ } #if DYNAMIC_BMI2 -TARGET_ATTRIBUTE("bmi2") static size_t FSE_decompress_wksp_body_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize) +BMI2_TARGET_ATTRIBUTE static size_t FSE_decompress_wksp_body_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize) { return FSE_decompress_wksp_body(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, 1); } @@ -382,22 +308,4 @@ return FSE_decompress_wksp_body_default(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize); } - -typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; - -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) { - U32 wksp[FSE_BUILD_DTABLE_WKSP_SIZE_U32(FSE_TABLELOG_ABSOLUTE_MAX, FSE_MAX_SYMBOL_VALUE)]; - return FSE_buildDTable_wksp(dt, normalizedCounter, maxSymbolValue, tableLog, wksp, sizeof(wksp)); -} - -size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize) -{ - /* Static analyzer seems unable to understand this table will be properly initialized later */ - U32 wksp[FSE_DECOMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; - return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, FSE_MAX_TABLELOG, wksp, sizeof(wksp)); -} -#endif - - #endif /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/common/huf.h b/Utilities/cmzstd/lib/common/huf.h index 3d47ced..73d1ee5 100644 --- a/Utilities/cmzstd/lib/common/huf.h +++ b/Utilities/cmzstd/lib/common/huf.h
@@ -1,7 +1,7 @@ /* ****************************************************************** * huff0 huffman codec, * part of Finite State Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -21,106 +21,29 @@ /* *** Dependencies *** */ #include "zstd_deps.h" /* size_t */ - - -/* *** library symbols visibility *** */ -/* Note : when linking with -fvisibility=hidden on gcc, or by default on Visual, - * HUF symbols remain "private" (internal symbols for library only). - * Set macro FSE_DLL_EXPORT to 1 if you want HUF symbols visible on DLL interface */ -#if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4) -# define HUF_PUBLIC_API __attribute__ ((visibility ("default"))) -#elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) /* Visual expected */ -# define HUF_PUBLIC_API __declspec(dllexport) -#elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1) -# define HUF_PUBLIC_API __declspec(dllimport) /* not required, just to generate faster code (saves a function pointer load from IAT and an indirect jump) */ -#else -# define HUF_PUBLIC_API -#endif - - -/* ========================== */ -/* *** simple functions *** */ -/* ========================== */ - -/** HUF_compress() : - * Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'. - * 'dst' buffer must be already allocated. - * Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize). - * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB. - * @return : size of compressed data (<= `dstCapacity`). - * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!! - * if HUF_isError(return), compression failed (more details using HUF_getErrorName()) - */ -HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity, - const void* src, size_t srcSize); - -/** HUF_decompress() : - * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize', - * into already allocated buffer 'dst', of minimum size 'dstSize'. - * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data. - * Note : in contrast with FSE, HUF_decompress can regenerate - * RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data, - * because it knows size to regenerate (originalSize). - * @return : size of regenerated data (== originalSize), - * or an error code, which can be tested using HUF_isError() - */ -HUF_PUBLIC_API size_t HUF_decompress(void* dst, size_t originalSize, - const void* cSrc, size_t cSrcSize); - - -/* *** Tool functions *** */ -#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ -HUF_PUBLIC_API size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ - -/* Error Management */ -HUF_PUBLIC_API unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ -HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ - - -/* *** Advanced function *** */ - -/** HUF_compress2() : - * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`. - * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX . - * `tableLog` must be `<= HUF_TABLELOG_MAX` . */ -HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned tableLog); - -/** HUF_compress4X_wksp() : - * Same as HUF_compress2(), but uses externally allocated `workSpace`. - * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */ -#define HUF_WORKSPACE_SIZE ((6 << 10) + 256) -#define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32)) -HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned tableLog, - void* workSpace, size_t wkspSize); - -#endif /* HUF_H_298734234 */ - -/* ****************************************************************** - * WARNING !! - * The following section contains advanced and experimental definitions - * which shall never be used in the context of a dynamic library, - * because they are not guaranteed to remain stable in the future. - * Only consider them in association with static linking. - * *****************************************************************/ -#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY) -#define HUF_H_HUF_STATIC_LINKING_ONLY - -/* *** Dependencies *** */ -#include "mem.h" /* U32 */ +#include "mem.h" /* U32 */ #define FSE_STATIC_LINKING_ONLY #include "fse.h" +/* *** Tool functions *** */ +#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ +size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ + +/* Error Management */ +unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ +const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ + + +#define HUF_WORKSPACE_SIZE ((8 << 10) + 512 /* sorting scratch space */) +#define HUF_WORKSPACE_SIZE_U64 (HUF_WORKSPACE_SIZE / sizeof(U64)) + /* *** Constants *** */ -#define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ +#define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_TABLELOG_ABSOLUTEMAX */ #define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */ #define HUF_SYMBOLVALUE_MAX 255 -#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ +#define HUF_TABLELOG_ABSOLUTEMAX 12 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) # error "HUF_TABLELOG_MAX is too large !" #endif @@ -136,15 +59,11 @@ /* static allocation of HUF's Compression Table */ /* this is a private definition, just exposed for allocation and strict aliasing purpose. never EVER access its members directly */ -struct HUF_CElt_s { - U16 val; - BYTE nbBits; -}; /* typedef'd to HUF_CElt */ -typedef struct HUF_CElt_s HUF_CElt; /* consider it an incomplete type */ -#define HUF_CTABLE_SIZE_U32(maxSymbolValue) ((maxSymbolValue)+1) /* Use tables of U32, for proper alignment */ -#define HUF_CTABLE_SIZE(maxSymbolValue) (HUF_CTABLE_SIZE_U32(maxSymbolValue) * sizeof(U32)) +typedef size_t HUF_CElt; /* consider it an incomplete type */ +#define HUF_CTABLE_SIZE_ST(maxSymbolValue) ((maxSymbolValue)+2) /* Use tables of size_t, for proper alignment */ +#define HUF_CTABLE_SIZE(maxSymbolValue) (HUF_CTABLE_SIZE_ST(maxSymbolValue) * sizeof(size_t)) #define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ - HUF_CElt name[HUF_CTABLE_SIZE_U32(maxSymbolValue)] /* no final ; */ + HUF_CElt name[HUF_CTABLE_SIZE_ST(maxSymbolValue)] /* no final ; */ /* static allocation of HUF's DTable */ typedef U32 HUF_DTable; @@ -158,25 +77,49 @@ /* **************************************** * Advanced decompression functions ******************************************/ -size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -#endif -size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */ -size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */ -size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */ -size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ -#endif +/** + * Huffman flags bitset. + * For all flags, 0 is the default value. + */ +typedef enum { + /** + * If compiled with DYNAMIC_BMI2: Set flag only if the CPU supports BMI2 at runtime. + * Otherwise: Ignored. + */ + HUF_flags_bmi2 = (1 << 0), + /** + * If set: Test possible table depths to find the one that produces the smallest header + encoded size. + * If unset: Use heuristic to find the table depth. + */ + HUF_flags_optimalDepth = (1 << 1), + /** + * If set: If the previous table can encode the input, always reuse the previous table. + * If unset: If the previous table can encode the input, reuse the previous table if it results in a smaller output. + */ + HUF_flags_preferRepeat = (1 << 2), + /** + * If set: Sample the input and check if the sample is uncompressible, if it is then don't attempt to compress. + * If unset: Always histogram the entire input. + */ + HUF_flags_suspectUncompressible = (1 << 3), + /** + * If set: Don't use assembly implementations + * If unset: Allow using assembly implementations + */ + HUF_flags_disableAsm = (1 << 4), + /** + * If set: Don't use the fast decoding loop, always use the fallback decoding loop. + * If unset: Use the fast decoding loop when possible. + */ + HUF_flags_disableFast = (1 << 5) +} HUF_flags_e; /* **************************************** * HUF detailed API * ****************************************/ +#define HUF_OPTIMAL_DEPTH_THRESHOLD ZSTD_btultra /*! HUF_compress() does the following: * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h") @@ -189,11 +132,12 @@ * For example, it's possible to compress several blocks using the same 'CTable', * or to save and regenerate 'CTable' using external methods. */ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); -size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */ -size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); +unsigned HUF_minTableLog(unsigned symbolCardinality); +unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue); +unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, void* workSpace, + size_t wkspSize, HUF_CElt* table, const unsigned* count, int flags); /* table is used as scratch space for building and testing tables, not a return value */ size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); -size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); +size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags); size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); @@ -202,22 +146,24 @@ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */ } HUF_repeat; + /** HUF_compress4X_repeat() : * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. - * If preferRepeat then the old table will always be used if valid. */ + * If preferRepeat then the old table will always be used if valid. + * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); + HUF_CElt* hufTable, HUF_repeat* repeat, int flags); /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE. */ -#define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1) +#define HUF_CTABLE_WORKSPACE_SIZE_U32 ((4 * (HUF_SYMBOLVALUE_MAX + 1)) + 192) #define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned)) size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, @@ -243,17 +189,16 @@ U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, const void* src, size_t srcSize, void* workspace, size_t wkspSize, - int bmi2); + int flags); /** HUF_readCTable() : * Loading a CTable saved with HUF_writeCTable() */ size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights); -/** HUF_getNbBits() : +/** HUF_getNbBitsFromCTable() : * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX - * Note 1 : is not inlined, as HUF_CElt definition is private - * Note 2 : const void* used, so that it can provide a statically allocated table as argument (which uses type U32) */ -U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue); + * Note 1 : is not inlined, as HUF_CElt definition is private */ +U32 HUF_getNbBitsFromCTable(const HUF_CElt* symbolTable, U32 symbolValue); /* * HUF_decompress() does the following: @@ -282,80 +227,46 @@ #define HUF_DECOMPRESS_WORKSPACE_SIZE ((2 << 10) + (1 << 9)) #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize); -size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize); -size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize); -#endif - -size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#endif - /* ====================== */ /* single stream variants */ /* ====================== */ -size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ -size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); +size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags); /** HUF_compress1X_repeat() : * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. - * If preferRepeat then the old table will always be used if valid. */ + * If preferRepeat then the old table will always be used if valid. + * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2); + HUF_CElt* hufTable, HUF_repeat* repeat, int flags); -size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ +size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); #ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ -#endif - -size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); -size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */ -size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */ -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */ -size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */ -#endif - -size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); -#endif -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); +size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); /**< double-symbols decoder */ #endif /* BMI2 variants. * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. */ -size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); +size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags); #ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); #endif -size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2); -size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags); +size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); #ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2); +size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags); +#endif +#ifndef HUF_FORCE_DECOMPRESS_X1 +size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags); #endif -#endif /* HUF_STATIC_LINKING_ONLY */ +#endif /* HUF_H_298734234 */ #if defined (__cplusplus) }
diff --git a/Utilities/cmzstd/lib/common/mem.h b/Utilities/cmzstd/lib/common/mem.h index 9f3b81a..98dd47a 100644 --- a/Utilities/cmzstd/lib/common/mem.h +++ b/Utilities/cmzstd/lib/common/mem.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -51,6 +51,8 @@ # include <stdint.h> /* intptr_t */ # endif typedef uint8_t BYTE; + typedef uint8_t U8; + typedef int8_t S8; typedef uint16_t U16; typedef int16_t S16; typedef uint32_t U32; @@ -63,6 +65,8 @@ # error "this implementation requires char to be exactly 8-bit type" #endif typedef unsigned char BYTE; + typedef unsigned char U8; + typedef signed char S8; #if USHRT_MAX != 65535 # error "this implementation requires short to be exactly 16-bit type" #endif @@ -129,21 +133,15 @@ /*-************************************************************** * Memory I/O Implementation *****************************************************************/ -/* MEM_FORCE_MEMORY_ACCESS : - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (i.e., not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. +/* MEM_FORCE_MEMORY_ACCESS : For accessing unaligned memory: + * Method 0 : always use `memcpy()`. Safe and portable. + * Method 1 : Use compiler extension to set unaligned access. * Method 2 : direct access. This method is portable but violate C standard. * It can generate buggy code on targets depending on alignment. - * In some circumstances, it's the only known way to get the most performance (i.e. GCC + ARMv6) - * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) + * Default : method 1 if supported, else method 0 */ #ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__) +# ifdef __GNUC__ # define MEM_FORCE_MEMORY_ACCESS 1 # endif #endif @@ -153,8 +151,22 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) { +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return 1; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + return 0; +#elif defined(__clang__) && __LITTLE_ENDIAN__ + return 1; +#elif defined(__clang__) && __BIG_ENDIAN__ + return 0; +#elif defined(_MSC_VER) && (_M_AMD64 || _M_IX86) + return 1; +#elif defined(__DMC__) && defined(_M_IX86) + return 1; +#else const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; +#endif } #if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) @@ -172,30 +184,19 @@ #elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1) -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -#if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32)) - __pragma( pack(push, 1) ) - typedef struct { U16 v; } unalign16; - typedef struct { U32 v; } unalign32; - typedef struct { U64 v; } unalign64; - typedef struct { size_t v; } unalignArch; - __pragma( pack(pop) ) -#else - typedef struct { U16 v; } __attribute__((packed)) unalign16; - typedef struct { U32 v; } __attribute__((packed)) unalign32; - typedef struct { U64 v; } __attribute__((packed)) unalign64; - typedef struct { size_t v; } __attribute__((packed)) unalignArch; -#endif +typedef __attribute__((aligned(1))) U16 unalign16; +typedef __attribute__((aligned(1))) U32 unalign32; +typedef __attribute__((aligned(1))) U64 unalign64; +typedef __attribute__((aligned(1))) size_t unalignArch; -MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign16*)ptr)->v; } -MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign32*)ptr)->v; } -MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign64*)ptr)->v; } -MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalignArch*)ptr)->v; } +MEM_STATIC U16 MEM_read16(const void* ptr) { return *(const unalign16*)ptr; } +MEM_STATIC U32 MEM_read32(const void* ptr) { return *(const unalign32*)ptr; } +MEM_STATIC U64 MEM_read64(const void* ptr) { return *(const unalign64*)ptr; } +MEM_STATIC size_t MEM_readST(const void* ptr) { return *(const unalignArch*)ptr; } -MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign16*)memPtr)->v = value; } -MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign32*)memPtr)->v = value; } -MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign64*)memPtr)->v = value; } +MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(unalign16*)memPtr = value; } +MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(unalign32*)memPtr = value; } +MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(unalign64*)memPtr = value; } #else @@ -239,6 +240,14 @@ #endif /* MEM_FORCE_MEMORY_ACCESS */ +MEM_STATIC U32 MEM_swap32_fallback(U32 in) +{ + return ((in << 24) & 0xff000000 ) | + ((in << 8) & 0x00ff0000 ) | + ((in >> 8) & 0x0000ff00 ) | + ((in >> 24) & 0x000000ff ); +} + MEM_STATIC U32 MEM_swap32(U32 in) { #if defined(_MSC_VER) /* Visual Studio */ @@ -247,13 +256,22 @@ || (defined(__clang__) && __has_builtin(__builtin_bswap32)) return __builtin_bswap32(in); #else - return ((in << 24) & 0xff000000 ) | - ((in << 8) & 0x00ff0000 ) | - ((in >> 8) & 0x0000ff00 ) | - ((in >> 24) & 0x000000ff ); + return MEM_swap32_fallback(in); #endif } +MEM_STATIC U64 MEM_swap64_fallback(U64 in) +{ + return ((in << 56) & 0xff00000000000000ULL) | + ((in << 40) & 0x00ff000000000000ULL) | + ((in << 24) & 0x0000ff0000000000ULL) | + ((in << 8) & 0x000000ff00000000ULL) | + ((in >> 8) & 0x00000000ff000000ULL) | + ((in >> 24) & 0x0000000000ff0000ULL) | + ((in >> 40) & 0x000000000000ff00ULL) | + ((in >> 56) & 0x00000000000000ffULL); +} + MEM_STATIC U64 MEM_swap64(U64 in) { #if defined(_MSC_VER) /* Visual Studio */ @@ -262,14 +280,7 @@ || (defined(__clang__) && __has_builtin(__builtin_bswap64)) return __builtin_bswap64(in); #else - return ((in << 56) & 0xff00000000000000ULL) | - ((in << 40) & 0x00ff000000000000ULL) | - ((in << 24) & 0x0000ff0000000000ULL) | - ((in << 8) & 0x000000ff00000000ULL) | - ((in >> 8) & 0x00000000ff000000ULL) | - ((in >> 24) & 0x0000000000ff0000ULL) | - ((in >> 40) & 0x000000000000ff00ULL) | - ((in >> 56) & 0x00000000000000ffULL); + return MEM_swap64_fallback(in); #endif }
diff --git a/Utilities/cmzstd/lib/common/pool.c b/Utilities/cmzstd/lib/common/pool.c index ea70b8b..d5ca5a7 100644 --- a/Utilities/cmzstd/lib/common/pool.c +++ b/Utilities/cmzstd/lib/common/pool.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -10,9 +10,9 @@ /* ====== Dependencies ======= */ +#include "../common/allocations.h" /* ZSTD_customCalloc, ZSTD_customFree */ #include "zstd_deps.h" /* size_t */ #include "debug.h" /* assert */ -#include "zstd_internal.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "pool.h" /* ====== Compiler specifics ====== */ @@ -86,7 +86,7 @@ { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; ctx->numThreadsBusy++; - ctx->queueEmpty = ctx->queueHead == ctx->queueTail; + ctx->queueEmpty = (ctx->queueHead == ctx->queueTail); /* Unlock the mutex, signal a pusher, and run the job */ ZSTD_pthread_cond_signal(&ctx->queuePushCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); @@ -96,15 +96,14 @@ /* If the intended queue size was 0, signal after finishing job */ ZSTD_pthread_mutex_lock(&ctx->queueMutex); ctx->numThreadsBusy--; - if (ctx->queueSize == 1) { - ZSTD_pthread_cond_signal(&ctx->queuePushCond); - } + ZSTD_pthread_cond_signal(&ctx->queuePushCond); ZSTD_pthread_mutex_unlock(&ctx->queueMutex); } } /* for (;;) */ assert(0); /* Unreachable */ } +/* ZSTD_createThreadPool() : public access point */ POOL_ctx* ZSTD_createThreadPool(size_t numThreads) { return POOL_create (numThreads, 0); } @@ -114,7 +113,8 @@ } POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, - ZSTD_customMem customMem) { + ZSTD_customMem customMem) +{ POOL_ctx* ctx; /* Check parameters */ if (!numThreads) { return NULL; } @@ -126,7 +126,7 @@ * empty and full queues. */ ctx->queueSize = queueSize + 1; - ctx->queue = (POOL_job*)ZSTD_customMalloc(ctx->queueSize * sizeof(POOL_job), customMem); + ctx->queue = (POOL_job*)ZSTD_customCalloc(ctx->queueSize * sizeof(POOL_job), customMem); ctx->queueHead = 0; ctx->queueTail = 0; ctx->numThreadsBusy = 0; @@ -140,7 +140,7 @@ } ctx->shutdown = 0; /* Allocate space for the thread handles */ - ctx->threads = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), customMem); + ctx->threads = (ZSTD_pthread_t*)ZSTD_customCalloc(numThreads * sizeof(ZSTD_pthread_t), customMem); ctx->threadCapacity = 0; ctx->customMem = customMem; /* Check for errors */ @@ -173,7 +173,7 @@ /* Join all of the threads */ { size_t i; for (i = 0; i < ctx->threadCapacity; ++i) { - ZSTD_pthread_join(ctx->threads[i], NULL); /* note : could fail */ + ZSTD_pthread_join(ctx->threads[i]); /* note : could fail */ } } } @@ -188,11 +188,22 @@ ZSTD_customFree(ctx, ctx->customMem); } +/*! POOL_joinJobs() : + * Waits for all queued jobs to finish executing. + */ +void POOL_joinJobs(POOL_ctx* ctx) { + ZSTD_pthread_mutex_lock(&ctx->queueMutex); + while(!ctx->queueEmpty || ctx->numThreadsBusy > 0) { + ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); + } + ZSTD_pthread_mutex_unlock(&ctx->queueMutex); +} + void ZSTD_freeThreadPool (ZSTD_threadPool* pool) { POOL_free (pool); } -size_t POOL_sizeof(POOL_ctx *ctx) { +size_t POOL_sizeof(const POOL_ctx* ctx) { if (ctx==NULL) return 0; /* supports sizeof NULL */ return sizeof(*ctx) + ctx->queueSize * sizeof(POOL_job) @@ -209,7 +220,7 @@ return 0; } /* numThreads > threadCapacity */ - { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); + { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customCalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem); if (!threadPool) return 1; /* replace existing thread pool */ ZSTD_memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool)); @@ -257,9 +268,12 @@ } -static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque) +static void +POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque) { - POOL_job const job = {function, opaque}; + POOL_job job; + job.function = function; + job.opaque = opaque; assert(ctx != NULL); if (ctx->shutdown) return; @@ -313,7 +327,9 @@ return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem); } -POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) { +POOL_ctx* +POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) +{ (void)numThreads; (void)queueSize; (void)customMem; @@ -325,6 +341,11 @@ (void)ctx; } +void POOL_joinJobs(POOL_ctx* ctx){ + assert(!ctx || ctx == &g_poolCtx); + (void)ctx; +} + int POOL_resize(POOL_ctx* ctx, size_t numThreads) { (void)ctx; (void)numThreads; return 0; @@ -341,7 +362,7 @@ return 1; } -size_t POOL_sizeof(POOL_ctx* ctx) { +size_t POOL_sizeof(const POOL_ctx* ctx) { if (ctx==NULL) return 0; /* supports sizeof NULL */ assert(ctx == &g_poolCtx); return sizeof(*ctx);
diff --git a/Utilities/cmzstd/lib/common/pool.h b/Utilities/cmzstd/lib/common/pool.h index e18aa07..eb22ff5 100644 --- a/Utilities/cmzstd/lib/common/pool.h +++ b/Utilities/cmzstd/lib/common/pool.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -38,6 +38,12 @@ */ void POOL_free(POOL_ctx* ctx); + +/*! POOL_joinJobs() : + * Waits for all queued jobs to finish executing. + */ +void POOL_joinJobs(POOL_ctx* ctx); + /*! POOL_resize() : * Expands or shrinks pool's number of threads. * This is more efficient than releasing + creating a new context, @@ -53,7 +59,7 @@ * @return threadpool memory usage * note : compatible with NULL (returns 0 in this case) */ -size_t POOL_sizeof(POOL_ctx* ctx); +size_t POOL_sizeof(const POOL_ctx* ctx); /*! POOL_function : * The function type that can be added to a thread pool. @@ -70,7 +76,7 @@ /*! POOL_tryAdd() : - * Add the job `function(opaque)` to thread pool _if_ a worker is available. + * Add the job `function(opaque)` to thread pool _if_ a queue slot is available. * Returns immediately even if not (does not block). * @return : 1 if successful, 0 if not. */
diff --git a/Utilities/cmzstd/lib/common/portability_macros.h b/Utilities/cmzstd/lib/common/portability_macros.h new file mode 100644 index 0000000..8fd6ea8 --- /dev/null +++ b/Utilities/cmzstd/lib/common/portability_macros.h
@@ -0,0 +1,156 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#ifndef ZSTD_PORTABILITY_MACROS_H +#define ZSTD_PORTABILITY_MACROS_H + +/** + * This header file contains macro definitions to support portability. + * This header is shared between C and ASM code, so it MUST only + * contain macro definitions. It MUST not contain any C code. + * + * This header ONLY defines macros to detect platforms/feature support. + * + */ + + +/* compat. with non-clang compilers */ +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif + +/* compat. with non-clang compilers */ +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +/* compat. with non-clang compilers */ +#ifndef __has_feature +# define __has_feature(x) 0 +#endif + +/* detects whether we are being compiled under msan */ +#ifndef ZSTD_MEMORY_SANITIZER +# if __has_feature(memory_sanitizer) +# define ZSTD_MEMORY_SANITIZER 1 +# else +# define ZSTD_MEMORY_SANITIZER 0 +# endif +#endif + +/* detects whether we are being compiled under asan */ +#ifndef ZSTD_ADDRESS_SANITIZER +# if __has_feature(address_sanitizer) +# define ZSTD_ADDRESS_SANITIZER 1 +# elif defined(__SANITIZE_ADDRESS__) +# define ZSTD_ADDRESS_SANITIZER 1 +# else +# define ZSTD_ADDRESS_SANITIZER 0 +# endif +#endif + +/* detects whether we are being compiled under dfsan */ +#ifndef ZSTD_DATAFLOW_SANITIZER +# if __has_feature(dataflow_sanitizer) +# define ZSTD_DATAFLOW_SANITIZER 1 +# else +# define ZSTD_DATAFLOW_SANITIZER 0 +# endif +#endif + +/* Mark the internal assembly functions as hidden */ +#ifdef __ELF__ +# define ZSTD_HIDE_ASM_FUNCTION(func) .hidden func +#else +# define ZSTD_HIDE_ASM_FUNCTION(func) +#endif + +/* Enable runtime BMI2 dispatch based on the CPU. + * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default. + */ +#ifndef DYNAMIC_BMI2 + #if ((defined(__clang__) && __has_attribute(__target__)) \ + || (defined(__GNUC__) \ + && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \ + && (defined(__x86_64__) || defined(_M_X64)) \ + && !defined(__BMI2__) + # define DYNAMIC_BMI2 1 + #else + # define DYNAMIC_BMI2 0 + #endif +#endif + +/** + * Only enable assembly for GNUC compatible compilers, + * because other platforms may not support GAS assembly syntax. + * + * Only enable assembly for Linux / MacOS, other platforms may + * work, but they haven't been tested. This could likely be + * extended to BSD systems. + * + * Disable assembly when MSAN is enabled, because MSAN requires + * 100% of code to be instrumented to work. + */ +#if defined(__GNUC__) +# if defined(__linux__) || defined(__linux) || defined(__APPLE__) +# if ZSTD_MEMORY_SANITIZER +# define ZSTD_ASM_SUPPORTED 0 +# elif ZSTD_DATAFLOW_SANITIZER +# define ZSTD_ASM_SUPPORTED 0 +# else +# define ZSTD_ASM_SUPPORTED 1 +# endif +# else +# define ZSTD_ASM_SUPPORTED 0 +# endif +#else +# define ZSTD_ASM_SUPPORTED 0 +#endif + +/** + * Determines whether we should enable assembly for x86-64 + * with BMI2. + * + * Enable if all of the following conditions hold: + * - ASM hasn't been explicitly disabled by defining ZSTD_DISABLE_ASM + * - Assembly is supported + * - We are compiling for x86-64 and either: + * - DYNAMIC_BMI2 is enabled + * - BMI2 is supported at compile time + */ +#if !defined(ZSTD_DISABLE_ASM) && \ + ZSTD_ASM_SUPPORTED && \ + defined(__x86_64__) && \ + (DYNAMIC_BMI2 || defined(__BMI2__)) +# define ZSTD_ENABLE_ASM_X86_64_BMI2 1 +#else +# define ZSTD_ENABLE_ASM_X86_64_BMI2 0 +#endif + +/* + * For x86 ELF targets, add .note.gnu.property section for Intel CET in + * assembly sources when CET is enabled. + * + * Additionally, any function that may be called indirectly must begin + * with ZSTD_CET_ENDBRANCH. + */ +#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__)) \ + && defined(__has_include) +# if __has_include(<cet.h>) +# include <cet.h> +# define ZSTD_CET_ENDBRANCH _CET_ENDBR +# endif +#endif + +#ifndef ZSTD_CET_ENDBRANCH +# define ZSTD_CET_ENDBRANCH +#endif + +#endif /* ZSTD_PORTABILITY_MACROS_H */
diff --git a/Utilities/cmzstd/lib/common/threading.c b/Utilities/cmzstd/lib/common/threading.c index 92cf57c..ca155b9 100644 --- a/Utilities/cmzstd/lib/common/threading.c +++ b/Utilities/cmzstd/lib/common/threading.c
@@ -23,8 +23,7 @@ #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** - * Windows minimalist Pthread Wrapper, based on : - * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html + * Windows minimalist Pthread Wrapper */ @@ -35,37 +34,92 @@ /* === Implementation === */ +typedef struct { + void* (*start_routine)(void*); + void* arg; + int initialized; + ZSTD_pthread_cond_t initialized_cond; + ZSTD_pthread_mutex_t initialized_mutex; +} ZSTD_thread_params_t; + static unsigned __stdcall worker(void *arg) { - ZSTD_pthread_t* const thread = (ZSTD_pthread_t*) arg; - thread->arg = thread->start_routine(thread->arg); + void* (*start_routine)(void*); + void* thread_arg; + + /* Initialized thread_arg and start_routine and signal main thread that we don't need it + * to wait any longer. + */ + { + ZSTD_thread_params_t* thread_param = (ZSTD_thread_params_t*)arg; + thread_arg = thread_param->arg; + start_routine = thread_param->start_routine; + + /* Signal main thread that we are running and do not depend on its memory anymore */ + ZSTD_pthread_mutex_lock(&thread_param->initialized_mutex); + thread_param->initialized = 1; + ZSTD_pthread_cond_signal(&thread_param->initialized_cond); + ZSTD_pthread_mutex_unlock(&thread_param->initialized_mutex); + } + + start_routine(thread_arg); + return 0; } int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, void* (*start_routine) (void*), void* arg) { + ZSTD_thread_params_t thread_param; (void)unused; - thread->arg = arg; - thread->start_routine = start_routine; - thread->handle = (HANDLE) _beginthreadex(NULL, 0, worker, thread, 0, NULL); - if (!thread->handle) + thread_param.start_routine = start_routine; + thread_param.arg = arg; + thread_param.initialized = 0; + *thread = NULL; + + /* Setup thread initialization synchronization */ + if(ZSTD_pthread_cond_init(&thread_param.initialized_cond, NULL)) { + /* Should never happen on Windows */ + return -1; + } + if(ZSTD_pthread_mutex_init(&thread_param.initialized_mutex, NULL)) { + /* Should never happen on Windows */ + ZSTD_pthread_cond_destroy(&thread_param.initialized_cond); + return -1; + } + + /* Spawn thread */ + *thread = (HANDLE)_beginthreadex(NULL, 0, worker, &thread_param, 0, NULL); + if (!thread) { + ZSTD_pthread_mutex_destroy(&thread_param.initialized_mutex); + ZSTD_pthread_cond_destroy(&thread_param.initialized_cond); return errno; - else - return 0; + } + + /* Wait for thread to be initialized */ + ZSTD_pthread_mutex_lock(&thread_param.initialized_mutex); + while(!thread_param.initialized) { + ZSTD_pthread_cond_wait(&thread_param.initialized_cond, &thread_param.initialized_mutex); + } + ZSTD_pthread_mutex_unlock(&thread_param.initialized_mutex); + ZSTD_pthread_mutex_destroy(&thread_param.initialized_mutex); + ZSTD_pthread_cond_destroy(&thread_param.initialized_cond); + + return 0; } -int ZSTD_pthread_join(ZSTD_pthread_t thread, void **value_ptr) +int ZSTD_pthread_join(ZSTD_pthread_t thread) { DWORD result; - if (!thread.handle) return 0; + if (!thread) return 0; - result = WaitForSingleObject(thread.handle, INFINITE); + result = WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + switch (result) { case WAIT_OBJECT_0: - if (value_ptr) *value_ptr = thread.arg; return 0; case WAIT_ABANDONED: return EINVAL;
diff --git a/Utilities/cmzstd/lib/common/threading.h b/Utilities/cmzstd/lib/common/threading.h index fd0060d..fb5c1c8 100644 --- a/Utilities/cmzstd/lib/common/threading.h +++ b/Utilities/cmzstd/lib/common/threading.h
@@ -23,8 +23,7 @@ #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** - * Windows minimalist Pthread Wrapper, based on : - * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html + * Windows minimalist Pthread Wrapper */ #ifdef WINVER # undef WINVER @@ -62,16 +61,12 @@ #define ZSTD_pthread_cond_broadcast(a) WakeAllConditionVariable((a)) /* ZSTD_pthread_create() and ZSTD_pthread_join() */ -typedef struct { - HANDLE handle; - void* (*start_routine)(void*); - void* arg; -} ZSTD_pthread_t; +typedef HANDLE ZSTD_pthread_t; int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused, void* (*start_routine) (void*), void* arg); -int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr); +int ZSTD_pthread_join(ZSTD_pthread_t thread); /** * add here more wrappers as required @@ -99,7 +94,7 @@ #define ZSTD_pthread_t pthread_t #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) -#define ZSTD_pthread_join(a, b) pthread_join((a),(b)) +#define ZSTD_pthread_join(a) pthread_join((a),NULL) #else /* DEBUGLEVEL >= 1 */ @@ -124,7 +119,7 @@ #define ZSTD_pthread_t pthread_t #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) -#define ZSTD_pthread_join(a, b) pthread_join((a),(b)) +#define ZSTD_pthread_join(a) pthread_join((a),NULL) #endif
diff --git a/Utilities/cmzstd/lib/common/xxhash.c b/Utilities/cmzstd/lib/common/xxhash.c index 926b336..fd237c9 100644 --- a/Utilities/cmzstd/lib/common/xxhash.c +++ b/Utilities/cmzstd/lib/common/xxhash.c
@@ -1,11 +1,11 @@ /* * xxHash - Fast Hash algorithm - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : - * - xxHash homepage: http://www.xxhash.com + * - xxHash homepage: https://cyan4973.github.io/xxHash/ * - xxHash source repository : https://github.com/Cyan4973/xxHash - * + * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). @@ -13,812 +13,12 @@ */ -/* ************************************* -* Tuning parameters -***************************************/ -/*!XXH_FORCE_MEMORY_ACCESS : - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. - * It can generate buggy code on targets which do not support unaligned memory accesses. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See http://stackoverflow.com/a/32095106/646947 for details. - * Prefer these methods in priority order (0 > 1 > 2) + +/* + * xxhash.c instantiates functions defined in xxhash.h */ -#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ -# if (defined(__INTEL_COMPILER) && !defined(WIN32)) || \ - (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) || \ - defined(__ICCARM__) -# define XXH_FORCE_MEMORY_ACCESS 1 -# endif -#endif -/*!XXH_ACCEPT_NULL_INPUT_POINTER : - * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. - * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. - * By default, this option is disabled. To enable it, uncomment below define : - */ -/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */ +#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */ +#define XXH_IMPLEMENTATION /* access definitions */ -/*!XXH_FORCE_NATIVE_FORMAT : - * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. - * Results are therefore identical for little-endian and big-endian CPU. - * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. - * Should endian-independence be of no importance for your application, you may set the #define below to 1, - * to improve speed for Big-endian CPU. - * This option has no impact on Little_Endian CPU. - */ -#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ -# define XXH_FORCE_NATIVE_FORMAT 0 -#endif - -/*!XXH_FORCE_ALIGN_CHECK : - * This is a minor performance trick, only useful with lots of very small keys. - * It means : check for aligned/unaligned input. - * The check costs one initial branch per hash; set to 0 when the input data - * is guaranteed to be aligned. - */ -#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ -# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) -# define XXH_FORCE_ALIGN_CHECK 0 -# else -# define XXH_FORCE_ALIGN_CHECK 1 -# endif -#endif - - -/* ************************************* -* Includes & Memory related functions -***************************************/ -/* Modify the local functions below should you wish to use some other memory routines */ -/* for ZSTD_malloc(), ZSTD_free() */ -#define ZSTD_DEPS_NEED_MALLOC -#include "zstd_deps.h" /* size_t, ZSTD_malloc, ZSTD_free, ZSTD_memcpy */ -static void* XXH_malloc(size_t s) { return ZSTD_malloc(s); } -static void XXH_free (void* p) { ZSTD_free(p); } -static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_memcpy(dest,src,size); } - -#ifndef XXH_STATIC_LINKING_ONLY -# define XXH_STATIC_LINKING_ONLY -#endif #include "xxhash.h" - - -/* ************************************* -* Compiler Specific Options -***************************************/ -#include "compiler.h" - - -/* ************************************* -* Basic Types -***************************************/ -#include "mem.h" /* BYTE, U32, U64, size_t */ - -#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) - -/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ -static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } -static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } - -#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign; - -static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -static U64 XXH_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } - -#else - -/* portable and safe solution. Generally efficient. - * see : http://stackoverflow.com/a/32095106/646947 - */ - -static U32 XXH_read32(const void* memPtr) -{ - U32 val; - ZSTD_memcpy(&val, memPtr, sizeof(val)); - return val; -} - -static U64 XXH_read64(const void* memPtr) -{ - U64 val; - ZSTD_memcpy(&val, memPtr, sizeof(val)); - return val; -} - -#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ - - -/* **************************************** -* Compiler-specific Functions and Macros -******************************************/ -#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) - -/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ -#if defined(_MSC_VER) -# define XXH_rotl32(x,r) _rotl(x,r) -# define XXH_rotl64(x,r) _rotl64(x,r) -#else -#if defined(__ICCARM__) -# include <intrinsics.h> -# define XXH_rotl32(x,r) __ROR(x,(32 - r)) -#else -# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) -#endif -# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) -#endif - -#if defined(_MSC_VER) /* Visual Studio */ -# define XXH_swap32 _byteswap_ulong -# define XXH_swap64 _byteswap_uint64 -#elif GCC_VERSION >= 403 -# define XXH_swap32 __builtin_bswap32 -# define XXH_swap64 __builtin_bswap64 -#else -static U32 XXH_swap32 (U32 x) -{ - return ((x << 24) & 0xff000000 ) | - ((x << 8) & 0x00ff0000 ) | - ((x >> 8) & 0x0000ff00 ) | - ((x >> 24) & 0x000000ff ); -} -static U64 XXH_swap64 (U64 x) -{ - return ((x << 56) & 0xff00000000000000ULL) | - ((x << 40) & 0x00ff000000000000ULL) | - ((x << 24) & 0x0000ff0000000000ULL) | - ((x << 8) & 0x000000ff00000000ULL) | - ((x >> 8) & 0x00000000ff000000ULL) | - ((x >> 24) & 0x0000000000ff0000ULL) | - ((x >> 40) & 0x000000000000ff00ULL) | - ((x >> 56) & 0x00000000000000ffULL); -} -#endif - - -/* ************************************* -* Architecture Macros -***************************************/ -typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; - -/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ -#ifndef XXH_CPU_LITTLE_ENDIAN - static const int g_one = 1; -# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one)) -#endif - - -/* *************************** -* Memory reads -*****************************/ -typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; - -FORCE_INLINE_TEMPLATE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) -{ - if (align==XXH_unaligned) - return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); - else - return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); -} - -FORCE_INLINE_TEMPLATE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) -{ - return XXH_readLE32_align(ptr, endian, XXH_unaligned); -} - -static U32 XXH_readBE32(const void* ptr) -{ - return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); -} - -FORCE_INLINE_TEMPLATE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) -{ - if (align==XXH_unaligned) - return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); - else - return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); -} - -FORCE_INLINE_TEMPLATE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) -{ - return XXH_readLE64_align(ptr, endian, XXH_unaligned); -} - -static U64 XXH_readBE64(const void* ptr) -{ - return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); -} - - -/* ************************************* -* Macros -***************************************/ -#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ - - -/* ************************************* -* Constants -***************************************/ -static const U32 PRIME32_1 = 2654435761U; -static const U32 PRIME32_2 = 2246822519U; -static const U32 PRIME32_3 = 3266489917U; -static const U32 PRIME32_4 = 668265263U; -static const U32 PRIME32_5 = 374761393U; - -static const U64 PRIME64_1 = 11400714785074694791ULL; -static const U64 PRIME64_2 = 14029467366897019727ULL; -static const U64 PRIME64_3 = 1609587929392839161ULL; -static const U64 PRIME64_4 = 9650029242287828579ULL; -static const U64 PRIME64_5 = 2870177450012600261ULL; - -XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } - - -/* ************************** -* Utils -****************************/ -XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dstState, const XXH32_state_t* restrict srcState) -{ - ZSTD_memcpy(dstState, srcState, sizeof(*dstState)); -} - -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dstState, const XXH64_state_t* restrict srcState) -{ - ZSTD_memcpy(dstState, srcState, sizeof(*dstState)); -} - - -/* *************************** -* Simple Hash Functions -*****************************/ - -static U32 XXH32_round(U32 seed, U32 input) -{ - seed += input * PRIME32_2; - seed = XXH_rotl32(seed, 13); - seed *= PRIME32_1; - return seed; -} - -FORCE_INLINE_TEMPLATE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) -{ - const BYTE* p = (const BYTE*)input; - const BYTE* bEnd = p + len; - U32 h32; -#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) - -#ifdef XXH_ACCEPT_NULL_INPUT_POINTER - if (p==NULL) { - len=0; - bEnd=p=(const BYTE*)(size_t)16; - } -#endif - - if (len>=16) { - const BYTE* const limit = bEnd - 16; - U32 v1 = seed + PRIME32_1 + PRIME32_2; - U32 v2 = seed + PRIME32_2; - U32 v3 = seed + 0; - U32 v4 = seed - PRIME32_1; - - do { - v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; - v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; - v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; - v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; - } while (p<=limit); - - h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); - } else { - h32 = seed + PRIME32_5; - } - - h32 += (U32) len; - - while (p+4<=bEnd) { - h32 += XXH_get32bits(p) * PRIME32_3; - h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; - p+=4; - } - - while (p<bEnd) { - h32 += (*p) * PRIME32_5; - h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; - p++; - } - - h32 ^= h32 >> 15; - h32 *= PRIME32_2; - h32 ^= h32 >> 13; - h32 *= PRIME32_3; - h32 ^= h32 >> 16; - - return h32; -} - - -XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) -{ -#if 0 - /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ - XXH32_CREATESTATE_STATIC(state); - XXH32_reset(state, seed); - XXH32_update(state, input, len); - return XXH32_digest(state); -#else - XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; - - if (XXH_FORCE_ALIGN_CHECK) { - if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); - else - return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); - } } - - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); - else - return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); -#endif -} - - -static U64 XXH64_round(U64 acc, U64 input) -{ - acc += input * PRIME64_2; - acc = XXH_rotl64(acc, 31); - acc *= PRIME64_1; - return acc; -} - -static U64 XXH64_mergeRound(U64 acc, U64 val) -{ - val = XXH64_round(0, val); - acc ^= val; - acc = acc * PRIME64_1 + PRIME64_4; - return acc; -} - -FORCE_INLINE_TEMPLATE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) -{ - const BYTE* p = (const BYTE*)input; - const BYTE* const bEnd = p + len; - U64 h64; -#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) - -#ifdef XXH_ACCEPT_NULL_INPUT_POINTER - if (p==NULL) { - len=0; - bEnd=p=(const BYTE*)(size_t)32; - } -#endif - - if (len>=32) { - const BYTE* const limit = bEnd - 32; - U64 v1 = seed + PRIME64_1 + PRIME64_2; - U64 v2 = seed + PRIME64_2; - U64 v3 = seed + 0; - U64 v4 = seed - PRIME64_1; - - do { - v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; - v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; - v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; - v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; - } while (p<=limit); - - h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); - h64 = XXH64_mergeRound(h64, v1); - h64 = XXH64_mergeRound(h64, v2); - h64 = XXH64_mergeRound(h64, v3); - h64 = XXH64_mergeRound(h64, v4); - - } else { - h64 = seed + PRIME64_5; - } - - h64 += (U64) len; - - while (p+8<=bEnd) { - U64 const k1 = XXH64_round(0, XXH_get64bits(p)); - h64 ^= k1; - h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; - p+=8; - } - - if (p+4<=bEnd) { - h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; - h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; - p+=4; - } - - while (p<bEnd) { - h64 ^= (*p) * PRIME64_5; - h64 = XXH_rotl64(h64, 11) * PRIME64_1; - p++; - } - - h64 ^= h64 >> 33; - h64 *= PRIME64_2; - h64 ^= h64 >> 29; - h64 *= PRIME64_3; - h64 ^= h64 >> 32; - - return h64; -} - - -XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) -{ -#if 0 - /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ - XXH64_CREATESTATE_STATIC(state); - XXH64_reset(state, seed); - XXH64_update(state, input, len); - return XXH64_digest(state); -#else - XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; - - if (XXH_FORCE_ALIGN_CHECK) { - if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); - else - return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); - } } - - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); - else - return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); -#endif -} - - -/* ************************************************** -* Advanced Hash Functions -****************************************************/ - -XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) -{ - return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); -} -XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) -{ - XXH_free(statePtr); - return XXH_OK; -} - -XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) -{ - return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); -} -XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) -{ - XXH_free(statePtr); - return XXH_OK; -} - - -/*** Hash feed ***/ - -XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) -{ - XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ - ZSTD_memset(&state, 0, sizeof(state)-4); /* do not write into reserved, for future removal */ - state.v1 = seed + PRIME32_1 + PRIME32_2; - state.v2 = seed + PRIME32_2; - state.v3 = seed + 0; - state.v4 = seed - PRIME32_1; - ZSTD_memcpy(statePtr, &state, sizeof(state)); - return XXH_OK; -} - - -XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) -{ - XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ - ZSTD_memset(&state, 0, sizeof(state)-8); /* do not write into reserved, for future removal */ - state.v1 = seed + PRIME64_1 + PRIME64_2; - state.v2 = seed + PRIME64_2; - state.v3 = seed + 0; - state.v4 = seed - PRIME64_1; - ZSTD_memcpy(statePtr, &state, sizeof(state)); - return XXH_OK; -} - - -FORCE_INLINE_TEMPLATE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) -{ - const BYTE* p = (const BYTE*)input; - const BYTE* const bEnd = p + len; - -#ifdef XXH_ACCEPT_NULL_INPUT_POINTER - if (input==NULL) return XXH_ERROR; -#endif - - state->total_len_32 += (unsigned)len; - state->large_len |= (len>=16) | (state->total_len_32>=16); - - if (state->memsize + len < 16) { /* fill in tmp buffer */ - XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); - state->memsize += (unsigned)len; - return XXH_OK; - } - - if (state->memsize) { /* some data left from previous update */ - XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); - { const U32* p32 = state->mem32; - state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; - state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; - state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; - state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++; - } - p += 16-state->memsize; - state->memsize = 0; - } - - if (p <= bEnd-16) { - const BYTE* const limit = bEnd - 16; - U32 v1 = state->v1; - U32 v2 = state->v2; - U32 v3 = state->v3; - U32 v4 = state->v4; - - do { - v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; - v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; - v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; - v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; - } while (p<=limit); - - state->v1 = v1; - state->v2 = v2; - state->v3 = v3; - state->v4 = v4; - } - - if (p < bEnd) { - XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); - state->memsize = (unsigned)(bEnd-p); - } - - return XXH_OK; -} - -XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) -{ - XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; - - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH32_update_endian(state_in, input, len, XXH_littleEndian); - else - return XXH32_update_endian(state_in, input, len, XXH_bigEndian); -} - - - -FORCE_INLINE_TEMPLATE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) -{ - const BYTE * p = (const BYTE*)state->mem32; - const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize; - U32 h32; - - if (state->large_len) { - h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); - } else { - h32 = state->v3 /* == seed */ + PRIME32_5; - } - - h32 += state->total_len_32; - - while (p+4<=bEnd) { - h32 += XXH_readLE32(p, endian) * PRIME32_3; - h32 = XXH_rotl32(h32, 17) * PRIME32_4; - p+=4; - } - - while (p<bEnd) { - h32 += (*p) * PRIME32_5; - h32 = XXH_rotl32(h32, 11) * PRIME32_1; - p++; - } - - h32 ^= h32 >> 15; - h32 *= PRIME32_2; - h32 ^= h32 >> 13; - h32 *= PRIME32_3; - h32 ^= h32 >> 16; - - return h32; -} - - -XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) -{ - XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; - - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH32_digest_endian(state_in, XXH_littleEndian); - else - return XXH32_digest_endian(state_in, XXH_bigEndian); -} - - - -/* **** XXH64 **** */ - -FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) -{ - const BYTE* p = (const BYTE*)input; - const BYTE* const bEnd = p + len; - -#ifdef XXH_ACCEPT_NULL_INPUT_POINTER - if (input==NULL) return XXH_ERROR; -#endif - - state->total_len += len; - - if (state->memsize + len < 32) { /* fill in tmp buffer */ - if (input != NULL) { - XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); - } - state->memsize += (U32)len; - return XXH_OK; - } - - if (state->memsize) { /* tmp buffer is full */ - XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); - state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); - state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); - state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); - state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); - p += 32-state->memsize; - state->memsize = 0; - } - - if (p+32 <= bEnd) { - const BYTE* const limit = bEnd - 32; - U64 v1 = state->v1; - U64 v2 = state->v2; - U64 v3 = state->v3; - U64 v4 = state->v4; - - do { - v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; - v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; - v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; - v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; - } while (p<=limit); - - state->v1 = v1; - state->v2 = v2; - state->v3 = v3; - state->v4 = v4; - } - - if (p < bEnd) { - XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); - state->memsize = (unsigned)(bEnd-p); - } - - return XXH_OK; -} - -XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) -{ - XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; - - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH64_update_endian(state_in, input, len, XXH_littleEndian); - else - return XXH64_update_endian(state_in, input, len, XXH_bigEndian); -} - - - -FORCE_INLINE_TEMPLATE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) -{ - const BYTE * p = (const BYTE*)state->mem64; - const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize; - U64 h64; - - if (state->total_len >= 32) { - U64 const v1 = state->v1; - U64 const v2 = state->v2; - U64 const v3 = state->v3; - U64 const v4 = state->v4; - - h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); - h64 = XXH64_mergeRound(h64, v1); - h64 = XXH64_mergeRound(h64, v2); - h64 = XXH64_mergeRound(h64, v3); - h64 = XXH64_mergeRound(h64, v4); - } else { - h64 = state->v3 + PRIME64_5; - } - - h64 += (U64) state->total_len; - - while (p+8<=bEnd) { - U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian)); - h64 ^= k1; - h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; - p+=8; - } - - if (p+4<=bEnd) { - h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; - h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; - p+=4; - } - - while (p<bEnd) { - h64 ^= (*p) * PRIME64_5; - h64 = XXH_rotl64(h64, 11) * PRIME64_1; - p++; - } - - h64 ^= h64 >> 33; - h64 *= PRIME64_2; - h64 ^= h64 >> 29; - h64 *= PRIME64_3; - h64 ^= h64 >> 32; - - return h64; -} - - -XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) -{ - XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; - - if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) - return XXH64_digest_endian(state_in, XXH_littleEndian); - else - return XXH64_digest_endian(state_in, XXH_bigEndian); -} - - -/* ************************** -* Canonical representation -****************************/ - -/*! Default XXH result types are basic unsigned 32 and 64 bits. -* The canonical representation follows human-readable write convention, aka big-endian (large digits first). -* These functions allow transformation of hash result into and from its canonical format. -* This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs. -*/ - -XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) -{ - XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); - if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); - ZSTD_memcpy(dst, &hash, sizeof(*dst)); -} - -XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) -{ - XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); - if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); - ZSTD_memcpy(dst, &hash, sizeof(*dst)); -} - -XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) -{ - return XXH_readBE32(src); -} - -XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) -{ - return XXH_readBE64(src); -}
diff --git a/Utilities/cmzstd/lib/common/xxhash.h b/Utilities/cmzstd/lib/common/xxhash.h index 16c1f16..b8b7329 100644 --- a/Utilities/cmzstd/lib/common/xxhash.h +++ b/Utilities/cmzstd/lib/common/xxhash.h
@@ -1,20 +1,36 @@ /* - * xxHash - Extremely Fast Hash algorithm - * Header File - * Copyright (c) Yann Collet, Facebook, Inc. + * xxHash - Fast Hash algorithm + * Copyright (c) Meta Platforms, Inc. and affiliates. * - * You can contact the author at : - * - xxHash source repository : https://github.com/Cyan4973/xxHash - * + * You can contact the author at : + * - xxHash homepage: https://cyan4973.github.io/xxHash/ + * - xxHash source repository : https://github.com/Cyan4973/xxHash + * * This source code is licensed under both the BSD-style license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ -/* Notice extracted from xxHash homepage : -xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +#ifndef XXH_NO_XXH3 +# define XXH_NO_XXH3 +#endif + +#ifndef XXH_NAMESPACE +# define XXH_NAMESPACE ZSTD_ +#endif + +/*! + * @mainpage xxHash + * + * @file xxhash.h + * xxHash prototypes and implementation + */ +/* TODO: update */ +/* Notice extracted from xxHash homepage: + +xxHash is an extremely fast hash algorithm, running at RAM speed limits. It also successfully passes all tests from the SMHasher suite. Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) @@ -22,7 +38,7 @@ Name Speed Q.Score Author xxHash 5.4 GB/s 10 CrapWow 3.2 GB/s 2 Andrew -MumurHash 3a 2.7 GB/s 10 Austin Appleby +MurmurHash 3a 2.7 GB/s 10 Austin Appleby SpookyHash 2.0 GB/s 10 Bob Jenkins SBox 1.4 GB/s 9 Bret Mulvey Lookup3 1.2 GB/s 9 Bob Jenkins @@ -37,8 +53,13 @@ It depends on successfully passing SMHasher test set. 10 is a perfect score. -A 64-bits version, named XXH64, is available since r35. -It offers much better speed, but for 64-bits applications only. +Note: SMHasher's CRC32 implementation is not the fastest one. +Other speed-oriented implementations can be faster, +especially in combination with PCLMUL instruction: +https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735 + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. Name Speed on 64 bits Speed on 32 bits XXH64 13.8 GB/s 1.9 GB/s XXH32 6.8 GB/s 6.0 GB/s @@ -48,33 +69,34 @@ extern "C" { #endif -#ifndef XXHASH_H_5627135585666179 -#define XXHASH_H_5627135585666179 1 - - /* **************************** -* Definitions -******************************/ -#include "zstd_deps.h" -typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; - - -/* **************************** -* API modifier -******************************/ -/** XXH_PRIVATE_API -* This is useful if you want to include xxhash functions in `static` mode -* in order to inline them, and remove their symbol from the public list. -* Methodology : -* #define XXH_PRIVATE_API -* #include "xxhash.h" -* `xxhash.c` is automatically included. -* It's not useful to compile and link it as a separate module anymore. -*/ -#ifdef XXH_PRIVATE_API -# ifndef XXH_STATIC_LINKING_ONLY -# define XXH_STATIC_LINKING_ONLY -# endif + * INLINE mode + ******************************/ +/*! + * XXH_INLINE_ALL (and XXH_PRIVATE_API) + * Use these build macros to inline xxhash into the target unit. + * Inlining improves performance on small inputs, especially when the length is + * expressed as a compile-time constant: + * + * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html + * + * It also keeps xxHash symbols private to the unit, so they are not exported. + * + * Usage: + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * + * Do not compile and link xxhash.o as a separate object, as it is not useful. + */ +#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ + && !defined(XXH_INLINE_ALL_31684351384) + /* this section should be traversed only once */ +# define XXH_INLINE_ALL_31684351384 + /* give access to the advanced API, required to compile implementations */ +# undef XXH_STATIC_LINKING_ONLY /* avoid macro redef */ +# define XXH_STATIC_LINKING_ONLY + /* make all functions private */ +# undef XXH_PUBLIC_API # if defined(__GNUC__) # define XXH_PUBLIC_API static __inline __attribute__((unused)) # elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) @@ -82,45 +104,205 @@ # elif defined(_MSC_VER) # define XXH_PUBLIC_API static __inline # else -# define XXH_PUBLIC_API static /* this version may generate warnings for unused static functions; disable the relevant warning */ + /* note: this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static # endif -#else -# define XXH_PUBLIC_API /* do nothing */ -#endif /* XXH_PRIVATE_API */ -/*!XXH_NAMESPACE, aka Namespace Emulation : + /* + * This part deals with the special case where a unit wants to inline xxHash, + * but "xxhash.h" has previously been included without XXH_INLINE_ALL, + * such as part of some previously included *.h header file. + * Without further action, the new include would just be ignored, + * and functions would effectively _not_ be inlined (silent failure). + * The following macros solve this situation by prefixing all inlined names, + * avoiding naming collision with previous inclusions. + */ + /* Before that, we unconditionally #undef all symbols, + * in case they were already defined with XXH_NAMESPACE. + * They will then be redefined for XXH_INLINE_ALL + */ +# undef XXH_versionNumber + /* XXH32 */ +# undef XXH32 +# undef XXH32_createState +# undef XXH32_freeState +# undef XXH32_reset +# undef XXH32_update +# undef XXH32_digest +# undef XXH32_copyState +# undef XXH32_canonicalFromHash +# undef XXH32_hashFromCanonical + /* XXH64 */ +# undef XXH64 +# undef XXH64_createState +# undef XXH64_freeState +# undef XXH64_reset +# undef XXH64_update +# undef XXH64_digest +# undef XXH64_copyState +# undef XXH64_canonicalFromHash +# undef XXH64_hashFromCanonical + /* XXH3_64bits */ +# undef XXH3_64bits +# undef XXH3_64bits_withSecret +# undef XXH3_64bits_withSeed +# undef XXH3_64bits_withSecretandSeed +# undef XXH3_createState +# undef XXH3_freeState +# undef XXH3_copyState +# undef XXH3_64bits_reset +# undef XXH3_64bits_reset_withSeed +# undef XXH3_64bits_reset_withSecret +# undef XXH3_64bits_update +# undef XXH3_64bits_digest +# undef XXH3_generateSecret + /* XXH3_128bits */ +# undef XXH128 +# undef XXH3_128bits +# undef XXH3_128bits_withSeed +# undef XXH3_128bits_withSecret +# undef XXH3_128bits_reset +# undef XXH3_128bits_reset_withSeed +# undef XXH3_128bits_reset_withSecret +# undef XXH3_128bits_reset_withSecretandSeed +# undef XXH3_128bits_update +# undef XXH3_128bits_digest +# undef XXH128_isEqual +# undef XXH128_cmp +# undef XXH128_canonicalFromHash +# undef XXH128_hashFromCanonical + /* Finally, free the namespace itself */ +# undef XXH_NAMESPACE -If you want to include _and expose_ xxHash functions from within your own library, -but also want to avoid symbol collisions with another library which also includes xxHash, + /* employ the namespace for XXH_INLINE_ALL */ +# define XXH_NAMESPACE XXH_INLINE_ + /* + * Some identifiers (enums, type names) are not symbols, + * but they must nonetheless be renamed to avoid redeclaration. + * Alternative solution: do not redeclare them. + * However, this requires some #ifdefs, and has a more dispersed impact. + * Meanwhile, renaming can be achieved in a single place. + */ +# define XXH_IPREF(Id) XXH_NAMESPACE ## Id +# define XXH_OK XXH_IPREF(XXH_OK) +# define XXH_ERROR XXH_IPREF(XXH_ERROR) +# define XXH_errorcode XXH_IPREF(XXH_errorcode) +# define XXH32_canonical_t XXH_IPREF(XXH32_canonical_t) +# define XXH64_canonical_t XXH_IPREF(XXH64_canonical_t) +# define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t) +# define XXH32_state_s XXH_IPREF(XXH32_state_s) +# define XXH32_state_t XXH_IPREF(XXH32_state_t) +# define XXH64_state_s XXH_IPREF(XXH64_state_s) +# define XXH64_state_t XXH_IPREF(XXH64_state_t) +# define XXH3_state_s XXH_IPREF(XXH3_state_s) +# define XXH3_state_t XXH_IPREF(XXH3_state_t) +# define XXH128_hash_t XXH_IPREF(XXH128_hash_t) + /* Ensure the header is parsed again, even if it was previously included */ +# undef XXHASH_H_5627135585666179 +# undef XXHASH_H_STATIC_13879238742 +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ -you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library -with the value of XXH_NAMESPACE (so avoid to keep it NULL and avoid numeric values). -Note that no change is required within the calling program as long as it includes `xxhash.h` : -regular symbol name will be automatically translated by this header. -*/ + +/* **************************************************************** + * Stable API + *****************************************************************/ +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + + +/*! + * @defgroup public Public API + * Contains details on the public xxHash functions. + * @{ + */ +/* specific declaration modes for Windows */ +#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) +# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif + +#ifdef XXH_DOXYGEN +/*! + * @brief Emulate a namespace by transparently prefixing all symbols. + * + * If you want to include _and expose_ xxHash functions from within your own + * library, but also want to avoid symbol collisions with other libraries which + * may also include xxHash, you can use XXH_NAMESPACE to automatically prefix + * any public symbol from xxhash library with the value of XXH_NAMESPACE + * (therefore, avoid empty or numeric values). + * + * Note that no change is required within the calling program as long as it + * includes `xxhash.h`: Regular symbol names will be automatically translated + * by this header. + */ +# define XXH_NAMESPACE /* YOUR NAME HERE */ +# undef XXH_NAMESPACE +#endif + #ifdef XXH_NAMESPACE # define XXH_CAT(A,B) A##B # define XXH_NAME2(A,B) XXH_CAT(A,B) -# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) -# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) # define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +/* XXH32 */ +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) # define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) -# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) # define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) -# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) # define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) -# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) # define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) -# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) # define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) -# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) # define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) -# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) # define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) -# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) # define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +/* XXH64 */ +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) # define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +/* XXH3_64bits */ +# define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) +# define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) +# define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) +# define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed) +# define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) +# define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) +# define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) +# define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) +# define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) +# define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) +# define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed) +# define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) +# define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) +# define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) +# define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed) +/* XXH3_128bits */ +# define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) +# define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) +# define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) +# define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) +# define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed) +# define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) +# define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) +# define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) +# define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed) +# define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) +# define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) +# define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) +# define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) +# define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) +# define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) #endif @@ -128,156 +310,5375 @@ * Version ***************************************/ #define XXH_VERSION_MAJOR 0 -#define XXH_VERSION_MINOR 6 -#define XXH_VERSION_RELEASE 2 +#define XXH_VERSION_MINOR 8 +#define XXH_VERSION_RELEASE 1 #define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) + +/*! + * @brief Obtains the xxHash version. + * + * This is mostly useful when xxHash is compiled as a shared library, + * since the returned value comes from the library, as opposed to header file. + * + * @return `XXH_VERSION_NUMBER` of the invoked library. + */ XXH_PUBLIC_API unsigned XXH_versionNumber (void); /* **************************** -* Simple Hash Functions +* Common basic types ******************************/ -typedef unsigned int XXH32_hash_t; -typedef unsigned long long XXH64_hash_t; +#include <stddef.h> /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; -XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); -XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +#if defined(XXH_DOXYGEN) /* Don't show <stdint.h> include */ +/*! + * @brief An unsigned 32-bit integer. + * + * Not necessarily defined to `uint32_t` but functionally equivalent. + */ +typedef uint32_t XXH32_hash_t; + +#elif !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include <stdint.h> + typedef uint32_t XXH32_hash_t; + +#else +# include <limits.h> +# if UINT_MAX == 0xFFFFFFFFUL + typedef unsigned int XXH32_hash_t; +# else +# if ULONG_MAX == 0xFFFFFFFFUL + typedef unsigned long XXH32_hash_t; +# else +# error "unsupported platform: need a 32-bit type" +# endif +# endif +#endif /*! -XXH32() : - Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input". - The memory between input & input+length must be valid (allocated and read-accessible). - "seed" can be used to alter the result predictably. - Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s -XXH64() : - Calculate the 64-bits hash of sequence of length "len" stored at memory address "input". - "seed" can be used to alter the result predictably. - This function runs 2x faster on 64-bits systems, but slower on 32-bits systems (see benchmark). -*/ + * @} + * + * @defgroup xxh32_family XXH32 family + * @ingroup public + * Contains functions used in the classic 32-bit xxHash algorithm. + * + * @note + * XXH32 is useful for older platforms, with no or poor 64-bit performance. + * Note that @ref xxh3_family provides competitive speed + * for both 32-bit and 64-bit systems, and offers true 64/128 bit hash results. + * + * @see @ref xxh64_family, @ref xxh3_family : Other xxHash families + * @see @ref xxh32_impl for implementation details + * @{ + */ +/*! + * @brief Calculates the 32-bit hash of @p input using xxHash32. + * + * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 32-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 32-bit hash value. + * + * @see + * XXH64(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): + * Direct equivalents for the other variants of xxHash. + * @see + * XXH32_createState(), XXH32_update(), XXH32_digest(): Streaming version. + */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); -/* **************************** -* Streaming Hash Functions -******************************/ -typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ -typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +/*! + * Streaming functions generate the xxHash value from an incremental input. + * This method is slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * An XXH state must first be allocated using `XXH*_createState()`. + * + * Start a new hash by initializing the state with a seed using `XXH*_reset()`. + * + * Then, feed the hash state by calling `XXH*_update()` as many times as necessary. + * + * The function returns an error code, with 0 meaning OK, and any other value + * meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using `XXH*_digest()`. + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a + * digest, and generate new hash values later on by invoking `XXH*_digest()`. + * + * When done, release the state using `XXH*_freeState()`. + * + * Example code for incrementally hashing a file: + * @code{.c} + * #include <stdio.h> + * #include <xxhash.h> + * #define BUFFER_SIZE 256 + * + * // Note: XXH64 and XXH3 use the same interface. + * XXH32_hash_t + * hashFile(FILE* stream) + * { + * XXH32_state_t* state; + * unsigned char buf[BUFFER_SIZE]; + * size_t amt; + * XXH32_hash_t hash; + * + * state = XXH32_createState(); // Create a state + * assert(state != NULL); // Error check here + * XXH32_reset(state, 0xbaad5eed); // Reset state with our seed + * while ((amt = fread(buf, 1, sizeof(buf), stream)) != 0) { + * XXH32_update(state, buf, amt); // Hash the file in chunks + * } + * hash = XXH32_digest(state); // Finalize the hash + * XXH32_freeState(state); // Clean up + * return hash; + * } + * @endcode + */ -/*! State allocation, compatible with dynamic libraries */ +/*! + * @typedef struct XXH32_state_s XXH32_state_t + * @brief The opaque state struct for the XXH32 streaming API. + * + * @see XXH32_state_s for details. + */ +typedef struct XXH32_state_s XXH32_state_t; +/*! + * @brief Allocates an @ref XXH32_state_t. + * + * Must be freed with XXH32_freeState(). + * @return An allocated XXH32_state_t on success, `NULL` on failure. + */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +/*! + * @brief Frees an @ref XXH32_state_t. + * + * Must be allocated with XXH32_createState(). + * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). + * @return XXH_OK. + */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +/*! + * @brief Copies one @ref XXH32_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); -XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); -XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +/*! + * @brief Resets an @ref XXH32_state_t to begin a new hash. + * + * This function resets and seeds a state. Call it before @ref XXH32_update(). + * + * @param statePtr The state struct to reset. + * @param seed The 32-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); - -/* hash streaming */ - -XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +/*! + * @brief Consumes a block of @p input to an @ref XXH32_state_t. + * + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. + */ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); + +/*! + * @brief Returns the calculated hash value from an @ref XXH32_state_t. + * + * @note + * Calling XXH32_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated xxHash32 value from that state. + */ XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); -XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +/******* Canonical representation *******/ + +/* + * The default return values from XXH functions are unsigned 32 and 64 bit + * integers. + * This the simplest and fastest format for further post-processing. + * + * However, this leaves open the question of what is the order on the byte level, + * since little and big endian conventions will store the same number differently. + * + * The canonical representation settles this issue by mandating big-endian + * convention, the same convention as human-readable numbers (large digits first). + * + * When writing hash values to storage, sending them over a network, or printing + * them, it's highly recommended to use the canonical representation to ensure + * portability across a wider range of systems, present and future. + * + * The following functions allow transformation of hash values to and from + * canonical format. + */ + +/*! + * @brief Canonical (big endian) representation of @ref XXH32_hash_t. + */ +typedef struct { + unsigned char digest[4]; /*!< Hash bytes, big endian */ +} XXH32_canonical_t; + +/*! + * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t. + * + * @param dst The @ref XXH32_canonical_t pointer to be stored to. + * @param hash The @ref XXH32_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + */ +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); + +/*! + * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. + * + * @param src The @ref XXH32_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + */ +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + + +#ifdef __has_attribute +# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define XXH_HAS_ATTRIBUTE(x) 0 +#endif + +/* C-language Attributes are added in C23. */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201710L) && defined(__has_c_attribute) +# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) +#else +# define XXH_HAS_C_ATTRIBUTE(x) 0 +#endif + +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define XXH_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +/* +Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute +introduced in CPP17 and C23. +CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough +C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough +*/ +#if XXH_HAS_C_ATTRIBUTE(x) +# define XXH_FALLTHROUGH [[fallthrough]] +#elif XXH_HAS_CPP_ATTRIBUTE(x) +# define XXH_FALLTHROUGH [[fallthrough]] +#elif XXH_HAS_ATTRIBUTE(__fallthrough__) +# define XXH_FALLTHROUGH __attribute__ ((fallthrough)) +#else +# define XXH_FALLTHROUGH +#endif + +/*! + * @} + * @ingroup public + * @{ + */ + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +#if defined(XXH_DOXYGEN) /* don't include <stdint.h> */ +/*! + * @brief An unsigned 64-bit integer. + * + * Not necessarily defined to `uint64_t` but functionally equivalent. + */ +typedef uint64_t XXH64_hash_t; +#elif !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include <stdint.h> + typedef uint64_t XXH64_hash_t; +#else +# include <limits.h> +# if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL + /* LP64 ABI says uint64_t is unsigned long */ + typedef unsigned long XXH64_hash_t; +# else + /* the following type must have a width of 64-bit */ + typedef unsigned long long XXH64_hash_t; +# endif +#endif + +/*! + * @} + * + * @defgroup xxh64_family XXH64 family + * @ingroup public + * @{ + * Contains functions used in the classic 64-bit xxHash algorithm. + * + * @note + * XXH3 provides competitive speed for both 32-bit and 64-bit systems, + * and offers true 64/128 bit hash results. + * It provides better speed for systems with vector processing capabilities. + */ + + +/*! + * @brief Calculates the 64-bit hash of @p input using xxHash64. + * + * This function usually runs faster on 64-bit systems, but slower on 32-bit + * systems (see benchmark). + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 64-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit hash. + * + * @see + * XXH32(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): + * Direct equivalents for the other variants of xxHash. + * @see + * XXH64_createState(), XXH64_update(), XXH64_digest(): Streaming version. + */ +XXH_PUBLIC_API XXH64_hash_t XXH64(const void* input, size_t length, XXH64_hash_t seed); + +/******* Streaming *******/ +/*! + * @brief The opaque state struct for the XXH64 streaming API. + * + * @see XXH64_state_s for details. + */ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed); XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); -/* -These functions generate the xxHash of an input provided in multiple segments. -Note that, for small input, they are slower than single-call functions, due to state management. -For small input, prefer `XXH32()` and `XXH64()` . - -XXH state must first be allocated, using XXH*_createState() . - -Start a new hash by initializing state with a seed, using XXH*_reset(). - -Then, feed the hash state by calling XXH*_update() as many times as necessary. -Obviously, input must be allocated and read accessible. -The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. - -Finally, a hash value can be produced anytime, by using XXH*_digest(). -This function returns the nn-bits hash as an int or long long. - -It's still possible to continue inserting input into the hash state after a digest, -and generate some new hashes later on, by calling again XXH*_digest(). - -When done, free XXH state space if it was allocated dynamically. -*/ - - -/* ************************** -* Utils -****************************/ -#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* ! C99 */ -# define restrict /* disable restrict */ -#endif - -XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dst_state, const XXH32_state_t* restrict src_state); -XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dst_state, const XXH64_state_t* restrict src_state); - - -/* ************************** -* Canonical representation -****************************/ -/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. -* The canonical representation uses human-readable write convention, aka big-endian (large digits first). -* These functions allow transformation of hash result into and from its canonical format. -* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. -*/ -typedef struct { unsigned char digest[4]; } XXH32_canonical_t; -typedef struct { unsigned char digest[8]; } XXH64_canonical_t; - -XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +/******* Canonical representation *******/ +typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); - -XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#ifndef XXH_NO_XXH3 +/*! + * @} + * ************************************************************************ + * @defgroup xxh3_family XXH3 family + * @ingroup public + * @{ + * + * XXH3 is a more recent hash algorithm featuring: + * - Improved speed for both small and large inputs + * - True 64-bit and 128-bit outputs + * - SIMD acceleration + * - Improved 32-bit viability + * + * Speed analysis methodology is explained here: + * + * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * + * Compared to XXH64, expect XXH3 to run approximately + * ~2x faster on large inputs and >3x faster on small ones, + * exact differences vary depending on platform. + * + * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic, + * but does not require it. + * Any 32-bit and 64-bit targets that can run XXH32 smoothly + * can run XXH3 at competitive speeds, even without vector support. + * Further details are explained in the implementation. + * + * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8, + * ZVector and scalar targets. This can be controlled via the XXH_VECTOR macro. + * + * XXH3 implementation is portable: + * it has a generic C90 formulation that can be compiled on any platform, + * all implementations generage exactly the same hash value on all platforms. + * Starting from v0.8.0, it's also labelled "stable", meaning that + * any future version will also generate the same hash value. + * + * XXH3 offers 2 variants, _64bits and _128bits. + * + * When only 64 bits are needed, prefer invoking the _64bits variant, as it + * reduces the amount of mixing, resulting in faster speed on small inputs. + * It's also generally simpler to manipulate a scalar return type than a struct. + * + * The API supports one-shot hashing, streaming mode, and custom secrets. + */ + +/*-********************************************************************** +* XXH3 64-bit variant +************************************************************************/ + +/* XXH3_64bits(): + * default 64-bit variant, using default secret and default seed of 0. + * It's the fastest variant. */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); + +/* + * XXH3_64bits_withSeed(): + * This variant generates a custom secret on the fly + * based on default secret altered using the `seed` value. + * While this operation is decently fast, note that it's not completely free. + * Note: seed==0 produces the same results as XXH3_64bits(). + */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); + +/*! + * The bare minimum size for a custom secret. + * + * @see + * XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(), + * XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret(). + */ +#define XXH3_SECRET_SIZE_MIN 136 + +/* + * XXH3_64bits_withSecret(): + * It's possible to provide any blob of bytes as a "secret" to generate the hash. + * This makes it more difficult for an external actor to prepare an intentional collision. + * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN). + * However, the quality of the secret impacts the dispersion of the hash algorithm. + * Therefore, the secret _must_ look like a bunch of random bytes. + * Avoid "trivial" or structured data such as repeated sequences or a text document. + * Whenever in doubt about the "randomness" of the blob of bytes, + * consider employing "XXH3_generateSecret()" instead (see below). + * It will generate a proper high entropy secret derived from the blob of bytes. + * Another advantage of using XXH3_generateSecret() is that + * it guarantees that all bits within the initial blob of bytes + * will impact every bit of the output. + * This is not necessarily the case when using the blob of bytes directly + * because, when hashing _small_ inputs, only a portion of the secret is employed. + */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); + + +/******* Streaming *******/ +/* + * Streaming requires state maintenance. + * This operation costs memory and CPU. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer one-shot functions whenever applicable. + */ + +/*! + * @brief The state struct for the XXH3 streaming API. + * + * @see XXH3_state_s for details. + */ +typedef struct XXH3_state_s XXH3_state_t; +XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); +XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); + +/* + * XXH3_64bits_reset(): + * Initialize with default parameters. + * digest will be equivalent to `XXH3_64bits()`. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); +/* + * XXH3_64bits_reset_withSeed(): + * Generate a custom secret from `seed`, and store it into `statePtr`. + * digest will be equivalent to `XXH3_64bits_withSeed()`. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); +/* + * XXH3_64bits_reset_withSecret(): + * `secret` is referenced, it _must outlive_ the hash streaming session. + * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`, + * and the quality of produced hash values depends on secret's entropy + * (secret's content should look like a bunch of random bytes). + * When in doubt about the randomness of a candidate `secret`, + * consider employing `XXH3_generateSecret()` instead (see below). + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); + +/* note : canonical representation of XXH3 is the same as XXH64 + * since they both produce XXH64_hash_t values */ + + +/*-********************************************************************** +* XXH3 128-bit variant +************************************************************************/ + +/*! + * @brief The return value from 128-bit hashes. + * + * Stored in little endian order, although the fields themselves are in native + * endianness. + */ +typedef struct { + XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ + XXH64_hash_t high64; /*!< `value >> 64` */ +} XXH128_hash_t; + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); + +/******* Streaming *******/ +/* + * Streaming requires state maintenance. + * This operation costs memory and CPU. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer one-shot functions whenever applicable. + * + * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). + * Use already declared XXH3_createState() and XXH3_freeState(). + * + * All reset and streaming functions have same meaning as their 64-bit counterpart. + */ + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr); + +/* Following helper functions make it possible to compare XXH128_hast_t values. + * Since XXH128_hash_t is a structure, this capability is not offered by the language. + * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */ + +/*! + * XXH128_isEqual(): + * Return: 1 if `h1` and `h2` are equal, 0 if they are not. + */ +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); + +/*! + * XXH128_cmp(): + * + * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. + * + * return: >0 if *h128_1 > *h128_2 + * =0 if *h128_1 == *h128_2 + * <0 if *h128_1 < *h128_2 + */ +XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); + + +/******* Canonical representation *******/ +typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); +XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); + + +#endif /* !XXH_NO_XXH3 */ +#endif /* XXH_NO_LONG_LONG */ + +/*! + * @} + */ #endif /* XXHASH_H_5627135585666179 */ -/* ================================================================================================ - This section contains definitions which are not guaranteed to remain stable. - They may change in future versions, becoming incompatible with a different version of the library. - They shall only be used with static linking. - Never use these definitions in association with dynamic linking ! -=================================================================================================== */ -#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXH_STATIC_H_3543687687345) -#define XXH_STATIC_H_3543687687345 +#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) +#define XXHASH_H_STATIC_13879238742 +/* **************************************************************************** + * This section contains declarations which are not guaranteed to remain stable. + * They may change in future versions, becoming incompatible with a different + * version of the library. + * These declarations should only be used with static linking. + * Never use them in association with dynamic linking! + ***************************************************************************** */ -/* These definitions are only meant to allow allocation of XXH state - statically, on stack, or in a struct for example. - Do not use members directly. */ +/* + * These definitions are only present to allow static allocation + * of XXH states, on stack or in a struct, for example. + * Never **ever** access their members directly. + */ - struct XXH32_state_s { - unsigned total_len_32; - unsigned large_len; - unsigned v1; - unsigned v2; - unsigned v3; - unsigned v4; - unsigned mem32[4]; /* buffer defined as U32 for alignment */ - unsigned memsize; - unsigned reserved; /* never read nor write, will be removed in a future version */ - }; /* typedef'd to XXH32_state_t */ - - struct XXH64_state_s { - unsigned long long total_len; - unsigned long long v1; - unsigned long long v2; - unsigned long long v3; - unsigned long long v4; - unsigned long long mem64[4]; /* buffer defined as U64 for alignment */ - unsigned memsize; - unsigned reserved[2]; /* never read nor write, will be removed in a future version */ - }; /* typedef'd to XXH64_state_t */ +/*! + * @internal + * @brief Structure for XXH32 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH32_state_t. + * Do not access the members of this struct directly. + * @see XXH64_state_s, XXH3_state_s + */ +struct XXH32_state_s { + XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ + XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ + XXH32_hash_t v[4]; /*!< Accumulator lanes */ + XXH32_hash_t mem32[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ + XXH32_hash_t memsize; /*!< Amount of data in @ref mem32 */ + XXH32_hash_t reserved; /*!< Reserved field. Do not read nor write to it. */ +}; /* typedef'd to XXH32_state_t */ -# ifdef XXH_PRIVATE_API -# include "xxhash.c" /* include xxhash functions as `static`, for inlining */ +#ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ + +/*! + * @internal + * @brief Structure for XXH64 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH64_state_t. + * Do not access the members of this struct directly. + * @see XXH32_state_s, XXH3_state_s + */ +struct XXH64_state_s { + XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ + XXH64_hash_t v[4]; /*!< Accumulator lanes */ + XXH64_hash_t mem64[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ + XXH32_hash_t memsize; /*!< Amount of data in @ref mem64 */ + XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ + XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it. */ +}; /* typedef'd to XXH64_state_t */ + + +#ifndef XXH_NO_XXH3 + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ +# include <stdalign.h> +# define XXH_ALIGN(n) alignas(n) +#elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */ +/* In C++ alignas() is a keyword */ +# define XXH_ALIGN(n) alignas(n) +#elif defined(__GNUC__) +# define XXH_ALIGN(n) __attribute__ ((aligned(n))) +#elif defined(_MSC_VER) +# define XXH_ALIGN(n) __declspec(align(n)) +#else +# define XXH_ALIGN(n) /* disabled */ +#endif + +/* Old GCC versions only accept the attribute after the type in structures. */ +#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ + && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ + && defined(__GNUC__) +# define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) +#else +# define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type +#endif + +/*! + * @brief The size of the internal XXH3 buffer. + * + * This is the optimal update size for incremental hashing. + * + * @see XXH3_64b_update(), XXH3_128b_update(). + */ +#define XXH3_INTERNALBUFFER_SIZE 256 + +/*! + * @brief Default size of the secret buffer (and @ref XXH3_kSecret). + * + * This is the size used in @ref XXH3_kSecret and the seeded functions. + * + * Not to be confused with @ref XXH3_SECRET_SIZE_MIN. + */ +#define XXH3_SECRET_DEFAULT_SIZE 192 + +/*! + * @internal + * @brief Structure for XXH3 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. + * Otherwise it is an opaque type. + * Never use this definition in combination with dynamic library. + * This allows fields to safely be changed in the future. + * + * @note ** This structure has a strict alignment requirement of 64 bytes!! ** + * Do not allocate this with `malloc()` or `new`, + * it will not be sufficiently aligned. + * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation. + * + * Typedef'd to @ref XXH3_state_t. + * Do never access the members of this struct directly. + * + * @see XXH3_INITSTATE() for stack initialization. + * @see XXH3_createState(), XXH3_freeState(). + * @see XXH32_state_s, XXH64_state_s + */ +struct XXH3_state_s { + XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); + /*!< The 8 accumulators. Similar to `vN` in @ref XXH32_state_s::v1 and @ref XXH64_state_s */ + XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); + /*!< Used to store a custom secret generated from a seed. */ + XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); + /*!< The internal buffer. @see XXH32_state_s::mem32 */ + XXH32_hash_t bufferedSize; + /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ + XXH32_hash_t useSeed; + /*!< Reserved field. Needed for padding on 64-bit. */ + size_t nbStripesSoFar; + /*!< Number or stripes processed. */ + XXH64_hash_t totalLen; + /*!< Total length hashed. 64-bit even on 32-bit targets. */ + size_t nbStripesPerBlock; + /*!< Number of stripes per block. */ + size_t secretLimit; + /*!< Size of @ref customSecret or @ref extSecret */ + XXH64_hash_t seed; + /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ + XXH64_hash_t reserved64; + /*!< Reserved field. */ + const unsigned char* extSecret; + /*!< Reference to an external secret for the _withSecret variants, NULL + * for other variants. */ + /* note: there may be some padding at the end due to alignment on 64 bytes */ +}; /* typedef'd to XXH3_state_t */ + +#undef XXH_ALIGN_MEMBER + +/*! + * @brief Initializes a stack-allocated `XXH3_state_s`. + * + * When the @ref XXH3_state_t structure is merely emplaced on stack, + * it should be initialized with XXH3_INITSTATE() or a memset() + * in case its first reset uses XXH3_NNbits_reset_withSeed(). + * This init can be omitted if the first reset uses default or _withSecret mode. + * This operation isn't necessary when the state is created with XXH3_createState(). + * Note that this doesn't prepare the state for a streaming operation, + * it's still necessary to use XXH3_NNbits_reset*() afterwards. + */ +#define XXH3_INITSTATE(XXH3_state_ptr) { (XXH3_state_ptr)->seed = 0; } + + +/* XXH128() : + * simple alias to pre-selected XXH3_128bits variant + */ +XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); + + +/* === Experimental API === */ +/* Symbols defined below must be considered tied to a specific library version. */ + +/* + * XXH3_generateSecret(): + * + * Derive a high-entropy secret from any user-defined content, named customSeed. + * The generated secret can be used in combination with `*_withSecret()` functions. + * The `_withSecret()` variants are useful to provide a higher level of protection than 64-bit seed, + * as it becomes much more difficult for an external actor to guess how to impact the calculation logic. + * + * The function accepts as input a custom seed of any length and any content, + * and derives from it a high-entropy secret of length @secretSize + * into an already allocated buffer @secretBuffer. + * @secretSize must be >= XXH3_SECRET_SIZE_MIN + * + * The generated secret can then be used with any `*_withSecret()` variant. + * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`, + * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()` + * are part of this list. They all accept a `secret` parameter + * which must be large enough for implementation reasons (>= XXH3_SECRET_SIZE_MIN) + * _and_ feature very high entropy (consist of random-looking bytes). + * These conditions can be a high bar to meet, so + * XXH3_generateSecret() can be employed to ensure proper quality. + * + * customSeed can be anything. It can have any size, even small ones, + * and its content can be anything, even "poor entropy" sources such as a bunch of zeroes. + * The resulting `secret` will nonetheless provide all required qualities. + * + * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize); + + +/* + * XXH3_generateSecret_fromSeed(): + * + * Generate the same secret as the _withSeed() variants. + * + * The resulting secret has a length of XXH3_SECRET_DEFAULT_SIZE (necessarily). + * @secretBuffer must be already allocated, of size at least XXH3_SECRET_DEFAULT_SIZE bytes. + * + * The generated secret can be used in combination with + *`*_withSecret()` and `_withSecretandSeed()` variants. + * This generator is notably useful in combination with `_withSecretandSeed()`, + * as a way to emulate a faster `_withSeed()` variant. + */ +XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_t seed); + +/* + * *_withSecretandSeed() : + * These variants generate hash values using either + * @seed for "short" keys (< XXH3_MIDSIZE_MAX = 240 bytes) + * or @secret for "large" keys (>= XXH3_MIDSIZE_MAX). + * + * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`. + * `_withSeed()` has to generate the secret on the fly for "large" keys. + * It's fast, but can be perceptible for "not so large" keys (< 1 KB). + * `_withSecret()` has to generate the masks on the fly for "small" keys, + * which requires more instructions than _withSeed() variants. + * Therefore, _withSecretandSeed variant combines the best of both worlds. + * + * When @secret has been generated by XXH3_generateSecret_fromSeed(), + * this variant produces *exactly* the same results as `_withSeed()` variant, + * hence offering only a pure speed benefit on "large" input, + * by skipping the need to regenerate the secret for every large input. + * + * Another usage scenario is to hash the secret to a 64-bit hash value, + * for example with XXH3_64bits(), which then becomes the seed, + * and then employ both the seed and the secret in _withSecretandSeed(). + * On top of speed, an added benefit is that each bit in the secret + * has a 50% chance to swap each bit in the output, + * via its impact to the seed. + * This is not guaranteed when using the secret directly in "small data" scenarios, + * because only portions of the secret are employed for small data. + */ +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecretandSeed(const void* data, size_t len, + const void* secret, size_t secretSize, + XXH64_hash_t seed); + +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecretandSeed(const void* data, size_t len, + const void* secret, size_t secretSize, + XXH64_hash_t seed64); + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, + const void* secret, size_t secretSize, + XXH64_hash_t seed64); + +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, + const void* secret, size_t secretSize, + XXH64_hash_t seed64); + + +#endif /* XXH_NO_XXH3 */ +#endif /* XXH_NO_LONG_LONG */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# define XXH_IMPLEMENTATION +#endif + +#endif /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */ + + +/* ======================================================================== */ +/* ======================================================================== */ +/* ======================================================================== */ + + +/*-********************************************************************** + * xxHash implementation + *-********************************************************************** + * xxHash's implementation used to be hosted inside xxhash.c. + * + * However, inlining requires implementation to be visible to the compiler, + * hence be included alongside the header. + * Previously, implementation was hosted inside xxhash.c, + * which was then #included when inlining was activated. + * This construction created issues with a few build and install systems, + * as it required xxhash.c to be stored in /include directory. + * + * xxHash implementation is now directly integrated within xxhash.h. + * As a consequence, xxhash.c is no longer needed in /include. + * + * xxhash.c is still available and is still useful. + * In a "normal" setup, when xxhash is not inlined, + * xxhash.h only exposes the prototypes and public symbols, + * while xxhash.c can be built into an object file xxhash.o + * which can then be linked into the final binary. + ************************************************************************/ + +#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \ + || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387) +# define XXH_IMPLEM_13a8737387 + +/* ************************************* +* Tuning parameters +***************************************/ + +/*! + * @defgroup tuning Tuning parameters + * @{ + * + * Various macros to control xxHash's behavior. + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Define this to disable 64-bit code. + * + * Useful if only using the @ref xxh32_family and you have a strict C90 compiler. + */ +# define XXH_NO_LONG_LONG +# undef XXH_NO_LONG_LONG /* don't actually */ +/*! + * @brief Controls how unaligned memory is accessed. + * + * By default, access to unaligned memory is controlled by `memcpy()`, which is + * safe and portable. + * + * Unfortunately, on some target/compiler combinations, the generated assembly + * is sub-optimal. + * + * The below switch allow selection of a different access method + * in the search for improved performance. + * + * @par Possible options: + * + * - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy` + * @par + * Use `memcpy()`. Safe and portable. Note that most modern compilers will + * eliminate the function call and treat it as an unaligned access. + * + * - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((packed))` + * @par + * Depends on compiler extensions and is therefore not portable. + * This method is safe _if_ your compiler supports it, + * and *generally* as fast or faster than `memcpy`. + * + * - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast + * @par + * Casts directly and dereferences. This method doesn't depend on the + * compiler, but it violates the C standard as it directly dereferences an + * unaligned pointer. It can generate buggy code on targets which do not + * support unaligned memory accesses, but in some circumstances, it's the + * only known way to get the most performance. + * + * - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift + * @par + * Also portable. This can generate the best code on old compilers which don't + * inline small `memcpy()` calls, and it might also be faster on big-endian + * systems which lack a native byteswap instruction. However, some compilers + * will emit literal byteshifts even if the target supports unaligned access. + * . + * + * @warning + * Methods 1 and 2 rely on implementation-defined behavior. Use these with + * care, as what works on one compiler/platform/optimization level may cause + * another to read garbage data or even crash. + * + * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. + * + * Prefer these methods in priority order (0 > 3 > 1 > 2) + */ +# define XXH_FORCE_MEMORY_ACCESS 0 + +/*! + * @def XXH_FORCE_ALIGN_CHECK + * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32() + * and XXH64() only). + * + * This is an important performance trick for architectures without decent + * unaligned memory access performance. + * + * It checks for input alignment, and when conditions are met, uses a "fast + * path" employing direct 32-bit/64-bit reads, resulting in _dramatically + * faster_ read speed. + * + * The check costs one initial branch per hash, which is generally negligible, + * but not zero. + * + * Moreover, it's not useful to generate an additional code path if memory + * access uses the same instruction for both aligned and unaligned + * addresses (e.g. x86 and aarch64). + * + * In these cases, the alignment check can be removed by setting this macro to 0. + * Then the code will always use unaligned memory access. + * Align check is automatically disabled on x86, x64 & arm64, + * which are platforms known to offer good unaligned memory accesses performance. + * + * This option does not affect XXH3 (only XXH32 and XXH64). + */ +# define XXH_FORCE_ALIGN_CHECK 0 + +/*! + * @def XXH_NO_INLINE_HINTS + * @brief When non-zero, sets all functions to `static`. + * + * By default, xxHash tries to force the compiler to inline almost all internal + * functions. + * + * This can usually improve performance due to reduced jumping and improved + * constant folding, but significantly increases the size of the binary which + * might not be favorable. + * + * Additionally, sometimes the forced inlining can be detrimental to performance, + * depending on the architecture. + * + * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the + * compiler full control on whether to inline or not. + * + * When not optimizing (-O0), optimizing for size (-Os, -Oz), or using + * -fno-inline with GCC or Clang, this will automatically be defined. + */ +# define XXH_NO_INLINE_HINTS 0 + +/*! + * @def XXH32_ENDJMP + * @brief Whether to use a jump for `XXH32_finalize`. + * + * For performance, `XXH32_finalize` uses multiple branches in the finalizer. + * This is generally preferable for performance, + * but depending on exact architecture, a jmp may be preferable. + * + * This setting is only possibly making a difference for very small inputs. + */ +# define XXH32_ENDJMP 0 + +/*! + * @internal + * @brief Redefines old internal names. + * + * For compatibility with code that uses xxHash's internals before the names + * were changed to improve namespacing. There is no other reason to use this. + */ +# define XXH_OLD_NAMES +# undef XXH_OLD_NAMES /* don't actually use, it is ugly. */ +#endif /* XXH_DOXYGEN */ +/*! + * @} + */ + +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ + /* prefer __packed__ structures (method 1) for gcc on armv7+ and mips */ +# if !defined(__clang__) && \ +( \ + (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + ( \ + defined(__GNUC__) && ( \ + (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || \ + ( \ + defined(__mips__) && \ + (__mips <= 5 || __mips_isa_rev < 6) && \ + (!defined(__mips16) || defined(__mips_mips16e2)) \ + ) \ + ) \ + ) \ +) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \ + || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */ +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + +#ifndef XXH_NO_INLINE_HINTS +# if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \ + || defined(__NO_INLINE__) /* -O0, -fno-inline */ +# define XXH_NO_INLINE_HINTS 1 +# else +# define XXH_NO_INLINE_HINTS 0 +# endif +#endif + +#ifndef XXH32_ENDJMP +/* generally preferable for performance */ +# define XXH32_ENDJMP 0 +#endif + +/*! + * @defgroup impl Implementation + * @{ + */ + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/* Modify the local functions below should you wish to use some other memory routines */ +/* for ZSTD_malloc(), ZSTD_free() */ +#define ZSTD_DEPS_NEED_MALLOC +#include "zstd_deps.h" /* size_t, ZSTD_malloc, ZSTD_free, ZSTD_memcpy */ +static void* XXH_malloc(size_t s) { return ZSTD_malloc(s); } +static void XXH_free (void* p) { ZSTD_free(p); } +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_memcpy(dest,src,size); } + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio warning fix */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + +#if XXH_NO_INLINE_HINTS /* disable inlining hints */ +# if defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __attribute__((unused)) +# else +# define XXH_FORCE_INLINE static +# endif +# define XXH_NO_INLINE static +/* enable inlining hints */ +#elif defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused)) +# define XXH_NO_INLINE static __attribute__((noinline)) +#elif defined(_MSC_VER) /* Visual Studio */ +# define XXH_FORCE_INLINE static __forceinline +# define XXH_NO_INLINE static __declspec(noinline) +#elif defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */ +# define XXH_FORCE_INLINE static inline +# define XXH_NO_INLINE static +#else +# define XXH_FORCE_INLINE static +# define XXH_NO_INLINE static +#endif + + + +/* ************************************* +* Debug +***************************************/ +/*! + * @ingroup tuning + * @def XXH_DEBUGLEVEL + * @brief Sets the debugging level. + * + * XXH_DEBUGLEVEL is expected to be defined externally, typically via the + * compiler's command line options. The value must be a number. + */ +#ifndef XXH_DEBUGLEVEL +# ifdef DEBUGLEVEL /* backwards compat */ +# define XXH_DEBUGLEVEL DEBUGLEVEL +# else +# define XXH_DEBUGLEVEL 0 +# endif +#endif + +#if (XXH_DEBUGLEVEL>=1) +# include <assert.h> /* note: can still be disabled with NDEBUG */ +# define XXH_ASSERT(c) assert(c) +#else +# define XXH_ASSERT(c) ((void)0) +#endif + +/* note: use after variable declarations */ +#ifndef XXH_STATIC_ASSERT +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ +# include <assert.h> +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) +# elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) +# else +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) +# endif +# define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) +#endif + +/*! + * @internal + * @def XXH_COMPILER_GUARD(var) + * @brief Used to prevent unwanted optimizations for @p var. + * + * It uses an empty GCC inline assembly statement with a register constraint + * which forces @p var into a general purpose register (e.g. eax, ebx, ecx + * on x86) and marks it as modified. + * + * This is used in a few places to avoid unwanted autovectorization (e.g. + * XXH32_round()). All vectorization we want is explicit via intrinsics, + * and _usually_ isn't wanted elsewhere. + * + * We also use it to prevent unwanted constant folding for AArch64 in + * XXH3_initCustomSecret_scalar(). + */ +#if defined(__GNUC__) || defined(__clang__) +# define XXH_COMPILER_GUARD(var) __asm__ __volatile__("" : "+r" (var)) +#else +# define XXH_COMPILER_GUARD(var) ((void)0) +#endif + +/* ************************************* +* Basic Types +***************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include <stdint.h> + typedef uint8_t xxh_u8; +#else + typedef unsigned char xxh_u8; +#endif +typedef XXH32_hash_t xxh_u32; + +#ifdef XXH_OLD_NAMES +# define BYTE xxh_u8 +# define U8 xxh_u8 +# define U32 xxh_u32 +#endif + +/* *** Memory access *** */ + +/*! + * @internal + * @fn xxh_u32 XXH_read32(const void* ptr) + * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit native endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32(const void* ptr) + * @brief Reads an unaligned 32-bit little endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readBE32(const void* ptr) + * @brief Reads an unaligned 32-bit big endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit big endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) + * @brief Like @ref XXH_readLE32(), but has an option for aligned reads. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is + * always @ref XXH_alignment::XXH_unaligned. + * + * @param ptr The pointer to read from. + * @param align Whether @p ptr is aligned. + * @pre + * If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte + * aligned. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +/* + * Manual byteshift. Best for old compilers which don't inline memcpy. + * We actually directly use XXH_readLE32 and XXH_readBE32. + */ +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* + * Force direct memory access. Only works on CPU which support unaligned memory + * access in hardware. + */ +static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* + * __pack instructions are safer but compiler specific, hence potentially + * problematic for some compilers. + * + * Currently only defined for GCC and ICC. + */ +#ifdef XXH_OLD_NAMES +typedef union { xxh_u32 u32; } __attribute__((packed)) unalign; +#endif +static xxh_u32 XXH_read32(const void* ptr) +{ + typedef union { xxh_u32 u32; } __attribute__((packed)) xxh_unalign; + return ((const xxh_unalign*)ptr)->u32; +} + +#else + +/* + * Portable and safe solution. Generally efficient. + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html + */ +static xxh_u32 XXH_read32(const void* memPtr) +{ + xxh_u32 val; + XXH_memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* *** Endianness *** */ + +/*! + * @ingroup tuning + * @def XXH_CPU_LITTLE_ENDIAN + * @brief Whether the target is little endian. + * + * Defined to 1 if the target is little endian, or 0 if it is big endian. + * It can be defined externally, for example on the compiler command line. + * + * If it is not defined, + * a runtime check (which is usually constant folded) is used instead. + * + * @note + * This is not necessarily defined to an integer constant. + * + * @see XXH_isLittleEndian() for the runtime check. + */ +#ifndef XXH_CPU_LITTLE_ENDIAN +/* + * Try to detect endianness automatically, to avoid the nonstandard behavior + * in `XXH_isLittleEndian()` + */ +# if defined(_WIN32) /* Windows is always little endian */ \ + || defined(__LITTLE_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define XXH_CPU_LITTLE_ENDIAN 1 +# elif defined(__BIG_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define XXH_CPU_LITTLE_ENDIAN 0 +# else +/*! + * @internal + * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN. + * + * Most compilers will constant fold this. + */ +static int XXH_isLittleEndian(void) +{ + /* + * Portable and well-defined behavior. + * Don't use static: it is detrimental to performance. + */ + const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 }; + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +# endif +#endif + + + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +#ifdef __has_builtin +# define XXH_HAS_BUILTIN(x) __has_builtin(x) +#else +# define XXH_HAS_BUILTIN(x) 0 +#endif + +/*! + * @internal + * @def XXH_rotl32(x,r) + * @brief 32-bit rotate left. + * + * @param x The 32-bit integer to be rotated. + * @param r The number of bits to rotate. + * @pre + * @p r > 0 && @p r < 32 + * @note + * @p x and @p r may be evaluated multiple times. + * @return The rotated result. + */ +#if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ + && XXH_HAS_BUILTIN(__builtin_rotateleft64) +# define XXH_rotl32 __builtin_rotateleft32 +# define XXH_rotl64 __builtin_rotateleft64 +/* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */ +#elif defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) +#endif + +/*! + * @internal + * @fn xxh_u32 XXH_swap32(xxh_u32 x) + * @brief A 32-bit byteswap. + * + * @param x The 32-bit integer to byteswap. + * @return @p x, byteswapped. + */ +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static xxh_u32 XXH_swap32 (xxh_u32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* *************************** +* Memory reads +*****************************/ + +/*! + * @internal + * @brief Enum to indicate whether a pointer is aligned. + */ +typedef enum { + XXH_aligned, /*!< Aligned */ + XXH_unaligned /*!< Possibly unaligned */ +} XXH_alignment; + +/* + * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. + * + * This is ideal for older compilers which don't inline memcpy. + */ +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) + +XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[0] + | ((xxh_u32)bytePtr[1] << 8) + | ((xxh_u32)bytePtr[2] << 16) + | ((xxh_u32)bytePtr[3] << 24); +} + +XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[3] + | ((xxh_u32)bytePtr[2] << 8) + | ((xxh_u32)bytePtr[1] << 16) + | ((xxh_u32)bytePtr[0] << 24); +} + +#else +XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); +} + +static xxh_u32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} +#endif + +XXH_FORCE_INLINE xxh_u32 +XXH_readLE32_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) { + return XXH_readLE32(ptr); + } else { + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr); + } +} + + +/* ************************************* +* Misc +***************************************/ +/*! @ingroup public */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +/*! + * @} + * @defgroup xxh32_impl XXH32 implementation + * @ingroup impl + * @{ + */ + /* #define instead of static const, to be used as initializers */ +#define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ +#define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ +#define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ +#define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */ +#define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */ + +#ifdef XXH_OLD_NAMES +# define PRIME32_1 XXH_PRIME32_1 +# define PRIME32_2 XXH_PRIME32_2 +# define PRIME32_3 XXH_PRIME32_3 +# define PRIME32_4 XXH_PRIME32_4 +# define PRIME32_5 XXH_PRIME32_5 +#endif + +/*! + * @internal + * @brief Normal stripe processing routine. + * + * This shuffles the bits so that any bit from @p input impacts several bits in + * @p acc. + * + * @param acc The accumulator lane. + * @param input The stripe of input to mix. + * @return The mixed accumulator lane. + */ +static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) +{ + acc += input * XXH_PRIME32_2; + acc = XXH_rotl32(acc, 13); + acc *= XXH_PRIME32_1; +#if (defined(__SSE4_1__) || defined(__aarch64__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) + /* + * UGLY HACK: + * A compiler fence is the only thing that prevents GCC and Clang from + * autovectorizing the XXH32 loop (pragmas and attributes don't work for some + * reason) without globally disabling SSE4.1. + * + * The reason we want to avoid vectorization is because despite working on + * 4 integers at a time, there are multiple factors slowing XXH32 down on + * SSE4: + * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on + * newer chips!) making it slightly slower to multiply four integers at + * once compared to four integers independently. Even when pmulld was + * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE + * just to multiply unless doing a long operation. + * + * - Four instructions are required to rotate, + * movqda tmp, v // not required with VEX encoding + * pslld tmp, 13 // tmp <<= 13 + * psrld v, 19 // x >>= 19 + * por v, tmp // x |= tmp + * compared to one for scalar: + * roll v, 13 // reliably fast across the board + * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason + * + * - Instruction level parallelism is actually more beneficial here because + * the SIMD actually serializes this operation: While v1 is rotating, v2 + * can load data, while v3 can multiply. SSE forces them to operate + * together. + * + * This is also enabled on AArch64, as Clang autovectorizes it incorrectly + * and it is pointless writing a NEON implementation that is basically the + * same speed as scalar for XXH32. + */ + XXH_COMPILER_GUARD(acc); +#endif + return acc; +} + +/*! + * @internal + * @brief Mixes all bits to finalize the hash. + * + * The final mix ensures that all input bits have a chance to impact any bit in + * the output digest, resulting in an unbiased distribution. + * + * @param h32 The hash to avalanche. + * @return The avalanched hash. + */ +static xxh_u32 XXH32_avalanche(xxh_u32 h32) +{ + h32 ^= h32 >> 15; + h32 *= XXH_PRIME32_2; + h32 ^= h32 >> 13; + h32 *= XXH_PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, align) + +/*! + * @internal + * @brief Processes the last 0-15 bytes of @p ptr. + * + * There may be up to 15 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param h32 The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 16. + * @param align Whether @p ptr is aligned. + * @return The finalized hash. + */ +static xxh_u32 +XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ +#define XXH_PROCESS1 do { \ + h32 += (*ptr++) * XXH_PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; \ +} while (0) + +#define XXH_PROCESS4 do { \ + h32 += XXH_get32bits(ptr) * XXH_PRIME32_3; \ + ptr += 4; \ + h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; \ +} while (0) + + if (ptr==NULL) XXH_ASSERT(len == 0); + + /* Compact rerolled version; generally faster */ + if (!XXH32_ENDJMP) { + len &= 15; + while (len >= 4) { + XXH_PROCESS4; + len -= 4; + } + while (len > 0) { + XXH_PROCESS1; + --len; + } + return XXH32_avalanche(h32); + } else { + switch(len&15) /* or switch(bEnd - p) */ { + case 12: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 8: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 4: XXH_PROCESS4; + return XXH32_avalanche(h32); + + case 13: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 9: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 5: XXH_PROCESS4; + XXH_PROCESS1; + return XXH32_avalanche(h32); + + case 14: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 10: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 6: XXH_PROCESS4; + XXH_PROCESS1; + XXH_PROCESS1; + return XXH32_avalanche(h32); + + case 15: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 11: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 7: XXH_PROCESS4; + XXH_FALLTHROUGH; + case 3: XXH_PROCESS1; + XXH_FALLTHROUGH; + case 2: XXH_PROCESS1; + XXH_FALLTHROUGH; + case 1: XXH_PROCESS1; + XXH_FALLTHROUGH; + case 0: return XXH32_avalanche(h32); + } + XXH_ASSERT(0); + return h32; /* reaching this point is deemed impossible */ + } +} + +#ifdef XXH_OLD_NAMES +# define PROCESS1 XXH_PROCESS1 +# define PROCESS4 XXH_PROCESS4 +#else +# undef XXH_PROCESS1 +# undef XXH_PROCESS4 +#endif + +/*! + * @internal + * @brief The implementation for @ref XXH32(). + * + * @param input , len , seed Directly passed from @ref XXH32(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE xxh_u32 +XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) +{ + xxh_u32 h32; + + if (input==NULL) XXH_ASSERT(len == 0); + + if (len>=16) { + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 15; + xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + xxh_u32 v2 = seed + XXH_PRIME32_2; + xxh_u32 v3 = seed + 0; + xxh_u32 v4 = seed - XXH_PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; + v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4; + v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4; + v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4; + } while (input < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + XXH_PRIME32_5; + } + + h32 += (xxh_u32)len; + + return XXH32_finalize(h32, input, len&15, align); +} + +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, (const xxh_u8*)input, len); + return XXH32_digest(&state); +#else + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); + } } + + return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); +#endif +} + + + +/******* Hash streaming *******/ +/*! + * @ingroup xxh32_family + */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + XXH_memcpy(dstState, srcState, sizeof(*dstState)); +} + +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) +{ + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + statePtr->v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + statePtr->v[1] = seed + XXH_PRIME32_2; + statePtr->v[2] = seed + 0; + statePtr->v[3] = seed - XXH_PRIME32_1; + return XXH_OK; +} + + +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API XXH_errorcode +XXH32_update(XXH32_state_t* state, const void* input, size_t len) +{ + if (input==NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } + + { const xxh_u8* p = (const xxh_u8*)input; + const xxh_u8* const bEnd = p + len; + + state->total_len_32 += (XXH32_hash_t)len; + state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len); + state->memsize += (XXH32_hash_t)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const xxh_u32* p32 = state->mem32; + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const xxh_u8* const limit = bEnd - 16; + + do { + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4; + } while (p<=limit); + + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) +{ + xxh_u32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v[0], 1) + + XXH_rotl32(state->v[1], 7) + + XXH_rotl32(state->v[2], 12) + + XXH_rotl32(state->v[3], 18); + } else { + h32 = state->v[2] /* == seed */ + XXH_PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); +} + + +/******* Canonical representation *******/ + +/*! + * @ingroup xxh32_family + * The default return values from XXH functions are unsigned 32 and 64 bit + * integers. + * + * The canonical representation uses big endian convention, the same convention + * as human-readable numbers (large digits first). + * + * This way, hash values can be written into a file or buffer, remaining + * comparable across different systems. + * + * The following functions allow transformation of hash values to and from their + * canonical format. + */ +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + /* XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); */ + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + XXH_memcpy(dst, &hash, sizeof(*dst)); +} +/*! @ingroup xxh32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ +/*! + * @} + * @ingroup impl + * @{ + */ +/******* Memory access *******/ + +typedef XXH64_hash_t xxh_u64; + +#ifdef XXH_OLD_NAMES +# define U64 xxh_u64 +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +/* + * Manual byteshift. Best for old compilers which don't inline memcpy. + * We actually directly use XXH_readLE64 and XXH_readBE64. + */ +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static xxh_u64 XXH_read64(const void* memPtr) +{ + return *(const xxh_u64*) memPtr; +} + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* + * __pack instructions are safer, but compiler specific, hence potentially + * problematic for some compilers. + * + * Currently only defined for GCC and ICC. + */ +#ifdef XXH_OLD_NAMES +typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64; +#endif +static xxh_u64 XXH_read64(const void* ptr) +{ + typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) xxh_unalign64; + return ((const xxh_unalign64*)ptr)->u64; +} + +#else + +/* + * Portable and safe solution. Generally efficient. + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html + */ +static xxh_u64 XXH_read64(const void* memPtr) +{ + xxh_u64 val; + XXH_memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static xxh_u64 XXH_swap64(xxh_u64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + + +/* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */ +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) + +XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[0] + | ((xxh_u64)bytePtr[1] << 8) + | ((xxh_u64)bytePtr[2] << 16) + | ((xxh_u64)bytePtr[3] << 24) + | ((xxh_u64)bytePtr[4] << 32) + | ((xxh_u64)bytePtr[5] << 40) + | ((xxh_u64)bytePtr[6] << 48) + | ((xxh_u64)bytePtr[7] << 56); +} + +XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[7] + | ((xxh_u64)bytePtr[6] << 8) + | ((xxh_u64)bytePtr[5] << 16) + | ((xxh_u64)bytePtr[4] << 24) + | ((xxh_u64)bytePtr[3] << 32) + | ((xxh_u64)bytePtr[2] << 40) + | ((xxh_u64)bytePtr[1] << 48) + | ((xxh_u64)bytePtr[0] << 56); +} + +#else +XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); +} + +static xxh_u64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} +#endif + +XXH_FORCE_INLINE xxh_u64 +XXH_readLE64_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) + return XXH_readLE64(ptr); + else + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr); +} + + +/******* xxh64 *******/ +/*! + * @} + * @defgroup xxh64_impl XXH64 implementation + * @ingroup impl + * @{ + */ +/* #define rather that static const, to be used as initializers */ +#define XXH_PRIME64_1 0x9E3779B185EBCA87ULL /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */ +#define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */ +#define XXH_PRIME64_3 0x165667B19E3779F9ULL /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */ +#define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ +#define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ + +#ifdef XXH_OLD_NAMES +# define PRIME64_1 XXH_PRIME64_1 +# define PRIME64_2 XXH_PRIME64_2 +# define PRIME64_3 XXH_PRIME64_3 +# define PRIME64_4 XXH_PRIME64_4 +# define PRIME64_5 XXH_PRIME64_5 +#endif + +static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) +{ + acc += input * XXH_PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= XXH_PRIME64_1; + return acc; +} + +static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; + return acc; +} + +static xxh_u64 XXH64_avalanche(xxh_u64 h64) +{ + h64 ^= h64 >> 33; + h64 *= XXH_PRIME64_2; + h64 ^= h64 >> 29; + h64 *= XXH_PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, align) + +static xxh_u64 +XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ + if (ptr==NULL) XXH_ASSERT(len == 0); + len &= 31; + while (len >= 8) { + xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); + ptr += 8; + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4; + len -= 8; + } + if (len >= 4) { + h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; + ptr += 4; + h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + len -= 4; + } + while (len > 0) { + h64 ^= (*ptr++) * XXH_PRIME64_5; + h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; + --len; + } + return XXH64_avalanche(h64); +} + +#ifdef XXH_OLD_NAMES +# define PROCESS1_64 XXH_PROCESS1_64 +# define PROCESS4_64 XXH_PROCESS4_64 +# define PROCESS8_64 XXH_PROCESS8_64 +#else +# undef XXH_PROCESS1_64 +# undef XXH_PROCESS4_64 +# undef XXH_PROCESS8_64 +#endif + +XXH_FORCE_INLINE xxh_u64 +XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) +{ + xxh_u64 h64; + if (input==NULL) XXH_ASSERT(len == 0); + + if (len>=32) { + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 31; + xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + xxh_u64 v2 = seed + XXH_PRIME64_2; + xxh_u64 v3 = seed + 0; + xxh_u64 v4 = seed - XXH_PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8; + v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; + v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; + v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; + } while (input<limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + XXH_PRIME64_5; + } + + h64 += (xxh_u64) len; + + return XXH64_finalize(h64, input, len, align); +} + + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, (const xxh_u8*)input, len); + return XXH64_digest(&state); +#else + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); + } } + + return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); + +#endif +} + +/******* Hash Streaming *******/ + +/*! @ingroup xxh64_family*/ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + XXH_memcpy(dstState, srcState, sizeof(*dstState)); +} + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, XXH64_hash_t seed) +{ + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + statePtr->v[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + statePtr->v[1] = seed + XXH_PRIME64_2; + statePtr->v[2] = seed + 0; + statePtr->v[3] = seed - XXH_PRIME64_1; + return XXH_OK; +} + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API XXH_errorcode +XXH64_update (XXH64_state_t* state, const void* input, size_t len) +{ + if (input==NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } + + { const xxh_u8* p = (const xxh_u8*)input; + const xxh_u8* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len); + state->memsize += (xxh_u32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0)); + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1)); + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2)); + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3)); + p += 32 - state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const xxh_u8* const limit = bEnd - 32; + + do { + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8; + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8; + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8; + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8; + } while (p<=limit); + + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_digest(const XXH64_state_t* state) +{ + xxh_u64 h64; + + if (state->total_len >= 32) { + h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18); + h64 = XXH64_mergeRound(h64, state->v[0]); + h64 = XXH64_mergeRound(h64, state->v[1]); + h64 = XXH64_mergeRound(h64, state->v[2]); + h64 = XXH64_mergeRound(h64, state->v[3]); + } else { + h64 = state->v[2] /*seed*/ + XXH_PRIME64_5; + } + + h64 += (xxh_u64) state->total_len; + + return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); +} + + +/******* Canonical representation *******/ + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + /* XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); */ + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + XXH_memcpy(dst, &hash, sizeof(*dst)); +} + +/*! @ingroup xxh64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#ifndef XXH_NO_XXH3 + +/* ********************************************************************* +* XXH3 +* New generation hash designed for speed on small keys and vectorization +************************************************************************ */ +/*! + * @} + * @defgroup xxh3_impl XXH3 implementation + * @ingroup impl + * @{ + */ + +/* === Compiler specifics === */ + +#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */ +# define XXH_RESTRICT /* disable */ +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ +# define XXH_RESTRICT restrict +#else +/* Note: it might be useful to define __restrict or __restrict__ for some C++ compilers */ +# define XXH_RESTRICT /* disable */ +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) \ + || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ + || defined(__clang__) +# define XXH_likely(x) __builtin_expect(x, 1) +# define XXH_unlikely(x) __builtin_expect(x, 0) +#else +# define XXH_likely(x) (x) +# define XXH_unlikely(x) (x) +#endif + +#if defined(__GNUC__) || defined(__clang__) +# if defined(__ARM_NEON__) || defined(__ARM_NEON) \ + || defined(__aarch64__) || defined(_M_ARM) \ + || defined(_M_ARM64) || defined(_M_ARM64EC) +# define inline __inline__ /* circumvent a clang bug */ +# include <arm_neon.h> +# undef inline +# elif defined(__AVX2__) +# include <immintrin.h> +# elif defined(__SSE2__) +# include <emmintrin.h> +# endif +#endif + +#if defined(_MSC_VER) +# include <intrin.h> +#endif + +/* + * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while + * remaining a true 64-bit/128-bit hash function. + * + * This is done by prioritizing a subset of 64-bit operations that can be + * emulated without too many steps on the average 32-bit machine. + * + * For example, these two lines seem similar, and run equally fast on 64-bit: + * + * xxh_u64 x; + * x ^= (x >> 47); // good + * x ^= (x >> 13); // bad + * + * However, to a 32-bit machine, there is a major difference. + * + * x ^= (x >> 47) looks like this: + * + * x.lo ^= (x.hi >> (47 - 32)); + * + * while x ^= (x >> 13) looks like this: + * + * // note: funnel shifts are not usually cheap. + * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13)); + * x.hi ^= (x.hi >> 13); + * + * The first one is significantly faster than the second, simply because the + * shift is larger than 32. This means: + * - All the bits we need are in the upper 32 bits, so we can ignore the lower + * 32 bits in the shift. + * - The shift result will always fit in the lower 32 bits, and therefore, + * we can ignore the upper 32 bits in the xor. + * + * Thanks to this optimization, XXH3 only requires these features to be efficient: + * + * - Usable unaligned access + * - A 32-bit or 64-bit ALU + * - If 32-bit, a decent ADC instruction + * - A 32 or 64-bit multiply with a 64-bit result + * - For the 128-bit variant, a decent byteswap helps short inputs. + * + * The first two are already required by XXH32, and almost all 32-bit and 64-bit + * platforms which can run XXH32 can run XXH3 efficiently. + * + * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one + * notable exception. + * + * First of all, Thumb-1 lacks support for the UMULL instruction which + * performs the important long multiply. This means numerous __aeabi_lmul + * calls. + * + * Second of all, the 8 functional registers are just not enough. + * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need + * Lo registers, and this shuffling results in thousands more MOVs than A32. + * + * A32 and T32 don't have this limitation. They can access all 14 registers, + * do a 32->64 multiply with UMULL, and the flexible operand allowing free + * shifts is helpful, too. + * + * Therefore, we do a quick sanity check. + * + * If compiling Thumb-1 for a target which supports ARM instructions, we will + * emit a warning, as it is not a "sane" platform to compile for. + * + * Usually, if this happens, it is because of an accident and you probably need + * to specify -march, as you likely meant to compile for a newer architecture. + * + * Credit: large sections of the vectorial and asm source code paths + * have been contributed by @easyaspi314 + */ +#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM) +# warning "XXH3 is highly inefficient without ARM or Thumb-2." +#endif + +/* ========================================== + * Vectorization detection + * ========================================== */ + +#ifdef XXH_DOXYGEN +/*! + * @ingroup tuning + * @brief Overrides the vectorization implementation chosen for XXH3. + * + * Can be defined to 0 to disable SIMD or any of the values mentioned in + * @ref XXH_VECTOR_TYPE. + * + * If this is not defined, it uses predefined macros to determine the best + * implementation. + */ +# define XXH_VECTOR XXH_SCALAR +/*! + * @ingroup tuning + * @brief Possible values for @ref XXH_VECTOR. + * + * Note that these are actually implemented as macros. + * + * If this is not defined, it is detected automatically. + * @ref XXH_X86DISPATCH overrides this. + */ +enum XXH_VECTOR_TYPE /* fake enum */ { + XXH_SCALAR = 0, /*!< Portable scalar version */ + XXH_SSE2 = 1, /*!< + * SSE2 for Pentium 4, Opteron, all x86_64. + * + * @note SSE2 is also guaranteed on Windows 10, macOS, and + * Android x86. + */ + XXH_AVX2 = 2, /*!< AVX2 for Haswell and Bulldozer */ + XXH_AVX512 = 3, /*!< AVX512 for Skylake and Icelake */ + XXH_NEON = 4, /*!< NEON for most ARMv7-A and all AArch64 */ + XXH_VSX = 5, /*!< VSX and ZVector for POWER8/z13 (64-bit) */ +}; +/*! + * @ingroup tuning + * @brief Selects the minimum alignment for XXH3's accumulators. + * + * When using SIMD, this should match the alignment required for said vector + * type, so, for example, 32 for AVX2. + * + * Default: Auto detected. + */ +# define XXH_ACC_ALIGN 8 +#endif + +/* Actual definition */ +#ifndef XXH_DOXYGEN +# define XXH_SCALAR 0 +# define XXH_SSE2 1 +# define XXH_AVX2 2 +# define XXH_AVX512 3 +# define XXH_NEON 4 +# define XXH_VSX 5 +#endif + +#ifndef XXH_VECTOR /* can be defined on command line */ +# if ( \ + defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ + || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \ + ) && ( \ + defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ + ) +# define XXH_VECTOR XXH_NEON +# elif defined(__AVX512F__) +# define XXH_VECTOR XXH_AVX512 +# elif defined(__AVX2__) +# define XXH_VECTOR XXH_AVX2 +# elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) +# define XXH_VECTOR XXH_SSE2 +# elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ + || (defined(__s390x__) && defined(__VEC__)) \ + && defined(__GNUC__) /* TODO: IBM XL */ +# define XXH_VECTOR XXH_VSX +# else +# define XXH_VECTOR XXH_SCALAR +# endif +#endif + +/* + * Controls the alignment of the accumulator, + * for compatibility with aligned vector loads, which are usually faster. + */ +#ifndef XXH_ACC_ALIGN +# if defined(XXH_X86DISPATCH) +# define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */ +# elif XXH_VECTOR == XXH_SCALAR /* scalar */ +# define XXH_ACC_ALIGN 8 +# elif XXH_VECTOR == XXH_SSE2 /* sse2 */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == XXH_AVX2 /* avx2 */ +# define XXH_ACC_ALIGN 32 +# elif XXH_VECTOR == XXH_NEON /* neon */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == XXH_VSX /* vsx */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == XXH_AVX512 /* avx512 */ +# define XXH_ACC_ALIGN 64 +# endif +#endif + +#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ + || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 +# define XXH_SEC_ALIGN XXH_ACC_ALIGN +#else +# define XXH_SEC_ALIGN 8 +#endif + +/* + * UGLY HACK: + * GCC usually generates the best code with -O3 for xxHash. + * + * However, when targeting AVX2, it is overzealous in its unrolling resulting + * in code roughly 3/4 the speed of Clang. + * + * There are other issues, such as GCC splitting _mm256_loadu_si256 into + * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which + * only applies to Sandy and Ivy Bridge... which don't even support AVX2. + * + * That is why when compiling the AVX2 version, it is recommended to use either + * -O2 -mavx2 -march=haswell + * or + * -O2 -mavx2 -mno-avx256-split-unaligned-load + * for decent performance, or to use Clang instead. + * + * Fortunately, we can control the first one with a pragma that forces GCC into + * -O2, but the other one we can't control without "failed to inline always + * inline function due to target mismatch" warnings. + */ +#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ + && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ +# pragma GCC push_options +# pragma GCC optimize("-O2") +#endif + + +#if XXH_VECTOR == XXH_NEON +/* + * NEON's setup for vmlal_u32 is a little more complicated than it is on + * SSE2, AVX2, and VSX. + * + * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an upcast. + * + * To do the same operation, the 128-bit 'Q' register needs to be split into + * two 64-bit 'D' registers, performing this operation:: + * + * [ a | b ] + * | '---------. .--------' | + * | x | + * | .---------' '--------. | + * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ] + * + * Due to significant changes in aarch64, the fastest method for aarch64 is + * completely different than the fastest method for ARMv7-A. + * + * ARMv7-A treats D registers as unions overlaying Q registers, so modifying + * D11 will modify the high half of Q5. This is similar to how modifying AH + * will only affect bits 8-15 of AX on x86. + * + * VZIP takes two registers, and puts even lanes in one register and odd lanes + * in the other. + * + * On ARMv7-A, this strangely modifies both parameters in place instead of + * taking the usual 3-operand form. + * + * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the + * lower and upper halves of the Q register to end up with the high and low + * halves where we want - all in one instruction. + * + * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], d11[1] } + * + * Unfortunately we need inline assembly for this: Instructions modifying two + * registers at once is not possible in GCC or Clang's IR, and they have to + * create a copy. + * + * aarch64 requires a different approach. + * + * In order to make it easier to write a decent compiler for aarch64, many + * quirks were removed, such as conditional execution. + * + * NEON was also affected by this. + * + * aarch64 cannot access the high bits of a Q-form register, and writes to a + * D-form register zero the high bits, similar to how writes to W-form scalar + * registers (or DWORD registers on x86_64) work. + * + * The formerly free vget_high intrinsics now require a vext (with a few + * exceptions) + * + * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent + * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one + * operand. + * + * The equivalent of the VZIP.32 on the lower and upper halves would be this + * mess: + * + * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] } + * zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] } + * zip2 v0.2s, v0.2s, v1.2s // v0 = { v0[1], v2[1] } + * + * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 (SHRN): + * + * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32); + * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF); + * + * This is available on ARMv7-A, but is less efficient than a single VZIP.32. + */ + +/*! + * Function-like macro: + * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t &outHi) + * { + * outLo = (uint32x2_t)(in & 0xFFFFFFFF); + * outHi = (uint32x2_t)(in >> 32); + * in = UNDEFINED; + * } + */ +# if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \ + && (defined(__GNUC__) || defined(__clang__)) \ + && (defined(__arm__) || defined(__thumb__) || defined(_M_ARM)) +# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ + do { \ + /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = upper D half */ \ + /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 */ \ + /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 */ \ + __asm__("vzip.32 %e0, %f0" : "+w" (in)); \ + (outLo) = vget_low_u32 (vreinterpretq_u32_u64(in)); \ + (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ + } while (0) +# else +# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ + do { \ + (outLo) = vmovn_u64 (in); \ + (outHi) = vshrn_n_u64 ((in), 32); \ + } while (0) +# endif + +/*! + * @ingroup tuning + * @brief Controls the NEON to scalar ratio for XXH3 + * + * On AArch64 when not optimizing for size, XXH3 will run 6 lanes using NEON and + * 2 lanes on scalar by default. + * + * This can be set to 2, 4, 6, or 8. ARMv7 will default to all 8 NEON lanes, as the + * emulated 64-bit arithmetic is too slow. + * + * Modern ARM CPUs are _very_ sensitive to how their pipelines are used. + * + * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but it can't + * have more than 2 NEON (F0/F1) micro-ops. If you are only using NEON instructions, + * you are only using 2/3 of the CPU bandwidth. + * + * This is even more noticeable on the more advanced cores like the A76 which + * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once. + * + * Therefore, @ref XXH3_NEON_LANES lanes will be processed using NEON, and the + * remaining lanes will use scalar instructions. This improves the bandwidth + * and also gives the integer pipelines something to do besides twiddling loop + * counters and pointers. + * + * This change benefits CPUs with large micro-op buffers without negatively affecting + * other CPUs: + * + * | Chipset | Dispatch type | NEON only | 6:2 hybrid | Diff. | + * |:----------------------|:--------------------|----------:|-----------:|------:| + * | Snapdragon 730 (A76) | 2 NEON/8 micro-ops | 8.8 GB/s | 10.1 GB/s | ~16% | + * | Snapdragon 835 (A73) | 2 NEON/3 micro-ops | 5.1 GB/s | 5.3 GB/s | ~5% | + * | Marvell PXA1928 (A53) | In-order dual-issue | 1.9 GB/s | 1.9 GB/s | 0% | + * + * It also seems to fix some bad codegen on GCC, making it almost as fast as clang. + * + * @see XXH3_accumulate_512_neon() + */ +# ifndef XXH3_NEON_LANES +# if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \ + && !defined(__OPTIMIZE_SIZE__) +# define XXH3_NEON_LANES 6 +# else +# define XXH3_NEON_LANES XXH_ACC_NB +# endif +# endif +#endif /* XXH_VECTOR == XXH_NEON */ + +/* + * VSX and Z Vector helpers. + * + * This is very messy, and any pull requests to clean this up are welcome. + * + * There are a lot of problems with supporting VSX and s390x, due to + * inconsistent intrinsics, spotty coverage, and multiple endiannesses. + */ +#if XXH_VECTOR == XXH_VSX +# if defined(__s390x__) +# include <s390intrin.h> +# else +/* gcc's altivec.h can have the unwanted consequence to unconditionally + * #define bool, vector, and pixel keywords, + * with bad consequences for programs already using these keywords for other purposes. + * The paragraph defining these macros is skipped when __APPLE_ALTIVEC__ is defined. + * __APPLE_ALTIVEC__ is _generally_ defined automatically by the compiler, + * but it seems that, in some cases, it isn't. + * Force the build macro to be defined, so that keywords are not altered. + */ +# if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__) +# define __APPLE_ALTIVEC__ +# endif +# include <altivec.h> # endif -#endif /* XXH_STATIC_LINKING_ONLY && XXH_STATIC_H_3543687687345 */ +typedef __vector unsigned long long xxh_u64x2; +typedef __vector unsigned char xxh_u8x16; +typedef __vector unsigned xxh_u32x4; + +# ifndef XXH_VSX_BE +# if defined(__BIG_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define XXH_VSX_BE 1 +# elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ +# warning "-maltivec=be is not recommended. Please use native endianness." +# define XXH_VSX_BE 1 +# else +# define XXH_VSX_BE 0 +# endif +# endif /* !defined(XXH_VSX_BE) */ + +# if XXH_VSX_BE +# if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) +# define XXH_vec_revb vec_revb +# else +/*! + * A polyfill for POWER9's vec_revb(). + */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) +{ + xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 }; + return vec_perm(val, val, vByteSwap); +} +# endif +# endif /* XXH_VSX_BE */ + +/*! + * Performs an unaligned vector load and byte swaps it on big endian. + */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) +{ + xxh_u64x2 ret; + XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2)); +# if XXH_VSX_BE + ret = XXH_vec_revb(ret); +# endif + return ret; +} + +/* + * vec_mulo and vec_mule are very problematic intrinsics on PowerPC + * + * These intrinsics weren't added until GCC 8, despite existing for a while, + * and they are endian dependent. Also, their meaning swap depending on version. + * */ +# if defined(__s390x__) + /* s390x is always big endian, no issue on this platform */ +# define XXH_vec_mulo vec_mulo +# define XXH_vec_mule vec_mule +# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) +/* Clang has a better way to control this, we can just use the builtin which doesn't swap. */ +# define XXH_vec_mulo __builtin_altivec_vmulouw +# define XXH_vec_mule __builtin_altivec_vmuleuw +# else +/* gcc needs inline assembly */ +/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b) +{ + xxh_u64x2 result; + __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) +{ + xxh_u64x2 result; + __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +# endif /* XXH_vec_mulo, XXH_vec_mule */ +#endif /* XXH_VECTOR == XXH_VSX */ + + +/* prefetch + * can be disabled, by declaring XXH_NO_PREFETCH build macro */ +#if defined(XXH_NO_PREFETCH) +# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ +#else +# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ +# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ +# define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) +# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) +# define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) +# else +# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ +# endif +#endif /* XXH_NO_PREFETCH */ + + +/* ========================================== + * XXH3 default settings + * ========================================== */ + +#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ + +#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) +# error "default keyset is not large enough" +#endif + +/*! Pseudorandom secret taken directly from FARSH. */ +XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, + 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, + 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, + 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, + 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, + 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, + 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, + 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, +}; + + +#ifdef XXH_OLD_NAMES +# define kSecret XXH3_kSecret +#endif + +#ifdef XXH_DOXYGEN +/*! + * @brief Calculates a 32-bit to 64-bit long multiply. + * + * Implemented as a macro. + * + * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't + * need to (but it shouldn't need to anyways, it is about 7 instructions to do + * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we + * use that instead of the normal method. + * + * If you are compiling for platforms like Thumb-1 and don't have a better option, + * you may also want to write your own long multiply routine here. + * + * @param x, y Numbers to be multiplied + * @return 64-bit product of the low 32 bits of @p x and @p y. + */ +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64(xxh_u64 x, xxh_u64 y) +{ + return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); +} +#elif defined(_MSC_VER) && defined(_M_IX86) +# define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) +#else +/* + * Downcast + upcast is usually better than masking on older compilers like + * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers. + * + * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands + * and perform a full 64x64 multiply -- entirely redundant on 32-bit. + */ +# define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) +#endif + +/*! + * @brief Calculates a 64->128-bit long multiply. + * + * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar + * version. + * + * @param lhs , rhs The 64-bit integers to be multiplied + * @return The 128-bit result represented in an @ref XXH128_hash_t. + */ +static XXH128_hash_t +XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) +{ + /* + * GCC/Clang __uint128_t method. + * + * On most 64-bit targets, GCC and Clang define a __uint128_t type. + * This is usually the best way as it usually uses a native long 64-bit + * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. + * + * Usually. + * + * Despite being a 32-bit platform, Clang (and emscripten) define this type + * despite not having the arithmetic for it. This results in a laggy + * compiler builtin call which calculates a full 128-bit multiply. + * In that case it is best to use the portable one. + * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 + */ +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \ + && defined(__SIZEOF_INT128__) \ + || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) + + __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; + XXH128_hash_t r128; + r128.low64 = (xxh_u64)(product); + r128.high64 = (xxh_u64)(product >> 64); + return r128; + + /* + * MSVC for x64's _umul128 method. + * + * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); + * + * This compiles to single operand MUL on x64. + */ +#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC) + +#ifndef _MSC_VER +# pragma intrinsic(_umul128) +#endif + xxh_u64 product_high; + xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); + XXH128_hash_t r128; + r128.low64 = product_low; + r128.high64 = product_high; + return r128; + + /* + * MSVC for ARM64's __umulh method. + * + * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. + */ +#elif defined(_M_ARM64) || defined(_M_ARM64EC) + +#ifndef _MSC_VER +# pragma intrinsic(__umulh) +#endif + XXH128_hash_t r128; + r128.low64 = lhs * rhs; + r128.high64 = __umulh(lhs, rhs); + return r128; + +#else + /* + * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. + * + * This is a fast and simple grade school multiply, which is shown below + * with base 10 arithmetic instead of base 0x100000000. + * + * 9 3 // D2 lhs = 93 + * x 7 5 // D2 rhs = 75 + * ---------- + * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 + * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 + * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 + * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 + * --------- + * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 + * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 + * --------- + * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 + * + * The reasons for adding the products like this are: + * 1. It avoids manual carry tracking. Just like how + * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. + * This avoids a lot of complexity. + * + * 2. It hints for, and on Clang, compiles to, the powerful UMAAL + * instruction available in ARM's Digital Signal Processing extension + * in 32-bit ARMv6 and later, which is shown below: + * + * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) + * { + * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; + * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); + * *RdHi = (xxh_u32)(product >> 32); + * } + * + * This instruction was designed for efficient long multiplication, and + * allows this to be calculated in only 4 instructions at speeds + * comparable to some 64-bit ALUs. + * + * 3. It isn't terrible on other platforms. Usually this will be a couple + * of 32-bit ADD/ADCs. + */ + + /* First calculate all of the cross products. */ + xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); + xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); + xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); + xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); + + /* Now add the products together. These will never overflow. */ + xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; + xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; + xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); + + XXH128_hash_t r128; + r128.low64 = lower; + r128.high64 = upper; + return r128; +#endif +} + +/*! + * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it. + * + * The reason for the separate function is to prevent passing too many structs + * around by value. This will hopefully inline the multiply, but we don't force it. + * + * @param lhs , rhs The 64-bit integers to multiply + * @return The low 64 bits of the product XOR'd by the high 64 bits. + * @see XXH_mult64to128() + */ +static xxh_u64 +XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) +{ + XXH128_hash_t product = XXH_mult64to128(lhs, rhs); + return product.low64 ^ product.high64; +} + +/*! Seems to produce slightly better code on GCC for some reason. */ +XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) +{ + XXH_ASSERT(0 <= shift && shift < 64); + return v64 ^ (v64 >> shift); +} + +/* + * This is a fast avalanche stage, + * suitable when input bits are already partially mixed + */ +static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) +{ + h64 = XXH_xorshift64(h64, 37); + h64 *= 0x165667919E3779F9ULL; + h64 = XXH_xorshift64(h64, 32); + return h64; +} + +/* + * This is a stronger avalanche, + * inspired by Pelle Evensen's rrmxmx + * preferable when input has not been previously mixed + */ +static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) +{ + /* this mix is inspired by Pelle Evensen's rrmxmx */ + h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); + h64 *= 0x9FB21C651E98DF25ULL; + h64 ^= (h64 >> 35) + len ; + h64 *= 0x9FB21C651E98DF25ULL; + return XXH_xorshift64(h64, 28); +} + + +/* ========================================== + * Short keys + * ========================================== + * One of the shortcomings of XXH32 and XXH64 was that their performance was + * sub-optimal on short lengths. It used an iterative algorithm which strongly + * favored lengths that were a multiple of 4 or 8. + * + * Instead of iterating over individual inputs, we use a set of single shot + * functions which piece together a range of lengths and operate in constant time. + * + * Additionally, the number of multiplies has been significantly reduced. This + * reduces latency, especially when emulating 64-bit multiplies on 32-bit. + * + * Depending on the platform, this may or may not be faster than XXH32, but it + * is almost guaranteed to be faster than XXH64. + */ + +/* + * At very short lengths, there isn't enough input to fully hide secrets, or use + * the entire secret. + * + * There is also only a limited amount of mixing we can do before significantly + * impacting performance. + * + * Therefore, we use different sections of the secret and always mix two secret + * samples with an XOR. This should have no effect on performance on the + * seedless or withSeed variants because everything _should_ be constant folded + * by modern compilers. + * + * The XOR mixing hides individual parts of the secret and increases entropy. + * + * This adds an extra layer of strength for custom secrets. + */ +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combined = { input[0], 0x01, input[0], input[0] } + * len = 2: combined = { input[1], 0x02, input[0], input[1] } + * len = 3: combined = { input[2], 0x03, input[0], input[1] } + */ + { xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) + | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); + xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; + xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; + return XXH64_avalanche(keyed); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; + { xxh_u32 const input1 = XXH_readLE32(input); + xxh_u32 const input2 = XXH_readLE32(input + len - 4); + xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed; + xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); + xxh_u64 const keyed = input64 ^ bitflip; + return XXH3_rrmxmx(keyed, len); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; + xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; + xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; + xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; + xxh_u64 const acc = len + + XXH_swap64(input_lo) + input_hi + + XXH3_mul128_fold64(input_lo, input_hi); + return XXH3_avalanche(acc); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(len <= 16); + { if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed); + if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed); + if (len) return XXH3_len_1to3_64b(input, len, secret, seed); + return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64))); + } +} + +/* + * DISCLAIMER: There are known *seed-dependent* multicollisions here due to + * multiplication by zero, affecting hashes of lengths 17 to 240. + * + * However, they are very unlikely. + * + * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all + * unseeded non-cryptographic hashes, it does not attempt to defend itself + * against specially crafted inputs, only random inputs. + * + * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes + * cancelling out the secret is taken an arbitrary number of times (addressed + * in XXH3_accumulate_512), this collision is very unlikely with random inputs + * and/or proper seeding: + * + * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a + * function that is only called up to 16 times per hash with up to 240 bytes of + * input. + * + * This is not too bad for a non-cryptographic hash function, especially with + * only 64 bit outputs. + * + * The 128-bit variant (which trades some speed for strength) is NOT affected + * by this, although it is always a good idea to use a proper seed if you care + * about strength. + */ +XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64) +{ +#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ + /* + * UGLY HACK: + * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in + * slower code. + * + * By forcing seed64 into a register, we disrupt the cost model and + * cause it to scalarize. See `XXH32_round()` + * + * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, + * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on + * GCC 9.2, despite both emitting scalar code. + * + * GCC generates much better scalar code than Clang for the rest of XXH3, + * which is why finding a more optimal codepath is an interest. + */ + XXH_COMPILER_GUARD(seed64); +#endif + { xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 const input_hi = XXH_readLE64(input+8); + return XXH3_mul128_fold64( + input_lo ^ (XXH_readLE64(secret) + seed64), + input_hi ^ (XXH_readLE64(secret+8) - seed64) + ); + } +} + +/* For mid range keys, XXH3 uses a Mum-hash variant. */ +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { xxh_u64 acc = len * XXH_PRIME64_1; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc += XXH3_mix16B(input+48, secret+96, seed); + acc += XXH3_mix16B(input+len-64, secret+112, seed); + } + acc += XXH3_mix16B(input+32, secret+64, seed); + acc += XXH3_mix16B(input+len-48, secret+80, seed); + } + acc += XXH3_mix16B(input+16, secret+32, seed); + acc += XXH3_mix16B(input+len-32, secret+48, seed); + } + acc += XXH3_mix16B(input+0, secret+0, seed); + acc += XXH3_mix16B(input+len-16, secret+16, seed); + + return XXH3_avalanche(acc); + } +} + +#define XXH3_MIDSIZE_MAX 240 + +XXH_NO_INLINE XXH64_hash_t +XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + #define XXH3_MIDSIZE_STARTOFFSET 3 + #define XXH3_MIDSIZE_LASTOFFSET 17 + + { xxh_u64 acc = len * XXH_PRIME64_1; + int const nbRounds = (int)len / 16; + int i; + for (i=0; i<8; i++) { + acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); + } + acc = XXH3_avalanche(acc); + XXH_ASSERT(nbRounds >= 8); +#if defined(__clang__) /* Clang */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. + * In everywhere else, it uses scalar code. + * + * For 64->128-bit multiplies, even if the NEON was 100% optimal, it + * would still be slower than UMAAL (see XXH_mult64to128). + * + * Unfortunately, Clang doesn't handle the long multiplies properly and + * converts them to the nonexistent "vmulq_u64" intrinsic, which is then + * scalarized into an ugly mess of VMOV.32 instructions. + * + * This mess is difficult to avoid without turning autovectorization + * off completely, but they are usually relatively minor and/or not + * worth it to fix. + * + * This loop is the easiest to fix, as unlike XXH32, this pragma + * _actually works_ because it is a loop vectorization instead of an + * SLP vectorization. + */ + #pragma clang loop vectorize(disable) +#endif + for (i=8 ; i < nbRounds; i++) { + acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); + } + /* last bytes */ + acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); + return XXH3_avalanche(acc); + } +} + + +/* ======= Long Keys ======= */ + +#define XXH_STRIPE_LEN 64 +#define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */ +#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64)) + +#ifdef XXH_OLD_NAMES +# define STRIPE_LEN XXH_STRIPE_LEN +# define ACC_NB XXH_ACC_NB +#endif + +XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) +{ + if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); + XXH_memcpy(dst, &v64, sizeof(v64)); +} + +/* Several intrinsic functions below are supposed to accept __int64 as argument, + * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ . + * However, several environments do not define __int64 type, + * requiring a workaround. + */ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) + typedef int64_t xxh_i64; +#else + /* the following type must have a width of 64-bit */ + typedef long long xxh_i64; +#endif + + +/* + * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized. + * + * It is a hardened version of UMAC, based off of FARSH's implementation. + * + * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD + * implementations, and it is ridiculously fast. + * + * We harden it by mixing the original input to the accumulators as well as the product. + * + * This means that in the (relatively likely) case of a multiply by zero, the + * original input is preserved. + * + * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve + * cross-pollination, as otherwise the upper and lower halves would be + * essentially independent. + * + * This doesn't matter on 64-bit hashes since they all get merged together in + * the end, so we skip the extra step. + * + * Both XXH3_64bits and XXH3_128bits use this subroutine. + */ + +#if (XXH_VECTOR == XXH_AVX512) \ + || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) + +#ifndef XXH_TARGET_AVX512 +# define XXH_TARGET_AVX512 /* disable attribute target */ +#endif + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void +XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + __m512i* const xacc = (__m512i *) acc; + XXH_ASSERT((((size_t)acc) & 63) == 0); + XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); + + { + /* data_vec = input[0]; */ + __m512i const data_vec = _mm512_loadu_si512 (input); + /* key_vec = secret[0]; */ + __m512i const key_vec = _mm512_loadu_si512 (secret); + /* data_key = data_vec ^ key_vec; */ + __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m512i const data_key_lo = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo); + /* xacc[0] += swap(data_vec); */ + __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2)); + __m512i const sum = _mm512_add_epi64(*xacc, data_swap); + /* xacc[0] += product; */ + *xacc = _mm512_add_epi64(product, sum); + } +} + +/* + * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. + * + * Multiplication isn't perfect, as explained by Google in HighwayHash: + * + * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to + * // varying degrees. In descending order of goodness, bytes + * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32. + * // As expected, the upper and lower bytes are much worse. + * + * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291 + * + * Since our algorithm uses a pseudorandom secret to add some variance into the + * mix, we don't need to (or want to) mix as often or as much as HighwayHash does. + * + * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid + * extraction. + * + * Both XXH3_64bits and XXH3_128bits use this subroutine. + */ + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void +XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 63) == 0); + XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); + { __m512i* const xacc = (__m512i*) acc; + const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); + + /* xacc[0] ^= (xacc[0] >> 47) */ + __m512i const acc_vec = *xacc; + __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47); + __m512i const data_vec = _mm512_xor_si512 (acc_vec, shifted); + /* xacc[0] ^= secret; */ + __m512i const key_vec = _mm512_loadu_si512 (secret); + __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); + + /* xacc[0] *= XXH_PRIME32_1; */ + __m512i const data_key_hi = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1)); + __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32); + __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32); + *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); + } +} + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void +XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0); + XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64); + XXH_ASSERT(((size_t)customSecret & 63) == 0); + (void)(&XXH_writeLE64); + { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); + __m512i const seed = _mm512_mask_set1_epi64(_mm512_set1_epi64((xxh_i64)seed64), 0xAA, (xxh_i64)(0U - seed64)); + + const __m512i* const src = (const __m512i*) ((const void*) XXH3_kSecret); + __m512i* const dest = ( __m512i*) customSecret; + int i; + XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 63) == 0); + for (i=0; i < nbRounds; ++i) { + /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*', + * this will warn "discards 'const' qualifier". */ + union { + const __m512i* cp; + void* p; + } remote_const_void; + remote_const_void.cp = src + i; + dest[i] = _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed); + } } +} + +#endif + +#if (XXH_VECTOR == XXH_AVX2) \ + || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) + +#ifndef XXH_TARGET_AVX2 +# define XXH_TARGET_AVX2 /* disable attribute target */ +#endif + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void +XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 31) == 0); + { __m256i* const xacc = (__m256i *) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i* const xinput = (const __m256i *) input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i* const xsecret = (const __m256i *) secret; + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { + /* data_vec = xinput[i]; */ + __m256i const data_vec = _mm256_loadu_si256 (xinput+i); + /* key_vec = xsecret[i]; */ + __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); + /* data_key = data_vec ^ key_vec; */ + __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m256i const data_key_lo = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); + __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm256_add_epi64(product, sum); + } } +} + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void +XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 31) == 0); + { __m256i* const xacc = (__m256i*) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i* const xsecret = (const __m256i *) secret; + const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1); + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m256i const acc_vec = xacc[i]; + __m256i const shifted = _mm256_srli_epi64 (acc_vec, 47); + __m256i const data_vec = _mm256_xor_si256 (acc_vec, shifted); + /* xacc[i] ^= xsecret; */ + __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); + __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); + __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); + xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); + } + } +} + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0); + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6); + XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); + (void)(&XXH_writeLE64); + XXH_PREFETCH(customSecret); + { __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64); + + const __m256i* const src = (const __m256i*) ((const void*) XXH3_kSecret); + __m256i* dest = ( __m256i*) customSecret; + +# if defined(__GNUC__) || defined(__clang__) + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + */ + XXH_COMPILER_GUARD(dest); +# endif + XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 31) == 0); + + /* GCC -O2 need unroll loop manually */ + dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src+0), seed); + dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src+1), seed); + dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src+2), seed); + dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src+3), seed); + dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src+4), seed); + dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src+5), seed); + } +} + +#endif + +/* x86dispatch always generates SSE2 */ +#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) + +#ifndef XXH_TARGET_SSE2 +# define XXH_TARGET_SSE2 /* disable attribute target */ +#endif + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void +XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + /* SSE2 is just a half-scale version of the AVX2 version. */ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { __m128i* const xacc = (__m128i *) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i* const xinput = (const __m128i *) input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i* const xsecret = (const __m128i *) secret; + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { + /* data_vec = xinput[i]; */ + __m128i const data_vec = _mm_loadu_si128 (xinput+i); + /* key_vec = xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128 (xsecret+i); + /* data_key = data_vec ^ key_vec; */ + __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m128i const product = _mm_mul_epu32 (data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); + __m128i const sum = _mm_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm_add_epi64(product, sum); + } } +} + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void +XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { __m128i* const xacc = (__m128i*) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i* const xsecret = (const __m128i *) secret; + const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = _mm_srli_epi64 (acc_vec, 47); + __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128 (xsecret+i); + __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32); + __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32); + xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); + } + } +} + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + (void)(&XXH_writeLE64); + { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); + +# if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 + /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ + XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; + __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); +# else + __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); +# endif + int i; + + const void* const src16 = XXH3_kSecret; + __m128i* dst16 = (__m128i*) customSecret; +# if defined(__GNUC__) || defined(__clang__) + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + */ + XXH_COMPILER_GUARD(dst16); +# endif + XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dst16 & 15) == 0); + + for (i=0; i < nbRounds; ++i) { + dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); + } } +} + +#endif + +#if (XXH_VECTOR == XXH_NEON) + +/* forward declarations for the scalar routines */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, size_t lane); + +XXH_FORCE_INLINE void +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, size_t lane); + +/*! + * @internal + * @brief The bulk processing loop for NEON. + * + * The NEON code path is actually partially scalar when running on AArch64. This + * is to optimize the pipelining and can have up to 15% speedup depending on the + * CPU, and it also mitigates some GCC codegen issues. + * + * @see XXH3_NEON_LANES for configuring this and details about this optimization. + */ +XXH_FORCE_INLINE void +XXH3_accumulate_512_neon( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0); + { + uint64x2_t* const xacc = (uint64x2_t *) acc; + /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ + uint8_t const* const xinput = (const uint8_t *) input; + uint8_t const* const xsecret = (const uint8_t *) secret; + + size_t i; + /* NEON for the first few lanes (these loops are normally interleaved) */ + for (i=0; i < XXH3_NEON_LANES / 2; i++) { + /* data_vec = xinput[i]; */ + uint8x16_t data_vec = vld1q_u8(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16)); + uint64x2_t data_key; + uint32x2_t data_key_lo, data_key_hi; + /* xacc[i] += swap(data_vec); */ + uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); + uint64x2_t const swapped = vextq_u64(data64, data64, 1); + xacc[i] = vaddq_u64 (xacc[i], swapped); + /* data_key = data_vec ^ key_vec; */ + data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); + /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); + * data_key_hi = (uint32x2_t) (data_key >> 32); + * data_key = UNDEFINED; */ + XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); + /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ + xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi); + + } + /* Scalar for the remainder. This may be a zero iteration loop. */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarRound(acc, input, secret, i); + } + } +} + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { uint64x2_t* xacc = (uint64x2_t*) acc; + uint8_t const* xsecret = (uint8_t const*) secret; + uint32x2_t prime = vdup_n_u32 (XXH_PRIME32_1); + + size_t i; + /* NEON for the first few lanes (these loops are normally interleaved) */ + for (i=0; i < XXH3_NEON_LANES / 2; i++) { + /* xacc[i] ^= (xacc[i] >> 47); */ + uint64x2_t acc_vec = xacc[i]; + uint64x2_t shifted = vshrq_n_u64 (acc_vec, 47); + uint64x2_t data_vec = veorq_u64 (acc_vec, shifted); + + /* xacc[i] ^= xsecret[i]; */ + uint8x16_t key_vec = vld1q_u8 (xsecret + (i * 16)); + uint64x2_t data_key = veorq_u64 (data_vec, vreinterpretq_u64_u8(key_vec)); + + /* xacc[i] *= XXH_PRIME32_1 */ + uint32x2_t data_key_lo, data_key_hi; + /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF); + * data_key_hi = (uint32x2_t) (xacc[i] >> 32); + * xacc[i] = UNDEFINED; */ + XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi); + { /* + * prod_hi = (data_key >> 32) * XXH_PRIME32_1; + * + * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will + * incorrectly "optimize" this: + * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b)); + * shifted = vshll_n_u32(tmp, 32); + * to this: + * tmp = "vmulq_u64"(a, b); // no such thing! + * shifted = vshlq_n_u64(tmp, 32); + * + * However, unlike SSE, Clang lacks a 64-bit multiply routine + * for NEON, and it scalarizes two 64-bit multiplies instead. + * + * vmull_u32 has the same timing as vmul_u32, and it avoids + * this bug completely. + * See https://bugs.llvm.org/show_bug.cgi?id=39967 + */ + uint64x2_t prod_hi = vmull_u32 (data_key_hi, prime); + /* xacc[i] = prod_hi << 32; */ + xacc[i] = vshlq_n_u64(prod_hi, 32); + /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */ + xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime); + } + } + /* Scalar for the remainder. This may be a zero iteration loop. */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); + } + } +} + +#endif + +#if (XXH_VECTOR == XXH_VSX) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + /* presumed aligned */ + unsigned int* const xacc = (unsigned int*) acc; + xxh_u64x2 const* const xinput = (xxh_u64x2 const*) input; /* no alignment restriction */ + xxh_u64x2 const* const xsecret = (xxh_u64x2 const*) secret; /* no alignment restriction */ + xxh_u64x2 const v32 = { 32, 32 }; + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { + /* data_vec = xinput[i]; */ + xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i); + /* key_vec = xsecret[i]; */ + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const data_key = data_vec ^ key_vec; + /* shuffled = (data_key << 32) | (data_key >> 32); */ + xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); + /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */ + xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); + /* acc_vec = xacc[i]; */ + xxh_u64x2 acc_vec = (xxh_u64x2)vec_xl(0, xacc + 4 * i); + acc_vec += product; + + /* swap high and low halves */ +#ifdef __s390x__ + acc_vec += vec_permi(data_vec, data_vec, 2); +#else + acc_vec += vec_xxpermdi(data_vec, data_vec, 2); +#endif + /* xacc[i] = acc_vec; */ + vec_xst((xxh_u32x4)acc_vec, 0, xacc + 4 * i); + } +} + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { xxh_u64x2* const xacc = (xxh_u64x2*) acc; + const xxh_u64x2* const xsecret = (const xxh_u64x2*) secret; + /* constants */ + xxh_u64x2 const v32 = { 32, 32 }; + xxh_u64x2 const v47 = { 47, 47 }; + xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 }; + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { + /* xacc[i] ^= (xacc[i] >> 47); */ + xxh_u64x2 const acc_vec = xacc[i]; + xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); + + /* xacc[i] ^= xsecret[i]; */ + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i); + xxh_u64x2 const data_key = data_vec ^ key_vec; + + /* xacc[i] *= XXH_PRIME32_1 */ + /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */ + xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); + /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ + xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); + xacc[i] = prod_odd + (prod_even << v32); + } } +} + +#endif + +/* scalar variants - universal */ + +/*! + * @internal + * @brief Scalar round for @ref XXH3_accumulate_512_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, + size_t lane) +{ + xxh_u64* xacc = (xxh_u64*) acc; + xxh_u8 const* xinput = (xxh_u8 const*) input; + xxh_u8 const* xsecret = (xxh_u8 const*) secret; + XXH_ASSERT(lane < XXH_ACC_NB); + XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + { + xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8); + xacc[lane ^ 1] += data_val; /* swap adjacent lanes */ + xacc[lane] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + } +} + +/*! + * @internal + * @brief Processes a 64 byte block of data using the scalar path. + */ +XXH_FORCE_INLINE void +XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + size_t i; + for (i=0; i < XXH_ACC_NB; i++) { + XXH3_scalarRound(acc, input, secret, i); + } +} + +/*! + * @internal + * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ +XXH_FORCE_INLINE void +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, + size_t lane) +{ + xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ + const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ + XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); + XXH_ASSERT(lane < XXH_ACC_NB); + { + xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8); + xxh_u64 acc64 = xacc[lane]; + acc64 = XXH_xorshift64(acc64, 47); + acc64 ^= key64; + acc64 *= XXH_PRIME32_1; + xacc[lane] = acc64; + } +} + +/*! + * @internal + * @brief Scrambles the accumulators after a large chunk has been read + */ +XXH_FORCE_INLINE void +XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + size_t i; + for (i=0; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); + } +} + +XXH_FORCE_INLINE void +XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + /* + * We need a separate pointer for the hack below, + * which requires a non-const pointer. + * Any decent compiler will optimize this out otherwise. + */ + const xxh_u8* kSecretPtr = XXH3_kSecret; + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + +#if defined(__clang__) && defined(__aarch64__) + /* + * UGLY HACK: + * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are + * placed sequentially, in order, at the top of the unrolled loop. + * + * While MOVK is great for generating constants (2 cycles for a 64-bit + * constant compared to 4 cycles for LDR), it fights for bandwidth with + * the arithmetic instructions. + * + * I L S + * MOVK + * MOVK + * MOVK + * MOVK + * ADD + * SUB STR + * STR + * By forcing loads from memory (as the asm line causes Clang to assume + * that XXH3_kSecretPtr has been changed), the pipelines are used more + * efficiently: + * I L S + * LDR + * ADD LDR + * SUB STR + * STR + * + * See XXH3_NEON_LANES for details on the pipsline. + * + * XXH3_64bits_withSeed, len == 256, Snapdragon 835 + * without hack: 2654.4 MB/s + * with hack: 3202.9 MB/s + */ + XXH_COMPILER_GUARD(kSecretPtr); +#endif + /* + * Note: in debug mode, this overrides the asm optimization + * and Clang will emit MOVK chains again. + */ + XXH_ASSERT(kSecretPtr == XXH3_kSecret); + + { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + int i; + for (i=0; i < nbRounds; i++) { + /* + * The asm hack causes Clang to assume that kSecretPtr aliases with + * customSecret, and on aarch64, this prevented LDP from merging two + * loads together for free. Putting the loads together before the stores + * properly generates LDP. + */ + xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i) + seed64; + xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64; + XXH_writeLE64((xxh_u8*)customSecret + 16*i, lo); + XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi); + } } +} + + +typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*); +typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); +typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); + + +#if (XXH_VECTOR == XXH_AVX512) + +#define XXH3_accumulate_512 XXH3_accumulate_512_avx512 +#define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 +#define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 + +#elif (XXH_VECTOR == XXH_AVX2) + +#define XXH3_accumulate_512 XXH3_accumulate_512_avx2 +#define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 +#define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 + +#elif (XXH_VECTOR == XXH_SSE2) + +#define XXH3_accumulate_512 XXH3_accumulate_512_sse2 +#define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 +#define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 + +#elif (XXH_VECTOR == XXH_NEON) + +#define XXH3_accumulate_512 XXH3_accumulate_512_neon +#define XXH3_scrambleAcc XXH3_scrambleAcc_neon +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#elif (XXH_VECTOR == XXH_VSX) + +#define XXH3_accumulate_512 XXH3_accumulate_512_vsx +#define XXH3_scrambleAcc XXH3_scrambleAcc_vsx +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#else /* scalar */ + +#define XXH3_accumulate_512 XXH3_accumulate_512_scalar +#define XXH3_scrambleAcc XXH3_scrambleAcc_scalar +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#endif + + + +#ifndef XXH_PREFETCH_DIST +# ifdef __clang__ +# define XXH_PREFETCH_DIST 320 +# else +# if (XXH_VECTOR == XXH_AVX512) +# define XXH_PREFETCH_DIST 512 +# else +# define XXH_PREFETCH_DIST 384 +# endif +# endif /* __clang__ */ +#endif /* XXH_PREFETCH_DIST */ + +/* + * XXH3_accumulate() + * Loops over XXH3_accumulate_512(). + * Assumption: nbStripes will not overflow the secret size + */ +XXH_FORCE_INLINE void +XXH3_accumulate( xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, + size_t nbStripes, + XXH3_f_accumulate_512 f_acc512) +{ + size_t n; + for (n = 0; n < nbStripes; n++ ) { + const xxh_u8* const in = input + n*XXH_STRIPE_LEN; + XXH_PREFETCH(in + XXH_PREFETCH_DIST); + f_acc512(acc, + in, + secret + n*XXH_SECRET_CONSUME_RATE); + } +} + +XXH_FORCE_INLINE void +XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) +{ + size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; + size_t const nb_blocks = (len - 1) / block_len; + + size_t n; + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + + for (n = 0; n < nb_blocks; n++) { + XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512); + f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); + } + + /* last partial block */ + XXH_ASSERT(len > XXH_STRIPE_LEN); + { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; + XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); + XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512); + + /* last stripe */ + { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; +#define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ + f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + } } +} + +XXH_FORCE_INLINE xxh_u64 +XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret) +{ + return XXH3_mul128_fold64( + acc[0] ^ XXH_readLE64(secret), + acc[1] ^ XXH_readLE64(secret+8) ); +} + +static XXH64_hash_t +XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) +{ + xxh_u64 result64 = start; + size_t i = 0; + + for (i = 0; i < 4; i++) { + result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i); +#if defined(__clang__) /* Clang */ \ + && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Prevent autovectorization on Clang ARMv7-a. Exact same problem as + * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. + * XXH3_64bits, len == 256, Snapdragon 835: + * without hack: 2063.7 MB/s + * with hack: 2560.7 MB/s + */ + XXH_COMPILER_GUARD(result64); +#endif + } + + return XXH3_avalanche(result64); +} + +#define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ + XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 } + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, + const void* XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) +{ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + /* do not align on 8, so that the secret is different from the accumulator */ +#define XXH_SECRET_MERGEACCS_START 11 + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); +} + +/* + * It's important for performance to transmit secret's size (when it's static) + * so that the compiler can properly optimize the vectorized loop. + * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. + */ +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; + return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc); +} + +/* + * It's preferable for performance that XXH3_hashLong is not inlined, + * as it results in a smaller function for small data, easier to the instruction cache. + * Note that inside this no_inline function, we do inline the internal loop, + * and provide a statically defined secret size to allow optimization of vector loop. + */ +XXH_NO_INLINE XXH64_hash_t +XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; (void)secret; (void)secretLen; + return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc); +} + +/* + * XXH3_hashLong_64b_withSeed(): + * Generate a custom key based on alteration of default XXH3_kSecret with the seed, + * and then use this key for long mode hashing. + * + * This operation is decently fast but nonetheless costs a little bit of time. + * Try to avoid it whenever possible (typically when seed==0). + * + * It's important for performance that XXH3_hashLong is not inlined. Not sure + * why (uop cache maybe?), but the difference is large and easily measurable. + */ +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, + XXH64_hash_t seed, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) +{ + if (seed == 0) + return XXH3_hashLong_64b_internal(input, len, + XXH3_kSecret, sizeof(XXH3_kSecret), + f_acc512, f_scramble); + { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed); + return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), + f_acc512, f_scramble); + } +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH64_hash_t +XXH3_hashLong_64b_withSeed(const void* input, size_t len, + XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen) +{ + (void)secret; (void)secretLen; + return XXH3_hashLong_64b_withSeed_internal(input, len, seed, + XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); +} + + +typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t, + XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t); + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong64_f f_hashLong) +{ + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secretLen` condition is not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + * Also, note that function signature doesn't offer room to return an error. + */ + if (len <= 16) + return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); + if (len <= 128) + return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen); +} + + +/* === Public entry point === */ + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len) +{ + return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +{ + return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +{ + return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); +} + +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_64b_withSecret(input, len, seed, (const xxh_u8*)secret, secretSize); +} + + +/* === XXH3 streaming === */ + +/* + * Malloc's a pointer that is always aligned to align. + * + * This must be freed with `XXH_alignedFree()`. + * + * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte + * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2 + * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON. + * + * This underalignment previously caused a rather obvious crash which went + * completely unnoticed due to XXH3_createState() not actually being tested. + * Credit to RedSpah for noticing this bug. + * + * The alignment is done manually: Functions like posix_memalign or _mm_malloc + * are avoided: To maintain portability, we would have to write a fallback + * like this anyways, and besides, testing for the existence of library + * functions without relying on external build tools is impossible. + * + * The method is simple: Overallocate, manually align, and store the offset + * to the original behind the returned pointer. + * + * Align must be a power of 2 and 8 <= align <= 128. + */ +static void* XXH_alignedMalloc(size_t s, size_t align) +{ + XXH_ASSERT(align <= 128 && align >= 8); /* range check */ + XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ + XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ + { /* Overallocate to make room for manual realignment and an offset byte */ + xxh_u8* base = (xxh_u8*)XXH_malloc(s + align); + if (base != NULL) { + /* + * Get the offset needed to align this pointer. + * + * Even if the returned pointer is aligned, there will always be + * at least one byte to store the offset to the original pointer. + */ + size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ + /* Add the offset for the now-aligned pointer */ + xxh_u8* ptr = base + offset; + + XXH_ASSERT((size_t)ptr % align == 0); + + /* Store the offset immediately before the returned pointer. */ + ptr[-1] = (xxh_u8)offset; + return ptr; + } + return NULL; + } +} +/* + * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass + * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. + */ +static void XXH_alignedFree(void* p) +{ + if (p != NULL) { + xxh_u8* ptr = (xxh_u8*)p; + /* Get the offset byte we added in XXH_malloc. */ + xxh_u8 offset = ptr[-1]; + /* Free the original malloc'd pointer */ + xxh_u8* base = ptr - offset; + XXH_free(base); + } +} +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) +{ + XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); + if (state==NULL) return NULL; + XXH3_INITSTATE(state); + return state; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) +{ + XXH_alignedFree(statePtr); + return XXH_OK; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API void +XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) +{ + XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); +} + +static void +XXH3_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const void* secret, size_t secretSize) +{ + size_t const initStart = offsetof(XXH3_state_t, bufferedSize); + size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; + XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); + XXH_ASSERT(statePtr != NULL); + /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ + memset((char*)statePtr + initStart, 0, initLength); + statePtr->acc[0] = XXH_PRIME32_3; + statePtr->acc[1] = XXH_PRIME64_1; + statePtr->acc[2] = XXH_PRIME64_2; + statePtr->acc[3] = XXH_PRIME64_3; + statePtr->acc[4] = XXH_PRIME64_4; + statePtr->acc[5] = XXH_PRIME32_2; + statePtr->acc[6] = XXH_PRIME64_5; + statePtr->acc[7] = XXH_PRIME32_1; + statePtr->seed = seed; + statePtr->useSeed = (seed != 0); + statePtr->extSecret = (const unsigned char*)secret; + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; + statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset(XXH3_state_t* statePtr) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_reset_internal(statePtr, 0, secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + if (statePtr == NULL) return XXH_ERROR; + if (seed==0) return XXH3_64bits_reset(statePtr); + if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed64) +{ + if (statePtr == NULL) return XXH_ERROR; + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + XXH3_reset_internal(statePtr, seed64, secret, secretSize); + statePtr->useSeed = 1; /* always, even if seed64==0 */ + return XXH_OK; +} + +/* Note : when XXH3_consumeStripes() is invoked, + * there must be a guarantee that at least one more byte must be consumed from input + * so that the function can blindly consume all stripes using the "normal" secret segment */ +XXH_FORCE_INLINE void +XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, + size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, + const xxh_u8* XXH_RESTRICT input, size_t nbStripes, + const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) +{ + XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */ + XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); + if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) { + /* need a scrambling operation */ + size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr; + size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock; + XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512); + f_scramble(acc, secret + secretLimit); + XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512); + *nbStripesSoFarPtr = nbStripesAfterBlock; + } else { + XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512); + *nbStripesSoFarPtr += nbStripes; + } +} + +#ifndef XXH3_STREAM_USE_STACK +# ifndef __clang__ /* clang doesn't need additional stack space */ +# define XXH3_STREAM_USE_STACK 1 +# endif +#endif +/* + * Both XXH3_64bits_update and XXH3_128bits_update use this routine. + */ +XXH_FORCE_INLINE XXH_errorcode +XXH3_update(XXH3_state_t* XXH_RESTRICT const state, + const xxh_u8* XXH_RESTRICT input, size_t len, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) +{ + if (input==NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } + + XXH_ASSERT(state != NULL); + { const xxh_u8* const bEnd = input + len; + const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* For some reason, gcc and MSVC seem to suffer greatly + * when operating accumulators directly into state. + * Operating into stack space seems to enable proper optimization. + * clang, on the other hand, doesn't seem to need this trick */ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; memcpy(acc, state->acc, sizeof(acc)); +#else + xxh_u64* XXH_RESTRICT const acc = state->acc; +#endif + state->totalLen += len; + XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); + + /* small input : just fill in tmp buffer */ + if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } + + /* total input is now > XXH3_INTERNALBUFFER_SIZE */ + #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) + XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ + + /* + * Internal buffer is partially filled (always, except at beginning) + * Complete it, then consume it. + */ + if (state->bufferedSize) { + size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; + XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); + input += loadSize; + XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, XXH3_INTERNALBUFFER_STRIPES, + secret, state->secretLimit, + f_acc512, f_scramble); + state->bufferedSize = 0; + } + XXH_ASSERT(input < bEnd); + + /* large input to consume : ingest per full block */ + if ((size_t)(bEnd - input) > state->nbStripesPerBlock * XXH_STRIPE_LEN) { + size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; + XXH_ASSERT(state->nbStripesPerBlock >= state->nbStripesSoFar); + /* join to current block's end */ + { size_t const nbStripesToEnd = state->nbStripesPerBlock - state->nbStripesSoFar; + XXH_ASSERT(nbStripesToEnd <= nbStripes); + XXH3_accumulate(acc, input, secret + state->nbStripesSoFar * XXH_SECRET_CONSUME_RATE, nbStripesToEnd, f_acc512); + f_scramble(acc, secret + state->secretLimit); + state->nbStripesSoFar = 0; + input += nbStripesToEnd * XXH_STRIPE_LEN; + nbStripes -= nbStripesToEnd; + } + /* consume per entire blocks */ + while(nbStripes >= state->nbStripesPerBlock) { + XXH3_accumulate(acc, input, secret, state->nbStripesPerBlock, f_acc512); + f_scramble(acc, secret + state->secretLimit); + input += state->nbStripesPerBlock * XXH_STRIPE_LEN; + nbStripes -= state->nbStripesPerBlock; + } + /* consume last partial block */ + XXH3_accumulate(acc, input, secret, nbStripes, f_acc512); + input += nbStripes * XXH_STRIPE_LEN; + XXH_ASSERT(input < bEnd); /* at least some bytes left */ + state->nbStripesSoFar = nbStripes; + /* buffer predecessor of last partial stripe */ + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); + XXH_ASSERT(bEnd - input <= XXH_STRIPE_LEN); + } else { + /* content to consume <= block size */ + /* Consume input by a multiple of internal buffer size */ + if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { + const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; + do { + XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + input, XXH3_INTERNALBUFFER_STRIPES, + secret, state->secretLimit, + f_acc512, f_scramble); + input += XXH3_INTERNALBUFFER_SIZE; + } while (input<limit); + /* buffer predecessor of last partial stripe */ + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); + } + } + + /* Some remaining input (always) : buffer it */ + XXH_ASSERT(input < bEnd); + XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); + XXH_ASSERT(state->bufferedSize == 0); + XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); + state->bufferedSize = (XXH32_hash_t)(bEnd-input); +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* save stack accumulators into state */ + memcpy(state->acc, acc, sizeof(acc)); +#endif + } + + return XXH_OK; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) +{ + return XXH3_update(state, (const xxh_u8*)input, len, + XXH3_accumulate_512, XXH3_scrambleAcc); +} + + +XXH_FORCE_INLINE void +XXH3_digest_long (XXH64_hash_t* acc, + const XXH3_state_t* state, + const unsigned char* secret) +{ + /* + * Digest on a local copy. This way, the state remains unaltered, and it can + * continue ingesting more input afterwards. + */ + XXH_memcpy(acc, state->acc, sizeof(state->acc)); + if (state->bufferedSize >= XXH_STRIPE_LEN) { + size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; + size_t nbStripesSoFar = state->nbStripesSoFar; + XXH3_consumeStripes(acc, + &nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, nbStripes, + secret, state->secretLimit, + XXH3_accumulate_512, XXH3_scrambleAcc); + /* last stripe */ + XXH3_accumulate_512(acc, + state->buffer + state->bufferedSize - XXH_STRIPE_LEN, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); + } else { /* bufferedSize < XXH_STRIPE_LEN */ + xxh_u8 lastStripe[XXH_STRIPE_LEN]; + size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; + XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ + XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + XXH3_accumulate_512(acc, + lastStripe, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); + } +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) +{ + const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + return XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)state->totalLen * XXH_PRIME64_1); + } + /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ + if (state->useSeed) + return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); +} + + + +/* ========================================== + * XXH3 128 bits (a.k.a XXH128) + * ========================================== + * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant, + * even without counting the significantly larger output size. + * + * For example, extra steps are taken to avoid the seed-dependent collisions + * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B). + * + * This strength naturally comes at the cost of some speed, especially on short + * lengths. Note that longer hashes are about as fast as the 64-bit version + * due to it using only a slight modification of the 64-bit loop. + * + * XXH128 is also more oriented towards 64-bit machines. It is still extremely + * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). + */ + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + /* A doubled version of 1to3_64b with different constants. */ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } + * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } + * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } + */ + { xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24) + | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); + xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); + xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; + xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed; + xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; + xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; + XXH128_hash_t h128; + h128.low64 = XXH64_avalanche(keyed_lo); + h128.high64 = XXH64_avalanche(keyed_hi); + return h128; + } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; + { xxh_u32 const input_lo = XXH_readLE32(input); + xxh_u32 const input_hi = XXH_readLE32(input + len - 4); + xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); + xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed; + xxh_u64 const keyed = input_64 ^ bitflip; + + /* Shift len to the left to ensure it is even, this avoids even multiplies. */ + XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); + + m128.high64 += (m128.low64 << 1); + m128.low64 ^= (m128.high64 >> 3); + + m128.low64 = XXH_xorshift64(m128.low64, 35); + m128.low64 *= 0x9FB21C651E98DF25ULL; + m128.low64 = XXH_xorshift64(m128.low64, 28); + m128.high64 = XXH3_avalanche(m128.high64); + return m128; + } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed; + xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed; + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 input_hi = XXH_readLE64(input + len - 8); + XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); + /* + * Put len in the middle of m128 to ensure that the length gets mixed to + * both the low and high bits in the 128x64 multiply below. + */ + m128.low64 += (xxh_u64)(len - 1) << 54; + input_hi ^= bitfliph; + /* + * Add the high 32 bits of input_hi to the high 32 bits of m128, then + * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to + * the high 64 bits of m128. + * + * The best approach to this operation is different on 32-bit and 64-bit. + */ + if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ + /* + * 32-bit optimized version, which is more readable. + * + * On 32-bit, it removes an ADC and delays a dependency between the two + * halves of m128.high64, but it generates an extra mask on 64-bit. + */ + m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); + } else { + /* + * 64-bit optimized (albeit more confusing) version. + * + * Uses some properties of addition and multiplication to remove the mask: + * + * Let: + * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) + * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) + * c = XXH_PRIME32_2 + * + * a + (b * c) + * Inverse Property: x + y - x == y + * a + (b * (1 + c - 1)) + * Distributive Property: x * (y + z) == (x * y) + (x * z) + * a + (b * 1) + (b * (c - 1)) + * Identity Property: x * 1 == x + * a + b + (b * (c - 1)) + * + * Substitute a, b, and c: + * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + * + * Since input_hi.hi + input_hi.lo == input_hi, we get this: + * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + */ + m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); + } + /* m128 ^= XXH_swap64(m128 >> 64); */ + m128.low64 ^= XXH_swap64(m128.high64); + + { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ + XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); + h128.high64 += m128.high64 * XXH_PRIME64_2; + + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = XXH3_avalanche(h128.high64); + return h128; + } } +} + +/* + * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN + */ +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(len <= 16); + { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); + if (len) return XXH3_len_1to3_128b(input, len, secret, seed); + { XXH128_hash_t h128; + xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72); + xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88); + h128.low64 = XXH64_avalanche(seed ^ bitflipl); + h128.high64 = XXH64_avalanche( seed ^ bitfliph); + return h128; + } } +} + +/* + * A bit slower than XXH3_mix16B, but handles multiply by zero better. + */ +XXH_FORCE_INLINE XXH128_hash_t +XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, + const xxh_u8* secret, XXH64_hash_t seed) +{ + acc.low64 += XXH3_mix16B (input_1, secret+0, seed); + acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); + acc.high64 += XXH3_mix16B (input_2, secret+16, seed); + acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); + return acc; +} + + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { XXH128_hash_t acc; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); + } + acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); + } + acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); + } + acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); + { XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); + return h128; + } + } +} + +XXH_NO_INLINE XXH128_hash_t +XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + { XXH128_hash_t acc; + int const nbRounds = (int)len / 32; + int i; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + for (i=0; i<4; i++) { + acc = XXH128_mix32B(acc, + input + (32 * i), + input + (32 * i) + 16, + secret + (32 * i), + seed); + } + acc.low64 = XXH3_avalanche(acc.low64); + acc.high64 = XXH3_avalanche(acc.high64); + XXH_ASSERT(nbRounds >= 4); + for (i=4 ; i < nbRounds; i++) { + acc = XXH128_mix32B(acc, + input + (32 * i), + input + (32 * i) + 16, + secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), + seed); + } + /* last bytes */ + acc = XXH128_mix32B(acc, + input + len - 16, + input + len - 32, + secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, + 0ULL - seed); + + { XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); + return h128; + } + } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble) +{ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { XXH128_hash_t h128; + h128.low64 = XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)len * XXH_PRIME64_1); + h128.high64 = XXH3_mergeAccs(acc, + secret + secretSize + - sizeof(acc) - XXH_SECRET_MERGEACCS_START, + ~((xxh_u64)len * XXH_PRIME64_2)); + return h128; + } +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH128_hash_t +XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + const void* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; (void)secret; (void)secretLen; + return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_accumulate_512, XXH3_scrambleAcc); +} + +/* + * It's important for performance to pass @secretLen (when it's static) + * to the compiler, so that it can properly optimize the vectorized loop. + */ +XXH_FORCE_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + const void* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, + XXH3_accumulate_512, XXH3_scrambleAcc); +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + XXH3_f_accumulate_512 f_acc512, + XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) +{ + if (seed64 == 0) + return XXH3_hashLong_128b_internal(input, len, + XXH3_kSecret, sizeof(XXH3_kSecret), + f_acc512, f_scramble); + { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed64); + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), + f_acc512, f_scramble); + } +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSeed(const void* input, size_t len, + XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) +{ + (void)secret; (void)secretLen; + return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, + XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret); +} + +typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, + XXH64_hash_t, const void* XXH_RESTRICT, size_t); + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_128bits_internal(const void* input, size_t len, + XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong128_f f_hl128) +{ + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secret` conditions are not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + */ + if (len <= 16) + return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); + if (len <= 128) + return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + return f_hl128(input, len, seed64, secret, secretLen); +} + + +/* === Public XXH128 API === */ + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len) +{ + return XXH3_128bits_internal(input, len, 0, + XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_128b_default); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +{ + return XXH3_128bits_internal(input, len, 0, + (const xxh_u8*)secret, secretSize, + XXH3_hashLong_128b_withSecret); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +{ + return XXH3_128bits_internal(input, len, seed, + XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_128b_withSeed); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH128(const void* input, size_t len, XXH64_hash_t seed) +{ + return XXH3_128bits_withSeed(input, len, seed); +} + + +/* === XXH3 128-bit streaming === */ + +/* + * All initialization and update functions are identical to 64-bit streaming variant. + * The only difference is the finalization routine. + */ + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset(XXH3_state_t* statePtr) +{ + return XXH3_64bits_reset(statePtr); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +{ + return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + return XXH3_64bits_reset_withSeed(statePtr, seed); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len) +{ + return XXH3_update(state, (const xxh_u8*)input, len, + XXH3_accumulate_512, XXH3_scrambleAcc); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) +{ + const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { XXH128_hash_t h128; + h128.low64 = XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (xxh_u64)state->totalLen * XXH_PRIME64_1); + h128.high64 = XXH3_mergeAccs(acc, + secret + state->secretLimit + XXH_STRIPE_LEN + - sizeof(acc) - XXH_SECRET_MERGEACCS_START, + ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); + return h128; + } + } + /* len <= XXH3_MIDSIZE_MAX : short code */ + if (state->seed) + return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); +} + +/* 128-bit utility functions */ + +#include <string.h> /* memcmp, memcpy */ + +/* return : 1 is equal, 0 if different */ +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) +{ + /* note : XXH128_hash_t is compact, it has no padding byte */ + return !(memcmp(&h1, &h2, sizeof(h1))); +} + +/* This prototype is compatible with stdlib's qsort(). + * return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) +{ + XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; + XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; + int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); + /* note : bets that, in most cases, hash values are different */ + if (hcmp) return hcmp; + return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); +} + + +/*====== Canonical representation ======*/ +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API void +XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) { + hash.high64 = XXH_swap64(hash.high64); + hash.low64 = XXH_swap64(hash.low64); + } + XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); + XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH128_hashFromCanonical(const XXH128_canonical_t* src) +{ + XXH128_hash_t h; + h.high64 = XXH_readBE64(src); + h.low64 = XXH_readBE64(src->digest + 8); + return h; +} + + + +/* ========================================== + * Secret generators + * ========================================== + */ +#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128) +{ + XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); + XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize) +{ +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(secretBuffer != NULL); + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); +#else + /* production mode, assert() are disabled */ + if (secretBuffer == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; +#endif + + if (customSeedSize == 0) { + customSeed = XXH3_kSecret; + customSeedSize = XXH_SECRET_DEFAULT_SIZE; + } +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(customSeed != NULL); +#else + if (customSeed == NULL) return XXH_ERROR; +#endif + + /* Fill secretBuffer with a copy of customSeed - repeat as needed */ + { size_t pos = 0; + while (pos < secretSize) { + size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); + memcpy((char*)secretBuffer + pos, customSeed, toCopy); + pos += toCopy; + } } + + { size_t const nbSeg16 = secretSize / 16; + size_t n; + XXH128_canonical_t scrambler; + XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); + for (n=0; n<nbSeg16; n++) { + XXH128_hash_t const h128 = XXH128(&scrambler, sizeof(scrambler), n); + XXH3_combine16((char*)secretBuffer + n*16, h128); + } + /* last segment */ + XXH3_combine16((char*)secretBuffer + secretSize - 16, XXH128_hashFromCanonical(&scrambler)); + } + return XXH_OK; +} + +/*! @ingroup xxh3_family */ +XXH_PUBLIC_API void +XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_t seed) +{ + XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + XXH3_initCustomSecret(secret, seed); + XXH_ASSERT(secretBuffer != NULL); + memcpy(secretBuffer, secret, XXH_SECRET_DEFAULT_SIZE); +} + + + +/* Pop our optimization override from above */ +#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ + && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */ +# pragma GCC pop_options +#endif + +#endif /* XXH_NO_LONG_LONG */ + +#endif /* XXH_NO_XXH3 */ + +/*! + * @} + */ +#endif /* XXH_IMPLEMENTATION */ #if defined (__cplusplus)
diff --git a/Utilities/cmzstd/lib/common/zstd_common.c b/Utilities/cmzstd/lib/common/zstd_common.c index 3d7e35b..3f04c22 100644 --- a/Utilities/cmzstd/lib/common/zstd_common.c +++ b/Utilities/cmzstd/lib/common/zstd_common.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,7 +14,6 @@ * Dependencies ***************************************/ #define ZSTD_DEPS_NEED_MALLOC -#include "zstd_deps.h" /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */ #include "error_private.h" #include "zstd_internal.h" @@ -47,37 +46,3 @@ /*! ZSTD_getErrorString() : * provides error code string from enum */ const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString(code); } - - - -/*=************************************************************** -* Custom allocator -****************************************************************/ -void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem) -{ - if (customMem.customAlloc) - return customMem.customAlloc(customMem.opaque, size); - return ZSTD_malloc(size); -} - -void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem) -{ - if (customMem.customAlloc) { - /* calloc implemented as malloc+memset; - * not as efficient as calloc, but next best guess for custom malloc */ - void* const ptr = customMem.customAlloc(customMem.opaque, size); - ZSTD_memset(ptr, 0, size); - return ptr; - } - return ZSTD_calloc(1, size); -} - -void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) -{ - if (ptr!=NULL) { - if (customMem.customFree) - customMem.customFree(customMem.opaque, ptr); - else - ZSTD_free(ptr); - } -}
diff --git a/Utilities/cmzstd/lib/common/zstd_deps.h b/Utilities/cmzstd/lib/common/zstd_deps.h index 1421134..4d767ae 100644 --- a/Utilities/cmzstd/lib/common/zstd_deps.h +++ b/Utilities/cmzstd/lib/common/zstd_deps.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/common/zstd_internal.h b/Utilities/cmzstd/lib/common/zstd_internal.h index 68252e9..1f942f2 100644 --- a/Utilities/cmzstd/lib/common/zstd_internal.h +++ b/Utilities/cmzstd/lib/common/zstd_internal.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -19,10 +19,8 @@ /*-************************************* * Dependencies ***************************************/ -#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) -#include <arm_neon.h> -#endif #include "compiler.h" +#include "cpu.h" #include "mem.h" #include "debug.h" /* assert, DEBUGLOG, RAWLOG, g_debuglevel */ #include "error_private.h" @@ -30,7 +28,6 @@ #include "../zstd.h" #define FSE_STATIC_LINKING_ONLY #include "fse.h" -#define HUF_STATIC_LINKING_ONLY #include "huf.h" #ifndef XXH_STATIC_LINKING_ONLY # define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ @@ -60,81 +57,7 @@ #undef MAX #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) - -/** - * Ignore: this is an internal helper. - * - * This is a helper function to help force C99-correctness during compilation. - * Under strict compilation modes, variadic macro arguments can't be empty. - * However, variadic function arguments can be. Using a function therefore lets - * us statically check that at least one (string) argument was passed, - * independent of the compilation flags. - */ -static INLINE_KEYWORD UNUSED_ATTR -void _force_has_format_string(const char *format, ...) { - (void)format; -} - -/** - * Ignore: this is an internal helper. - * - * We want to force this function invocation to be syntactically correct, but - * we don't want to force runtime evaluation of its arguments. - */ -#define _FORCE_HAS_FORMAT_STRING(...) \ - if (0) { \ - _force_has_format_string(__VA_ARGS__); \ - } - -/** - * Return the specified error if the condition evaluates to true. - * - * In debug modes, prints additional information. - * In order to do that (particularly, printing the conditional that failed), - * this can't just wrap RETURN_ERROR(). - */ -#define RETURN_ERROR_IF(cond, err, ...) \ - if (cond) { \ - RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \ - __FILE__, __LINE__, ZSTD_QUOTE(cond), ZSTD_QUOTE(ERROR(err))); \ - _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ - RAWLOG(3, ": " __VA_ARGS__); \ - RAWLOG(3, "\n"); \ - return ERROR(err); \ - } - -/** - * Unconditionally return the specified error. - * - * In debug modes, prints additional information. - */ -#define RETURN_ERROR(err, ...) \ - do { \ - RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", \ - __FILE__, __LINE__, ZSTD_QUOTE(ERROR(err))); \ - _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ - RAWLOG(3, ": " __VA_ARGS__); \ - RAWLOG(3, "\n"); \ - return ERROR(err); \ - } while(0); - -/** - * If the provided expression evaluates to an error code, returns that error code. - * - * In debug modes, prints additional information. - */ -#define FORWARD_IF_ERROR(err, ...) \ - do { \ - size_t const err_code = (err); \ - if (ERR_isError(err_code)) { \ - RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", \ - __FILE__, __LINE__, ZSTD_QUOTE(err), ERR_getErrorName(err_code)); \ - _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ - RAWLOG(3, ": " __VA_ARGS__); \ - RAWLOG(3, "\n"); \ - return err_code; \ - } \ - } while(0); +#define BOUNDED(min,val,max) (MAX(min,MIN(val,max))) /*-************************************* @@ -143,7 +66,6 @@ #define ZSTD_OPT_NUM (1<<12) #define ZSTD_REP_NUM 3 /* number of repcodes */ -#define ZSTD_REP_MOVE (ZSTD_REP_NUM-1) static UNUSED_ATTR const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 }; #define KB *(1 <<10) @@ -170,9 +92,9 @@ #define ZSTD_FRAMECHECKSUMSIZE 4 #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ -#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ +#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */) /* for a non-null block */ +#define MIN_LITERALS_FOR_4_STREAMS 6 -#define HufLog 12 typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; #define LONGNBSEQ 0x7F00 @@ -180,6 +102,7 @@ #define MINMATCH 3 #define Litbits 8 +#define LitHufLog 11 #define MaxLit ((1<<Litbits) - 1) #define MaxML 52 #define MaxLL 35 @@ -190,12 +113,14 @@ #define LLFSELog 9 #define OffFSELog 8 #define MaxFSELog MAX(MAX(MLFSELog, LLFSELog), OffFSELog) +#define MaxMLBits 16 +#define MaxLLBits 16 #define ZSTD_MAX_HUF_HEADER_SIZE 128 /* header + <= 127 byte tree description */ /* Each table cannot take more than #symbols * FSELog bits */ #define ZSTD_MAX_FSE_HEADERS_SIZE (((MaxML + 1) * MLFSELog + (MaxLL + 1) * LLFSELog + (MaxOff + 1) * OffFSELog + 7) / 8) -static UNUSED_ATTR const U32 LL_bits[MaxLL+1] = { +static UNUSED_ATTR const U8 LL_bits[MaxLL+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, @@ -212,7 +137,7 @@ #define LL_DEFAULTNORMLOG 6 /* for static allocation */ static UNUSED_ATTR const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG; -static UNUSED_ATTR const U32 ML_bits[MaxML+1] = { +static UNUSED_ATTR const U8 ML_bits[MaxML+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -247,19 +172,30 @@ * Shared functions to include for inlining *********************************************/ static void ZSTD_copy8(void* dst, const void* src) { -#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) +#if defined(ZSTD_ARCH_ARM_NEON) vst1_u8((uint8_t*)dst, vld1_u8((const uint8_t*)src)); #else ZSTD_memcpy(dst, src, 8); #endif } - #define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; } + +/* Need to use memmove here since the literal buffer can now be located within + the dst buffer. In circumstances where the op "catches up" to where the + literal buffer is, there can be partial overlaps in this call on the final + copy if the literal is being shifted by less than 16 bytes. */ static void ZSTD_copy16(void* dst, const void* src) { -#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) +#if defined(ZSTD_ARCH_ARM_NEON) vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src)); +#elif defined(ZSTD_ARCH_X86_SSE2) + _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src)); +#elif defined(__clang__) + ZSTD_memmove(dst, src, 16); #else - ZSTD_memcpy(dst, src, 16); + /* ZSTD_memmove is not inlined properly by gcc */ + BYTE copy16_buf[16]; + ZSTD_memcpy(copy16_buf, src, 16); + ZSTD_memcpy(dst, copy16_buf, 16); #endif } #define COPY16(d,s) { ZSTD_copy16(d,s); d+=16; s+=16; } @@ -288,8 +224,6 @@ BYTE* op = (BYTE*)dst; BYTE* const oend = op + length; - assert(diff >= 8 || (ovtype == ZSTD_no_overlap && diff <= -WILDCOPY_VECLEN)); - if (ovtype == ZSTD_overlap_src_before_dst && diff < WILDCOPY_VECLEN) { /* Handle short offset copies. */ do { @@ -303,12 +237,6 @@ * one COPY16() in the first call. Then, do two calls per loop since * at that point it is more likely to have a high trip count. */ -#ifdef __aarch64__ - do { - COPY16(op, ip); - } - while (op < oend); -#else ZSTD_copy16(op, ip); if (16 >= length) return; op += 16; @@ -318,7 +246,6 @@ COPY16(op, ip); } while (op < oend); -#endif } } @@ -352,9 +279,9 @@ * Private declarations *********************************************/ typedef struct seqDef_s { - U32 offset; /* offset == rawOffset + ZSTD_REP_NUM, or equivalently, offCode + 1 */ + U32 offBase; /* offBase == Offset + ZSTD_REP_NUM, or repcode 1,2,3 */ U16 litLength; - U16 matchLength; + U16 mlBase; /* mlBase == matchLength - MINMATCH */ } seqDef; /* Controls whether seqStore has a single "long" litLength or matchLength. See seqStore_t. */ @@ -367,11 +294,11 @@ typedef struct { seqDef* sequencesStart; seqDef* sequences; /* ptr to end of sequences */ - BYTE* litStart; - BYTE* lit; /* ptr to end of literals */ - BYTE* llCode; - BYTE* mlCode; - BYTE* ofCode; + BYTE* litStart; + BYTE* lit; /* ptr to end of literals */ + BYTE* llCode; + BYTE* mlCode; + BYTE* ofCode; size_t maxNbSeq; size_t maxNbLit; @@ -379,8 +306,8 @@ * in the seqStore that has a value larger than U16 (if it exists). To do so, we increment * the existing value of the litLength or matchLength by 0x10000. */ - ZSTD_longLengthType_e longLengthType; - U32 longLengthPos; /* Index of the sequence to apply long length modification to */ + ZSTD_longLengthType_e longLengthType; + U32 longLengthPos; /* Index of the sequence to apply long length modification to */ } seqStore_t; typedef struct { @@ -396,13 +323,13 @@ { ZSTD_sequenceLength seqLen; seqLen.litLength = seq->litLength; - seqLen.matchLength = seq->matchLength + MINMATCH; + seqLen.matchLength = seq->mlBase + MINMATCH; if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) { if (seqStore->longLengthType == ZSTD_llt_literalLength) { - seqLen.litLength += 0xFFFF; + seqLen.litLength += 0x10000; } if (seqStore->longLengthType == ZSTD_llt_matchLength) { - seqLen.matchLength += 0xFFFF; + seqLen.matchLength += 0x10000; } } return seqLen; @@ -415,46 +342,13 @@ * `decompressedBound != ZSTD_CONTENTSIZE_ERROR` */ typedef struct { + size_t nbBlocks; size_t compressedSize; unsigned long long decompressedBound; } ZSTD_frameSizeInfo; /* decompress & legacy */ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */ -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ - -/* custom memory allocation functions */ -void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem); -void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem); -void ZSTD_customFree(void* ptr, ZSTD_customMem customMem); - - -MEM_STATIC U32 ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */ -{ - assert(val != 0); - { -# if defined(_MSC_VER) /* Visual */ -# if STATIC_BMI2 == 1 - return _lzcnt_u32(val)^31; -# else - unsigned long r=0; - return _BitScanReverse(&r, val) ? (unsigned)r : 0; -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ - return __builtin_clz (val) ^ 31; -# elif defined(__ICCARM__) /* IAR Intrinsic */ - return 31 - __CLZ(val); -# else /* Software version */ - static const U32 DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; - U32 v = val; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return DeBruijnClz[(v * 0x07C4ACDDU) >> 27]; -# endif - } -} +int ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ /* ZSTD_invalidateRepCodes() : @@ -482,6 +376,14 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, const void* src, size_t srcSize); +/** + * @returns true iff the CPU supports dynamic BMI2 dispatch. + */ +MEM_STATIC int ZSTD_cpuSupportsBmi2(void) +{ + ZSTD_cpuid_t cpuid = ZSTD_cpuid(); + return ZSTD_cpuid_bmi1(cpuid) && ZSTD_cpuid_bmi2(cpuid); +} #if defined (__cplusplus) }
diff --git a/Utilities/cmzstd/lib/common/zstd_trace.h b/Utilities/cmzstd/lib/common/zstd_trace.h index 485cadf..da20534 100644 --- a/Utilities/cmzstd/lib/common/zstd_trace.h +++ b/Utilities/cmzstd/lib/common/zstd_trace.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -17,8 +17,17 @@ #include <stddef.h> -/* weak symbol support */ -#if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && defined(__GNUC__) && \ +/* weak symbol support + * For now, enable conservatively: + * - Only GNUC + * - Only ELF + * - Only x86-64, i386 and aarch64 + * Also, explicitly disable on platforms known not to work so they aren't + * forgotten in the future. + */ +#if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && \ + defined(__GNUC__) && defined(__ELF__) && \ + (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(__aarch64__)) && \ !defined(__APPLE__) && !defined(_WIN32) && !defined(__MINGW32__) && \ !defined(__CYGWIN__) && !defined(_AIX) # define ZSTD_HAVE_WEAK_SYMBOLS 1
diff --git a/Utilities/cmzstd/lib/compress/clevels.h b/Utilities/cmzstd/lib/compress/clevels.h new file mode 100644 index 0000000..c18da46 --- /dev/null +++ b/Utilities/cmzstd/lib/compress/clevels.h
@@ -0,0 +1,134 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#ifndef ZSTD_CLEVELS_H +#define ZSTD_CLEVELS_H + +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */ +#include "../zstd.h" + +/*-===== Pre-defined compression levels =====-*/ + +#define ZSTD_MAX_CLEVEL 22 + +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif + +static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { +{ /* "default" - for any srcSize > 256 KB */ + /* W, C, H, S, L, TL, strat */ + { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */ + { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */ + { 20, 15, 16, 1, 6, 0, ZSTD_fast }, /* level 2 */ + { 21, 16, 17, 1, 5, 0, ZSTD_dfast }, /* level 3 */ + { 21, 18, 18, 1, 5, 0, ZSTD_dfast }, /* level 4 */ + { 21, 18, 19, 3, 5, 2, ZSTD_greedy }, /* level 5 */ + { 21, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6 */ + { 21, 19, 20, 4, 5, 8, ZSTD_lazy }, /* level 7 */ + { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 8 */ + { 22, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */ + { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 10 */ + { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 11 */ + { 22, 22, 23, 6, 5, 32, ZSTD_lazy2 }, /* level 12 */ + { 22, 22, 22, 4, 5, 32, ZSTD_btlazy2 }, /* level 13 */ + { 22, 22, 23, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */ + { 22, 23, 23, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */ + { 22, 22, 22, 5, 5, 48, ZSTD_btopt }, /* level 16 */ + { 23, 23, 22, 5, 4, 64, ZSTD_btopt }, /* level 17 */ + { 23, 23, 22, 6, 3, 64, ZSTD_btultra }, /* level 18 */ + { 23, 24, 22, 7, 3,256, ZSTD_btultra2}, /* level 19 */ + { 25, 25, 23, 7, 3,256, ZSTD_btultra2}, /* level 20 */ + { 26, 26, 24, 7, 3,512, ZSTD_btultra2}, /* level 21 */ + { 27, 27, 25, 9, 3,999, ZSTD_btultra2}, /* level 22 */ +}, +{ /* for srcSize <= 256 KB */ + /* W, C, H, S, L, T, strat */ + { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ + { 18, 13, 14, 1, 6, 0, ZSTD_fast }, /* level 1 */ + { 18, 14, 14, 1, 5, 0, ZSTD_dfast }, /* level 2 */ + { 18, 16, 16, 1, 4, 0, ZSTD_dfast }, /* level 3 */ + { 18, 16, 17, 3, 5, 2, ZSTD_greedy }, /* level 4.*/ + { 18, 17, 18, 5, 5, 2, ZSTD_greedy }, /* level 5.*/ + { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/ + { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */ + { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ + { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ + { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ + { 18, 18, 19, 5, 4, 12, ZSTD_btlazy2 }, /* level 11.*/ + { 18, 19, 19, 7, 4, 12, ZSTD_btlazy2 }, /* level 12.*/ + { 18, 18, 19, 4, 4, 16, ZSTD_btopt }, /* level 13 */ + { 18, 18, 19, 4, 3, 32, ZSTD_btopt }, /* level 14.*/ + { 18, 18, 19, 6, 3,128, ZSTD_btopt }, /* level 15.*/ + { 18, 19, 19, 6, 3,128, ZSTD_btultra }, /* level 16.*/ + { 18, 19, 19, 8, 3,256, ZSTD_btultra }, /* level 17.*/ + { 18, 19, 19, 6, 3,128, ZSTD_btultra2}, /* level 18.*/ + { 18, 19, 19, 8, 3,256, ZSTD_btultra2}, /* level 19.*/ + { 18, 19, 19, 10, 3,512, ZSTD_btultra2}, /* level 20.*/ + { 18, 19, 19, 12, 3,512, ZSTD_btultra2}, /* level 21.*/ + { 18, 19, 19, 13, 3,999, ZSTD_btultra2}, /* level 22.*/ +}, +{ /* for srcSize <= 128 KB */ + /* W, C, H, S, L, T, strat */ + { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ + { 17, 12, 13, 1, 6, 0, ZSTD_fast }, /* level 1 */ + { 17, 13, 15, 1, 5, 0, ZSTD_fast }, /* level 2 */ + { 17, 15, 16, 2, 5, 0, ZSTD_dfast }, /* level 3 */ + { 17, 17, 17, 2, 4, 0, ZSTD_dfast }, /* level 4 */ + { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */ + { 17, 16, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */ + { 17, 16, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */ + { 17, 16, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ + { 17, 16, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ + { 17, 16, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ + { 17, 17, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 11 */ + { 17, 18, 17, 7, 4, 12, ZSTD_btlazy2 }, /* level 12 */ + { 17, 18, 17, 3, 4, 12, ZSTD_btopt }, /* level 13.*/ + { 17, 18, 17, 4, 3, 32, ZSTD_btopt }, /* level 14.*/ + { 17, 18, 17, 6, 3,256, ZSTD_btopt }, /* level 15.*/ + { 17, 18, 17, 6, 3,128, ZSTD_btultra }, /* level 16.*/ + { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 17.*/ + { 17, 18, 17, 10, 3,512, ZSTD_btultra }, /* level 18.*/ + { 17, 18, 17, 5, 3,256, ZSTD_btultra2}, /* level 19.*/ + { 17, 18, 17, 7, 3,512, ZSTD_btultra2}, /* level 20.*/ + { 17, 18, 17, 9, 3,512, ZSTD_btultra2}, /* level 21.*/ + { 17, 18, 17, 11, 3,999, ZSTD_btultra2}, /* level 22.*/ +}, +{ /* for srcSize <= 16 KB */ + /* W, C, H, S, L, T, strat */ + { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ + { 14, 14, 15, 1, 5, 0, ZSTD_fast }, /* level 1 */ + { 14, 14, 15, 1, 4, 0, ZSTD_fast }, /* level 2 */ + { 14, 14, 15, 2, 4, 0, ZSTD_dfast }, /* level 3 */ + { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4 */ + { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/ + { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */ + { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */ + { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/ + { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/ + { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/ + { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/ + { 14, 15, 14, 4, 3, 24, ZSTD_btopt }, /* level 12.*/ + { 14, 15, 14, 5, 3, 32, ZSTD_btultra }, /* level 13.*/ + { 14, 15, 15, 6, 3, 64, ZSTD_btultra }, /* level 14.*/ + { 14, 15, 15, 7, 3,256, ZSTD_btultra }, /* level 15.*/ + { 14, 15, 15, 5, 3, 48, ZSTD_btultra2}, /* level 16.*/ + { 14, 15, 15, 6, 3,128, ZSTD_btultra2}, /* level 17.*/ + { 14, 15, 15, 7, 3,256, ZSTD_btultra2}, /* level 18.*/ + { 14, 15, 15, 8, 3,256, ZSTD_btultra2}, /* level 19.*/ + { 14, 15, 15, 8, 3,512, ZSTD_btultra2}, /* level 20.*/ + { 14, 15, 15, 9, 3,512, ZSTD_btultra2}, /* level 21.*/ + { 14, 15, 15, 10, 3,999, ZSTD_btultra2}, /* level 22.*/ +}, +}; + + + +#endif /* ZSTD_CLEVELS_H */
diff --git a/Utilities/cmzstd/lib/compress/fse_compress.c b/Utilities/cmzstd/lib/compress/fse_compress.c index 1b6a076..5d37708 100644 --- a/Utilities/cmzstd/lib/compress/fse_compress.c +++ b/Utilities/cmzstd/lib/compress/fse_compress.c
@@ -1,6 +1,6 @@ /* ****************************************************************** * FSE : Finite State Entropy encoder - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -26,6 +26,7 @@ #define ZSTD_DEPS_NEED_MALLOC #define ZSTD_DEPS_NEED_MATH64 #include "../common/zstd_deps.h" /* ZSTD_malloc, ZSTD_free, ZSTD_memcpy, ZSTD_memset */ +#include "../common/bits.h" /* ZSTD_highbit32 */ /* ************************************************************** @@ -75,13 +76,14 @@ void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableLog ? tableSize>>1 : 1) ; FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); U32 const step = FSE_TABLESTEP(tableSize); + U32 const maxSV1 = maxSymbolValue+1; - U32* cumul = (U32*)workSpace; - FSE_FUNCTION_TYPE* tableSymbol = (FSE_FUNCTION_TYPE*)(cumul + (maxSymbolValue + 2)); + U16* cumul = (U16*)workSpace; /* size = maxSV1 */ + FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)(cumul + (maxSV1+1)); /* size = tableSize */ U32 highThreshold = tableSize-1; - if ((size_t)workSpace & 3) return ERROR(GENERIC); /* Must be 4 byte aligned */ + assert(((size_t)workSpace & 1) == 0); /* Must be 2 bytes-aligned */ if (FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog) > wkspSize) return ERROR(tableLog_tooLarge); /* CTable header */ tableU16[-2] = (U16) tableLog; @@ -89,7 +91,7 @@ assert(tableLog < 16); /* required for threshold strategy to work */ /* For explanations on how to distribute symbol values over the table : - * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ + * https://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */ #ifdef __clang_analyzer__ ZSTD_memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize); /* useless initialization, just to keep scan-build happy */ @@ -98,20 +100,61 @@ /* symbol start positions */ { U32 u; cumul[0] = 0; - for (u=1; u <= maxSymbolValue+1; u++) { + for (u=1; u <= maxSV1; u++) { if (normalizedCounter[u-1]==-1) { /* Low proba symbol */ cumul[u] = cumul[u-1] + 1; tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u-1); } else { - cumul[u] = cumul[u-1] + normalizedCounter[u-1]; + assert(normalizedCounter[u-1] >= 0); + cumul[u] = cumul[u-1] + (U16)normalizedCounter[u-1]; + assert(cumul[u] >= cumul[u-1]); /* no overflow */ } } - cumul[maxSymbolValue+1] = tableSize+1; + cumul[maxSV1] = (U16)(tableSize+1); } /* Spread symbols */ - { U32 position = 0; + if (highThreshold == tableSize - 1) { + /* Case for no low prob count symbols. Lay down 8 bytes at a time + * to reduce branch misses since we are operating on a small block + */ + BYTE* const spread = tableSymbol + tableSize; /* size = tableSize + 8 (may write beyond tableSize) */ + { U64 const add = 0x0101010101010101ull; + size_t pos = 0; + U64 sv = 0; + U32 s; + for (s=0; s<maxSV1; ++s, sv += add) { + int i; + int const n = normalizedCounter[s]; + MEM_write64(spread + pos, sv); + for (i = 8; i < n; i += 8) { + MEM_write64(spread + pos + i, sv); + } + assert(n>=0); + pos += (size_t)n; + } + } + /* Spread symbols across the table. Lack of lowprob symbols means that + * we don't need variable sized inner loop, so we can unroll the loop and + * reduce branch misses. + */ + { size_t position = 0; + size_t s; + size_t const unroll = 2; /* Experimentally determined optimal unroll */ + assert(tableSize % unroll == 0); /* FSE_MIN_TABLELOG is 5 */ + for (s = 0; s < (size_t)tableSize; s += unroll) { + size_t u; + for (u = 0; u < unroll; ++u) { + size_t const uPosition = (position + (u * step)) & tableMask; + tableSymbol[uPosition] = spread[s + u]; + } + position = (position + (unroll * step)) & tableMask; + } + assert(position == 0); /* Must have initialized all positions */ + } + } else { + U32 position = 0; U32 symbol; - for (symbol=0; symbol<=maxSymbolValue; symbol++) { + for (symbol=0; symbol<maxSV1; symbol++) { int nbOccurrences; int const freq = normalizedCounter[symbol]; for (nbOccurrences=0; nbOccurrences<freq; nbOccurrences++) { @@ -120,7 +163,6 @@ while (position > highThreshold) position = (position + step) & tableMask; /* Low proba area */ } } - assert(position==0); /* Must have initialized all positions */ } @@ -144,16 +186,17 @@ case -1: case 1: symbolTT[s].deltaNbBits = (tableLog << 16) - (1<<tableLog); - symbolTT[s].deltaFindState = total - 1; + assert(total <= INT_MAX); + symbolTT[s].deltaFindState = (int)(total - 1); total ++; break; default : - { - U32 const maxBitsOut = tableLog - BIT_highbit32 (normalizedCounter[s]-1); - U32 const minStatePlus = normalizedCounter[s] << maxBitsOut; + assert(normalizedCounter[s] > 1); + { U32 const maxBitsOut = tableLog - ZSTD_highbit32 ((U32)normalizedCounter[s]-1); + U32 const minStatePlus = (U32)normalizedCounter[s] << maxBitsOut; symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus; - symbolTT[s].deltaFindState = total - normalizedCounter[s]; - total += normalizedCounter[s]; + symbolTT[s].deltaFindState = (int)(total - (unsigned)normalizedCounter[s]); + total += (unsigned)normalizedCounter[s]; } } } } #if 0 /* debug : symbol costs */ @@ -164,32 +207,26 @@ symbol, normalizedCounter[symbol], FSE_getMaxNbBits(symbolTT, symbol), (double)FSE_bitCost(symbolTT, tableLog, symbol, 8) / 256); - } - } + } } #endif return 0; } -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) -{ - FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE]; /* memset() is not necessary, even if static analyzer complain about it */ - return FSE_buildCTable_wksp(ct, normalizedCounter, maxSymbolValue, tableLog, tableSymbol, sizeof(tableSymbol)); -} -#endif - #ifndef FSE_COMMONDEFS_ONLY - /*-************************************************************** * FSE NCount encoding ****************************************************************/ size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog) { - size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog) >> 3) + 3; + size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog + + 4 /* bitCount initialized at 4 */ + + 2 /* first two symbols may use one additional bit each */) / 8) + + 1 /* round up to whole nb bytes */ + + 2 /* additional two bytes for bitstream flush */; return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */ } @@ -306,21 +343,11 @@ * FSE Compression Code ****************************************************************/ -FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog) -{ - size_t size; - if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX; - size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32); - return (FSE_CTable*)ZSTD_malloc(size); -} - -void FSE_freeCTable (FSE_CTable* ct) { ZSTD_free(ct); } - /* provides the minimum logSize to safely represent a distribution */ static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue) { - U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1; - U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2; + U32 minBitsSrc = ZSTD_highbit32((U32)(srcSize)) + 1; + U32 minBitsSymbols = ZSTD_highbit32(maxSymbolValue) + 2; U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols; assert(srcSize > 1); /* Not supported, RLE should be used instead */ return minBits; @@ -328,7 +355,7 @@ unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus) { - U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus; + U32 maxBitsSrc = ZSTD_highbit32((U32)(srcSize - 1)) - minus; U32 tableLog = maxTableLog; U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue); assert(srcSize > 1); /* Not supported, RLE should be used instead */ @@ -496,40 +523,6 @@ return tableLog; } - -/* fake FSE_CTable, for raw (uncompressed) input */ -size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits) -{ - const unsigned tableSize = 1 << nbBits; - const unsigned tableMask = tableSize - 1; - const unsigned maxSymbolValue = tableMask; - void* const ptr = ct; - U16* const tableU16 = ( (U16*) ptr) + 2; - void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableSize>>1); /* assumption : tableLog >= 1 */ - FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT); - unsigned s; - - /* Sanity checks */ - if (nbBits < 1) return ERROR(GENERIC); /* min size */ - - /* header */ - tableU16[-2] = (U16) nbBits; - tableU16[-1] = (U16) maxSymbolValue; - - /* Build table */ - for (s=0; s<tableSize; s++) - tableU16[s] = (U16)(tableSize + s); - - /* Build Symbol Transformation Table */ - { const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits); - for (s=0; s<=maxSymbolValue; s++) { - symbolTT[s].deltaNbBits = deltaNbBits; - symbolTT[s].deltaFindState = s-1; - } } - - return 0; -} - /* fake FSE_CTable, for rle input (always same symbol) */ size_t FSE_buildCTable_rle (FSE_CTable* ct, BYTE symbolValue) { @@ -628,82 +621,4 @@ size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); } -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -/* FSE_compress_wksp() : - * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`). - * `wkspSize` size must be `(1<<tableLog)`. - */ -size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize) -{ - BYTE* const ostart = (BYTE*) dst; - BYTE* op = ostart; - BYTE* const oend = ostart + dstSize; - - unsigned count[FSE_MAX_SYMBOL_VALUE+1]; - S16 norm[FSE_MAX_SYMBOL_VALUE+1]; - FSE_CTable* CTable = (FSE_CTable*)workSpace; - size_t const CTableSize = FSE_CTABLE_SIZE_U32(tableLog, maxSymbolValue); - void* scratchBuffer = (void*)(CTable + CTableSize); - size_t const scratchBufferSize = wkspSize - (CTableSize * sizeof(FSE_CTable)); - -#ifdef __clang_analyzer__ - memset(norm, 0, sizeof(norm)); -#endif - - /* init conditions */ - if (wkspSize < FSE_COMPRESS_WKSP_SIZE_U32(tableLog, maxSymbolValue)) return ERROR(tableLog_tooLarge); - if (srcSize <= 1) return 0; /* Not compressible */ - if (!maxSymbolValue) maxSymbolValue = FSE_MAX_SYMBOL_VALUE; - if (!tableLog) tableLog = FSE_DEFAULT_TABLELOG; - - /* Scan input and build symbol stats */ - { CHECK_V_F(maxCount, HIST_count_wksp(count, &maxSymbolValue, src, srcSize, scratchBuffer, scratchBufferSize) ); - if (maxCount == srcSize) return 1; /* only a single symbol in src : rle */ - if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */ - if (maxCount < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */ - } - - tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue); - CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue, /* useLowProbCount */ srcSize >= 2048) ); - - /* Write table description header */ - { CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) ); - op += nc_err; - } - - /* Compress */ - CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, scratchBufferSize) ); - { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, src, srcSize, CTable) ); - if (cSize == 0) return 0; /* not enough space for compressed data */ - op += cSize; - } - - /* check compressibility */ - if ( (size_t)(op-ostart) >= srcSize-1 ) return 0; - - return op-ostart; -} - -typedef struct { - FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)]; - union { - U32 hist_wksp[HIST_WKSP_SIZE_U32]; - BYTE scratchBuffer[1 << FSE_MAX_TABLELOG]; - } workspace; -} fseWkspMax_t; - -size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog) -{ - fseWkspMax_t scratchBuffer; - DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_COMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */ - if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); - return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer)); -} - -size_t FSE_compress (void* dst, size_t dstCapacity, const void* src, size_t srcSize) -{ - return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG); -} -#endif - #endif /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/compress/hist.c b/Utilities/cmzstd/lib/compress/hist.c index 073c57e..e2fb431 100644 --- a/Utilities/cmzstd/lib/compress/hist.c +++ b/Utilities/cmzstd/lib/compress/hist.c
@@ -1,7 +1,7 @@ /* ****************************************************************** * hist : Histogram functions * part of Finite State Entropy project - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/compress/hist.h b/Utilities/cmzstd/lib/compress/hist.h index 228ed48..887896b 100644 --- a/Utilities/cmzstd/lib/compress/hist.h +++ b/Utilities/cmzstd/lib/compress/hist.h
@@ -1,7 +1,7 @@ /* ****************************************************************** * hist : Histogram functions * part of Finite State Entropy project - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/compress/huf_compress.c b/Utilities/cmzstd/lib/compress/huf_compress.c index 485906e..2987187 100644 --- a/Utilities/cmzstd/lib/compress/huf_compress.c +++ b/Utilities/cmzstd/lib/compress/huf_compress.c
@@ -1,6 +1,6 @@ /* ****************************************************************** * Huffman encoder, part of New Generation Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -29,9 +29,9 @@ #include "hist.h" #define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */ #include "../common/fse.h" /* header compression */ -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/error_private.h" +#include "../common/bits.h" /* ZSTD_highbit32 */ /* ************************************************************** @@ -42,17 +42,93 @@ /* ************************************************************** -* Utils +* Required declarations ****************************************************************/ -unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue) +typedef struct nodeElt_s { + U32 count; + U16 parent; + BYTE byte; + BYTE nbBits; +} nodeElt; + + +/* ************************************************************** +* Debug Traces +****************************************************************/ + +#if DEBUGLEVEL >= 2 + +static size_t showU32(const U32* arr, size_t size) { - return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + size_t u; + for (u=0; u<size; u++) { + RAWLOG(6, " %u", arr[u]); (void)arr; + } + RAWLOG(6, " \n"); + return size; } +static size_t HUF_getNbBits(HUF_CElt elt); + +static size_t showCTableBits(const HUF_CElt* ctable, size_t size) +{ + size_t u; + for (u=0; u<size; u++) { + RAWLOG(6, " %zu", HUF_getNbBits(ctable[u])); (void)ctable; + } + RAWLOG(6, " \n"); + return size; + +} + +static size_t showHNodeSymbols(const nodeElt* hnode, size_t size) +{ + size_t u; + for (u=0; u<size; u++) { + RAWLOG(6, " %u", hnode[u].byte); (void)hnode; + } + RAWLOG(6, " \n"); + return size; +} + +static size_t showHNodeBits(const nodeElt* hnode, size_t size) +{ + size_t u; + for (u=0; u<size; u++) { + RAWLOG(6, " %u", hnode[u].nbBits); (void)hnode; + } + RAWLOG(6, " \n"); + return size; +} + +#endif + /* ******************************************************* * HUF : Huffman block compression *********************************************************/ +#define HUF_WORKSPACE_MAX_ALIGNMENT 8 + +static void* HUF_alignUpWorkspace(void* workspace, size_t* workspaceSizePtr, size_t align) +{ + size_t const mask = align - 1; + size_t const rem = (size_t)workspace & mask; + size_t const add = (align - rem) & mask; + BYTE* const aligned = (BYTE*)workspace + add; + assert((align & (align - 1)) == 0); /* pow 2 */ + assert(align <= HUF_WORKSPACE_MAX_ALIGNMENT); + if (*workspaceSizePtr >= add) { + assert(add < align); + assert(((size_t)aligned & mask) == 0); + *workspaceSizePtr -= add; + return aligned; + } else { + *workspaceSizePtr = 0; + return NULL; + } +} + + /* HUF_compressWeights() : * Same as FSE_compress(), but dedicated to huff0's weights compression. * The use case needs much less stack memory. @@ -67,7 +143,10 @@ S16 norm[HUF_TABLELOG_MAX+1]; } HUF_CompressWeightsWksp; -static size_t HUF_compressWeights(void* dst, size_t dstSize, const void* weightTable, size_t wtSize, void* workspace, size_t workspaceSize) +static size_t +HUF_compressWeights(void* dst, size_t dstSize, + const void* weightTable, size_t wtSize, + void* workspace, size_t workspaceSize) { BYTE* const ostart = (BYTE*) dst; BYTE* op = ostart; @@ -75,7 +154,7 @@ unsigned maxSymbolValue = HUF_TABLELOG_MAX; U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER; - HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)workspace; + HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)HUF_alignUpWorkspace(workspace, &workspaceSize, ZSTD_ALIGNOF(U32)); if (workspaceSize < sizeof(HUF_CompressWeightsWksp)) return ERROR(GENERIC); @@ -106,6 +185,40 @@ return (size_t)(op-ostart); } +static size_t HUF_getNbBits(HUF_CElt elt) +{ + return elt & 0xFF; +} + +static size_t HUF_getNbBitsFast(HUF_CElt elt) +{ + return elt; +} + +static size_t HUF_getValue(HUF_CElt elt) +{ + return elt & ~(size_t)0xFF; +} + +static size_t HUF_getValueFast(HUF_CElt elt) +{ + return elt; +} + +static void HUF_setNbBits(HUF_CElt* elt, size_t nbBits) +{ + assert(nbBits <= HUF_TABLELOG_ABSOLUTEMAX); + *elt = nbBits; +} + +static void HUF_setValue(HUF_CElt* elt, size_t value) +{ + size_t const nbBits = HUF_getNbBits(*elt); + if (nbBits > 0) { + assert((value >> nbBits) == 0); + *elt |= value << (sizeof(HUF_CElt) * 8 - nbBits); + } +} typedef struct { HUF_CompressWeightsWksp wksp; @@ -117,9 +230,12 @@ const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize) { + HUF_CElt const* const ct = CTable + 1; BYTE* op = (BYTE*)dst; U32 n; - HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)workspace; + HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)HUF_alignUpWorkspace(workspace, &workspaceSize, ZSTD_ALIGNOF(U32)); + + HUF_STATIC_ASSERT(HUF_CTABLE_WORKSPACE_SIZE >= sizeof(HUF_WriteCTableWksp)); /* check conditions */ if (workspaceSize < sizeof(HUF_WriteCTableWksp)) return ERROR(GENERIC); @@ -130,9 +246,10 @@ for (n=1; n<huffLog+1; n++) wksp->bitsToWeight[n] = (BYTE)(huffLog + 1 - n); for (n=0; n<maxSymbolValue; n++) - wksp->huffWeight[n] = wksp->bitsToWeight[CTable[n].nbBits]; + wksp->huffWeight[n] = wksp->bitsToWeight[HUF_getNbBits(ct[n])]; /* attempt weights compression by FSE */ + if (maxDstSize < 1) return ERROR(dstSize_tooSmall); { CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, wksp->huffWeight, maxSymbolValue, &wksp->wksp, sizeof(wksp->wksp)) ); if ((hSize>1) & (hSize < maxSymbolValue/2)) { /* FSE compressed */ op[0] = (BYTE)hSize; @@ -149,16 +266,6 @@ return ((maxSymbolValue+1)/2) + 1; } -/*! HUF_writeCTable() : - `CTable` : Huffman tree to save, using huf representation. - @return : size of saved CTable */ -size_t HUF_writeCTable (void* dst, size_t maxDstSize, - const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog) -{ - HUF_WriteCTableWksp wksp; - return HUF_writeCTable_wksp(dst, maxDstSize, CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp)); -} - size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* hasZeroWeights) { @@ -166,6 +273,7 @@ U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1]; /* large enough for values from 0 to 16 */ U32 tableLog = 0; U32 nbSymbols = 0; + HUF_CElt* const ct = CTable + 1; /* get symbol weights */ CHECK_V_F(readSize, HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX+1, rankVal, &nbSymbols, &tableLog, src, srcSize)); @@ -175,6 +283,8 @@ if (tableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); if (nbSymbols > *maxSymbolValuePtr+1) return ERROR(maxSymbolValue_tooSmall); + CTable[0] = tableLog; + /* Prepare base value per rank */ { U32 n, nextRankStart = 0; for (n=1; n<=tableLog; n++) { @@ -186,13 +296,13 @@ /* fill nbBits */ { U32 n; for (n=0; n<nbSymbols; n++) { const U32 w = huffWeight[n]; - CTable[n].nbBits = (BYTE)(tableLog + 1 - w) & -(w != 0); + HUF_setNbBits(ct + n, (BYTE)(tableLog + 1 - w) & -(w != 0)); } } /* fill val */ { U16 nbPerRank[HUF_TABLELOG_MAX+2] = {0}; /* support w=0=>n=tableLog+1 */ U16 valPerRank[HUF_TABLELOG_MAX+2] = {0}; - { U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[CTable[n].nbBits]++; } + { U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[HUF_getNbBits(ct[n])]++; } /* determine stating value per rank */ valPerRank[tableLog+1] = 0; /* for w==0 */ { U16 min = 0; @@ -202,77 +312,73 @@ min >>= 1; } } /* assign value within rank, symbol order */ - { U32 n; for (n=0; n<nbSymbols; n++) CTable[n].val = valPerRank[CTable[n].nbBits]++; } + { U32 n; for (n=0; n<nbSymbols; n++) HUF_setValue(ct + n, valPerRank[HUF_getNbBits(ct[n])]++); } } *maxSymbolValuePtr = nbSymbols - 1; return readSize; } -U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue) +U32 HUF_getNbBitsFromCTable(HUF_CElt const* CTable, U32 symbolValue) { - const HUF_CElt* table = (const HUF_CElt*)symbolTable; + const HUF_CElt* const ct = CTable + 1; assert(symbolValue <= HUF_SYMBOLVALUE_MAX); - return table[symbolValue].nbBits; + return (U32)HUF_getNbBits(ct[symbolValue]); } -typedef struct nodeElt_s { - U32 count; - U16 parent; - BYTE byte; - BYTE nbBits; -} nodeElt; - /** * HUF_setMaxHeight(): - * Enforces maxNbBits on the Huffman tree described in huffNode. + * Try to enforce @targetNbBits on the Huffman tree described in @huffNode. * - * It sets all nodes with nbBits > maxNbBits to be maxNbBits. Then it adjusts - * the tree to so that it is a valid canonical Huffman tree. + * It attempts to convert all nodes with nbBits > @targetNbBits + * to employ @targetNbBits instead. Then it adjusts the tree + * so that it remains a valid canonical Huffman tree. * * @pre The sum of the ranks of each symbol == 2^largestBits, * where largestBits == huffNode[lastNonNull].nbBits. * @post The sum of the ranks of each symbol == 2^largestBits, - * where largestBits is the return value <= maxNbBits. + * where largestBits is the return value (expected <= targetNbBits). * - * @param huffNode The Huffman tree modified in place to enforce maxNbBits. + * @param huffNode The Huffman tree modified in place to enforce targetNbBits. + * It's presumed sorted, from most frequent to rarest symbol. * @param lastNonNull The symbol with the lowest count in the Huffman tree. - * @param maxNbBits The maximum allowed number of bits, which the Huffman tree + * @param targetNbBits The allowed number of bits, which the Huffman tree * may not respect. After this function the Huffman tree will - * respect maxNbBits. - * @return The maximum number of bits of the Huffman tree after adjustment, - * necessarily no more than maxNbBits. + * respect targetNbBits. + * @return The maximum number of bits of the Huffman tree after adjustment. */ -static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits) +static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 targetNbBits) { const U32 largestBits = huffNode[lastNonNull].nbBits; - /* early exit : no elt > maxNbBits, so the tree is already valid. */ - if (largestBits <= maxNbBits) return largestBits; + /* early exit : no elt > targetNbBits, so the tree is already valid. */ + if (largestBits <= targetNbBits) return largestBits; + + DEBUGLOG(5, "HUF_setMaxHeight (targetNbBits = %u)", targetNbBits); /* there are several too large elements (at least >= 2) */ { int totalCost = 0; - const U32 baseCost = 1 << (largestBits - maxNbBits); + const U32 baseCost = 1 << (largestBits - targetNbBits); int n = (int)lastNonNull; - /* Adjust any ranks > maxNbBits to maxNbBits. + /* Adjust any ranks > targetNbBits to targetNbBits. * Compute totalCost, which is how far the sum of the ranks is * we are over 2^largestBits after adjust the offending ranks. */ - while (huffNode[n].nbBits > maxNbBits) { + while (huffNode[n].nbBits > targetNbBits) { totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits)); - huffNode[n].nbBits = (BYTE)maxNbBits; + huffNode[n].nbBits = (BYTE)targetNbBits; n--; } - /* n stops at huffNode[n].nbBits <= maxNbBits */ - assert(huffNode[n].nbBits <= maxNbBits); - /* n end at index of smallest symbol using < maxNbBits */ - while (huffNode[n].nbBits == maxNbBits) --n; + /* n stops at huffNode[n].nbBits <= targetNbBits */ + assert(huffNode[n].nbBits <= targetNbBits); + /* n end at index of smallest symbol using < targetNbBits */ + while (huffNode[n].nbBits == targetNbBits) --n; - /* renorm totalCost from 2^largestBits to 2^maxNbBits + /* renorm totalCost from 2^largestBits to 2^targetNbBits * note : totalCost is necessarily a multiple of baseCost */ - assert((totalCost & (baseCost - 1)) == 0); - totalCost >>= (largestBits - maxNbBits); + assert(((U32)totalCost & (baseCost - 1)) == 0); + totalCost >>= (largestBits - targetNbBits); assert(totalCost > 0); /* repay normalized cost */ @@ -281,19 +387,19 @@ /* Get pos of last (smallest = lowest cum. count) symbol per rank */ ZSTD_memset(rankLast, 0xF0, sizeof(rankLast)); - { U32 currentNbBits = maxNbBits; + { U32 currentNbBits = targetNbBits; int pos; for (pos=n ; pos >= 0; pos--) { if (huffNode[pos].nbBits >= currentNbBits) continue; - currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */ - rankLast[maxNbBits-currentNbBits] = (U32)pos; + currentNbBits = huffNode[pos].nbBits; /* < targetNbBits */ + rankLast[targetNbBits-currentNbBits] = (U32)pos; } } while (totalCost > 0) { /* Try to reduce the next power of 2 above totalCost because we * gain back half the rank. */ - U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1; + U32 nBitsToDecrease = ZSTD_highbit32((U32)totalCost) + 1; for ( ; nBitsToDecrease > 1; nBitsToDecrease--) { U32 const highPos = rankLast[nBitsToDecrease]; U32 const lowPos = rankLast[nBitsToDecrease-1]; @@ -333,7 +439,7 @@ rankLast[nBitsToDecrease] = noSymbol; else { rankLast[nBitsToDecrease]--; - if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease) + if (huffNode[rankLast[nBitsToDecrease]].nbBits != targetNbBits-nBitsToDecrease) rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */ } } /* while (totalCost > 0) */ @@ -345,11 +451,11 @@ * TODO. */ while (totalCost < 0) { /* Sometimes, cost correction overshoot */ - /* special case : no rank 1 symbol (using maxNbBits-1); - * let's create one from largest rank 0 (using maxNbBits). + /* special case : no rank 1 symbol (using targetNbBits-1); + * let's create one from largest rank 0 (using targetNbBits). */ if (rankLast[1] == noSymbol) { - while (huffNode[n].nbBits == maxNbBits) n--; + while (huffNode[n].nbBits == targetNbBits) n--; huffNode[n+1].nbBits--; assert(n >= 0); rankLast[1] = (U32)(n+1); @@ -363,26 +469,122 @@ } /* repay normalized cost */ } /* there are several too large elements (at least >= 2) */ - return maxNbBits; + return targetNbBits; } typedef struct { - U32 base; - U32 curr; + U16 base; + U16 curr; } rankPos; -typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32]; +typedef nodeElt huffNodeTable[2 * (HUF_SYMBOLVALUE_MAX + 1)]; -#define RANK_POSITION_TABLE_SIZE 32 +/* Number of buckets available for HUF_sort() */ +#define RANK_POSITION_TABLE_SIZE 192 typedef struct { huffNodeTable huffNodeTbl; rankPos rankPosition[RANK_POSITION_TABLE_SIZE]; } HUF_buildCTable_wksp_tables; +/* RANK_POSITION_DISTINCT_COUNT_CUTOFF == Cutoff point in HUF_sort() buckets for which we use log2 bucketing. + * Strategy is to use as many buckets as possible for representing distinct + * counts while using the remainder to represent all "large" counts. + * + * To satisfy this requirement for 192 buckets, we can do the following: + * Let buckets 0-166 represent distinct counts of [0, 166] + * Let buckets 166 to 192 represent all remaining counts up to RANK_POSITION_MAX_COUNT_LOG using log2 bucketing. + */ +#define RANK_POSITION_MAX_COUNT_LOG 32 +#define RANK_POSITION_LOG_BUCKETS_BEGIN ((RANK_POSITION_TABLE_SIZE - 1) - RANK_POSITION_MAX_COUNT_LOG - 1 /* == 158 */) +#define RANK_POSITION_DISTINCT_COUNT_CUTOFF (RANK_POSITION_LOG_BUCKETS_BEGIN + ZSTD_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */) + +/* Return the appropriate bucket index for a given count. See definition of + * RANK_POSITION_DISTINCT_COUNT_CUTOFF for explanation of bucketing strategy. + */ +static U32 HUF_getIndex(U32 const count) { + return (count < RANK_POSITION_DISTINCT_COUNT_CUTOFF) + ? count + : ZSTD_highbit32(count) + RANK_POSITION_LOG_BUCKETS_BEGIN; +} + +/* Helper swap function for HUF_quickSortPartition() */ +static void HUF_swapNodes(nodeElt* a, nodeElt* b) { + nodeElt tmp = *a; + *a = *b; + *b = tmp; +} + +/* Returns 0 if the huffNode array is not sorted by descending count */ +MEM_STATIC int HUF_isSorted(nodeElt huffNode[], U32 const maxSymbolValue1) { + U32 i; + for (i = 1; i < maxSymbolValue1; ++i) { + if (huffNode[i].count > huffNode[i-1].count) { + return 0; + } + } + return 1; +} + +/* Insertion sort by descending order */ +HINT_INLINE void HUF_insertionSort(nodeElt huffNode[], int const low, int const high) { + int i; + int const size = high-low+1; + huffNode += low; + for (i = 1; i < size; ++i) { + nodeElt const key = huffNode[i]; + int j = i - 1; + while (j >= 0 && huffNode[j].count < key.count) { + huffNode[j + 1] = huffNode[j]; + j--; + } + huffNode[j + 1] = key; + } +} + +/* Pivot helper function for quicksort. */ +static int HUF_quickSortPartition(nodeElt arr[], int const low, int const high) { + /* Simply select rightmost element as pivot. "Better" selectors like + * median-of-three don't experimentally appear to have any benefit. + */ + U32 const pivot = arr[high].count; + int i = low - 1; + int j = low; + for ( ; j < high; j++) { + if (arr[j].count > pivot) { + i++; + HUF_swapNodes(&arr[i], &arr[j]); + } + } + HUF_swapNodes(&arr[i + 1], &arr[high]); + return i + 1; +} + +/* Classic quicksort by descending with partially iterative calls + * to reduce worst case callstack size. + */ +static void HUF_simpleQuickSort(nodeElt arr[], int low, int high) { + int const kInsertionSortThreshold = 8; + if (high - low < kInsertionSortThreshold) { + HUF_insertionSort(arr, low, high); + return; + } + while (low < high) { + int const idx = HUF_quickSortPartition(arr, low, high); + if (idx - low < high - idx) { + HUF_simpleQuickSort(arr, low, idx - 1); + low = idx + 1; + } else { + HUF_simpleQuickSort(arr, idx + 1, high); + high = idx - 1; + } + } +} + /** * HUF_sort(): * Sorts the symbols [0, maxSymbolValue] by count[symbol] in decreasing order. + * This is a typical bucket sorting strategy that uses either quicksort or insertion sort to sort each bucket. * * @param[out] huffNode Sorted symbols by decreasing count. Only members `.count` and `.byte` are filled. * Must have (maxSymbolValue + 1) entries. @@ -390,42 +592,51 @@ * @param[in] maxSymbolValue Maximum symbol value. * @param rankPosition This is a scratch workspace. Must have RANK_POSITION_TABLE_SIZE entries. */ -static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue, rankPos* rankPosition) -{ - int n; - int const maxSymbolValue1 = (int)maxSymbolValue + 1; +static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSymbolValue, rankPos rankPosition[]) { + U32 n; + U32 const maxSymbolValue1 = maxSymbolValue+1; /* Compute base and set curr to base. - * For symbol s let lowerRank = BIT_highbit32(count[n]+1) and rank = lowerRank + 1. - * Then 2^lowerRank <= count[n]+1 <= 2^rank. + * For symbol s let lowerRank = HUF_getIndex(count[n]) and rank = lowerRank + 1. + * See HUF_getIndex to see bucketing strategy. * We attribute each symbol to lowerRank's base value, because we want to know where * each rank begins in the output, so for rank R we want to count ranks R+1 and above. */ ZSTD_memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE); for (n = 0; n < maxSymbolValue1; ++n) { - U32 lowerRank = BIT_highbit32(count[n] + 1); + U32 lowerRank = HUF_getIndex(count[n]); + assert(lowerRank < RANK_POSITION_TABLE_SIZE - 1); rankPosition[lowerRank].base++; } + assert(rankPosition[RANK_POSITION_TABLE_SIZE - 1].base == 0); + /* Set up the rankPosition table */ for (n = RANK_POSITION_TABLE_SIZE - 1; n > 0; --n) { rankPosition[n-1].base += rankPosition[n].base; rankPosition[n-1].curr = rankPosition[n-1].base; } - /* Sort */ + + /* Insert each symbol into their appropriate bucket, setting up rankPosition table. */ for (n = 0; n < maxSymbolValue1; ++n) { U32 const c = count[n]; - U32 const r = BIT_highbit32(c+1) + 1; - U32 pos = rankPosition[r].curr++; - /* Insert into the correct position in the rank. - * We have at most 256 symbols, so this insertion should be fine. - */ - while ((pos > rankPosition[r].base) && (c > huffNode[pos-1].count)) { - huffNode[pos] = huffNode[pos-1]; - pos--; - } + U32 const r = HUF_getIndex(c) + 1; + U32 const pos = rankPosition[r].curr++; + assert(pos < maxSymbolValue1); huffNode[pos].count = c; huffNode[pos].byte = (BYTE)n; } + + /* Sort each bucket. */ + for (n = RANK_POSITION_DISTINCT_COUNT_CUTOFF; n < RANK_POSITION_TABLE_SIZE - 1; ++n) { + int const bucketSize = rankPosition[n].curr - rankPosition[n].base; + U32 const bucketStartIdx = rankPosition[n].base; + if (bucketSize > 1) { + assert(bucketStartIdx < maxSymbolValue1); + HUF_simpleQuickSort(huffNode + bucketStartIdx, 0, bucketSize-1); + } + } + + assert(HUF_isSorted(huffNode, maxSymbolValue1)); } @@ -449,6 +660,7 @@ int lowS, lowN; int nodeNb = STARTNODE; int n, nodeRoot; + DEBUGLOG(5, "HUF_buildTree (alphabet size = %u)", maxSymbolValue + 1); /* init for parents */ nonNullRank = (int)maxSymbolValue; while(huffNode[nonNullRank].count == 0) nonNullRank--; @@ -475,6 +687,8 @@ for (n=0; n<=nonNullRank; n++) huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1; + DEBUGLOG(6, "Initial distribution of bits completed (%zu sorted symbols)", showHNodeBits(huffNode, maxSymbolValue+1)); + return nonNullRank; } @@ -490,6 +704,7 @@ */ static void HUF_buildCTableFromTree(HUF_CElt* CTable, nodeElt const* huffNode, int nonNullRank, U32 maxSymbolValue, U32 maxNbBits) { + HUF_CElt* const ct = CTable + 1; /* fill result into ctable (val, nbBits) */ int n; U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0}; @@ -505,127 +720,373 @@ min >>= 1; } } for (n=0; n<alphabetSize; n++) - CTable[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */ + HUF_setNbBits(ct + huffNode[n].byte, huffNode[n].nbBits); /* push nbBits per symbol, symbol order */ for (n=0; n<alphabetSize; n++) - CTable[n].val = valPerRank[CTable[n].nbBits]++; /* assign value within rank, symbol order */ + HUF_setValue(ct + n, valPerRank[HUF_getNbBits(ct[n])]++); /* assign value within rank, symbol order */ + CTable[0] = maxNbBits; } -size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize) +size_t +HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, + void* workSpace, size_t wkspSize) { - HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace; + HUF_buildCTable_wksp_tables* const wksp_tables = + (HUF_buildCTable_wksp_tables*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(U32)); nodeElt* const huffNode0 = wksp_tables->huffNodeTbl; nodeElt* const huffNode = huffNode0+1; int nonNullRank; + HUF_STATIC_ASSERT(HUF_CTABLE_WORKSPACE_SIZE == sizeof(HUF_buildCTable_wksp_tables)); + + DEBUGLOG(5, "HUF_buildCTable_wksp (alphabet size = %u)", maxSymbolValue+1); + /* safety checks */ - if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */ if (wkspSize < sizeof(HUF_buildCTable_wksp_tables)) - return ERROR(workSpace_tooSmall); + return ERROR(workSpace_tooSmall); if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT; if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) - return ERROR(maxSymbolValue_tooLarge); + return ERROR(maxSymbolValue_tooLarge); ZSTD_memset(huffNode0, 0, sizeof(huffNodeTable)); /* sort, decreasing order */ HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition); + DEBUGLOG(6, "sorted symbols completed (%zu symbols)", showHNodeSymbols(huffNode, maxSymbolValue+1)); /* build tree */ nonNullRank = HUF_buildTree(huffNode, maxSymbolValue); - /* enforce maxTableLog */ + /* determine and enforce maxTableLog */ maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits); if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */ - HUF_buildCTableFromTree(tree, huffNode, nonNullRank, maxSymbolValue, maxNbBits); + HUF_buildCTableFromTree(CTable, huffNode, nonNullRank, maxSymbolValue, maxNbBits); return maxNbBits; } size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) { + HUF_CElt const* ct = CTable + 1; size_t nbBits = 0; int s; for (s = 0; s <= (int)maxSymbolValue; ++s) { - nbBits += CTable[s].nbBits * count[s]; + nbBits += HUF_getNbBits(ct[s]) * count[s]; } return nbBits >> 3; } int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) { + HUF_CElt const* ct = CTable + 1; int bad = 0; int s; for (s = 0; s <= (int)maxSymbolValue; ++s) { - bad |= (count[s] != 0) & (CTable[s].nbBits == 0); + bad |= (count[s] != 0) & (HUF_getNbBits(ct[s]) == 0); } return !bad; } size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); } -FORCE_INLINE_TEMPLATE void -HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable) +/** HUF_CStream_t: + * Huffman uses its own BIT_CStream_t implementation. + * There are three major differences from BIT_CStream_t: + * 1. HUF_addBits() takes a HUF_CElt (size_t) which is + * the pair (nbBits, value) in the format: + * format: + * - Bits [0, 4) = nbBits + * - Bits [4, 64 - nbBits) = 0 + * - Bits [64 - nbBits, 64) = value + * 2. The bitContainer is built from the upper bits and + * right shifted. E.g. to add a new value of N bits + * you right shift the bitContainer by N, then or in + * the new value into the N upper bits. + * 3. The bitstream has two bit containers. You can add + * bits to the second container and merge them into + * the first container. + */ + +#define HUF_BITS_IN_CONTAINER (sizeof(size_t) * 8) + +typedef struct { + size_t bitContainer[2]; + size_t bitPos[2]; + + BYTE* startPtr; + BYTE* ptr; + BYTE* endPtr; +} HUF_CStream_t; + +/**! HUF_initCStream(): + * Initializes the bitstream. + * @returns 0 or an error code. + */ +static size_t HUF_initCStream(HUF_CStream_t* bitC, + void* startPtr, size_t dstCapacity) { - BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits); + ZSTD_memset(bitC, 0, sizeof(*bitC)); + bitC->startPtr = (BYTE*)startPtr; + bitC->ptr = bitC->startPtr; + bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer[0]); + if (dstCapacity <= sizeof(bitC->bitContainer[0])) return ERROR(dstSize_tooSmall); + return 0; } -#define HUF_FLUSHBITS(s) BIT_flushBits(s) +/*! HUF_addBits(): + * Adds the symbol stored in HUF_CElt elt to the bitstream. + * + * @param elt The element we're adding. This is a (nbBits, value) pair. + * See the HUF_CStream_t docs for the format. + * @param idx Insert into the bitstream at this idx. + * @param kFast This is a template parameter. If the bitstream is guaranteed + * to have at least 4 unused bits after this call it may be 1, + * otherwise it must be 0. HUF_addBits() is faster when fast is set. + */ +FORCE_INLINE_TEMPLATE void HUF_addBits(HUF_CStream_t* bitC, HUF_CElt elt, int idx, int kFast) +{ + assert(idx <= 1); + assert(HUF_getNbBits(elt) <= HUF_TABLELOG_ABSOLUTEMAX); + /* This is efficient on x86-64 with BMI2 because shrx + * only reads the low 6 bits of the register. The compiler + * knows this and elides the mask. When fast is set, + * every operation can use the same value loaded from elt. + */ + bitC->bitContainer[idx] >>= HUF_getNbBits(elt); + bitC->bitContainer[idx] |= kFast ? HUF_getValueFast(elt) : HUF_getValue(elt); + /* We only read the low 8 bits of bitC->bitPos[idx] so it + * doesn't matter that the high bits have noise from the value. + */ + bitC->bitPos[idx] += HUF_getNbBitsFast(elt); + assert((bitC->bitPos[idx] & 0xFF) <= HUF_BITS_IN_CONTAINER); + /* The last 4-bits of elt are dirty if fast is set, + * so we must not be overwriting bits that have already been + * inserted into the bit container. + */ +#if DEBUGLEVEL >= 1 + { + size_t const nbBits = HUF_getNbBits(elt); + size_t const dirtyBits = nbBits == 0 ? 0 : ZSTD_highbit32((U32)nbBits) + 1; + (void)dirtyBits; + /* Middle bits are 0. */ + assert(((elt >> dirtyBits) << (dirtyBits + nbBits)) == 0); + /* We didn't overwrite any bits in the bit container. */ + assert(!kFast || (bitC->bitPos[idx] & 0xFF) <= HUF_BITS_IN_CONTAINER); + (void)dirtyBits; + } +#endif +} -#define HUF_FLUSHBITS_1(stream) \ - if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream) +FORCE_INLINE_TEMPLATE void HUF_zeroIndex1(HUF_CStream_t* bitC) +{ + bitC->bitContainer[1] = 0; + bitC->bitPos[1] = 0; +} -#define HUF_FLUSHBITS_2(stream) \ - if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream) +/*! HUF_mergeIndex1() : + * Merges the bit container @ index 1 into the bit container @ index 0 + * and zeros the bit container @ index 1. + */ +FORCE_INLINE_TEMPLATE void HUF_mergeIndex1(HUF_CStream_t* bitC) +{ + assert((bitC->bitPos[1] & 0xFF) < HUF_BITS_IN_CONTAINER); + bitC->bitContainer[0] >>= (bitC->bitPos[1] & 0xFF); + bitC->bitContainer[0] |= bitC->bitContainer[1]; + bitC->bitPos[0] += bitC->bitPos[1]; + assert((bitC->bitPos[0] & 0xFF) <= HUF_BITS_IN_CONTAINER); +} + +/*! HUF_flushBits() : +* Flushes the bits in the bit container @ index 0. +* +* @post bitPos will be < 8. +* @param kFast If kFast is set then we must know a-priori that +* the bit container will not overflow. +*/ +FORCE_INLINE_TEMPLATE void HUF_flushBits(HUF_CStream_t* bitC, int kFast) +{ + /* The upper bits of bitPos are noisy, so we must mask by 0xFF. */ + size_t const nbBits = bitC->bitPos[0] & 0xFF; + size_t const nbBytes = nbBits >> 3; + /* The top nbBits bits of bitContainer are the ones we need. */ + size_t const bitContainer = bitC->bitContainer[0] >> (HUF_BITS_IN_CONTAINER - nbBits); + /* Mask bitPos to account for the bytes we consumed. */ + bitC->bitPos[0] &= 7; + assert(nbBits > 0); + assert(nbBits <= sizeof(bitC->bitContainer[0]) * 8); + assert(bitC->ptr <= bitC->endPtr); + MEM_writeLEST(bitC->ptr, bitContainer); + bitC->ptr += nbBytes; + assert(!kFast || bitC->ptr <= bitC->endPtr); + if (!kFast && bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr; + /* bitContainer doesn't need to be modified because the leftover + * bits are already the top bitPos bits. And we don't care about + * noise in the lower values. + */ +} + +/*! HUF_endMark() + * @returns The Huffman stream end mark: A 1-bit value = 1. + */ +static HUF_CElt HUF_endMark(void) +{ + HUF_CElt endMark; + HUF_setNbBits(&endMark, 1); + HUF_setValue(&endMark, 1); + return endMark; +} + +/*! HUF_closeCStream() : + * @return Size of CStream, in bytes, + * or 0 if it could not fit into dstBuffer */ +static size_t HUF_closeCStream(HUF_CStream_t* bitC) +{ + HUF_addBits(bitC, HUF_endMark(), /* idx */ 0, /* kFast */ 0); + HUF_flushBits(bitC, /* kFast */ 0); + { + size_t const nbBits = bitC->bitPos[0] & 0xFF; + if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */ + return (size_t)(bitC->ptr - bitC->startPtr) + (nbBits > 0); + } +} + +FORCE_INLINE_TEMPLATE void +HUF_encodeSymbol(HUF_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable, int idx, int fast) +{ + HUF_addBits(bitCPtr, CTable[symbol], idx, fast); +} + +FORCE_INLINE_TEMPLATE void +HUF_compress1X_usingCTable_internal_body_loop(HUF_CStream_t* bitC, + const BYTE* ip, size_t srcSize, + const HUF_CElt* ct, + int kUnroll, int kFastFlush, int kLastFast) +{ + /* Join to kUnroll */ + int n = (int)srcSize; + int rem = n % kUnroll; + if (rem > 0) { + for (; rem > 0; --rem) { + HUF_encodeSymbol(bitC, ip[--n], ct, 0, /* fast */ 0); + } + HUF_flushBits(bitC, kFastFlush); + } + assert(n % kUnroll == 0); + + /* Join to 2 * kUnroll */ + if (n % (2 * kUnroll)) { + int u; + for (u = 1; u < kUnroll; ++u) { + HUF_encodeSymbol(bitC, ip[n - u], ct, 0, 1); + } + HUF_encodeSymbol(bitC, ip[n - kUnroll], ct, 0, kLastFast); + HUF_flushBits(bitC, kFastFlush); + n -= kUnroll; + } + assert(n % (2 * kUnroll) == 0); + + for (; n>0; n-= 2 * kUnroll) { + /* Encode kUnroll symbols into the bitstream @ index 0. */ + int u; + for (u = 1; u < kUnroll; ++u) { + HUF_encodeSymbol(bitC, ip[n - u], ct, /* idx */ 0, /* fast */ 1); + } + HUF_encodeSymbol(bitC, ip[n - kUnroll], ct, /* idx */ 0, /* fast */ kLastFast); + HUF_flushBits(bitC, kFastFlush); + /* Encode kUnroll symbols into the bitstream @ index 1. + * This allows us to start filling the bit container + * without any data dependencies. + */ + HUF_zeroIndex1(bitC); + for (u = 1; u < kUnroll; ++u) { + HUF_encodeSymbol(bitC, ip[n - kUnroll - u], ct, /* idx */ 1, /* fast */ 1); + } + HUF_encodeSymbol(bitC, ip[n - kUnroll - kUnroll], ct, /* idx */ 1, /* fast */ kLastFast); + /* Merge bitstream @ index 1 into the bitstream @ index 0 */ + HUF_mergeIndex1(bitC); + HUF_flushBits(bitC, kFastFlush); + } + assert(n == 0); + +} + +/** + * Returns a tight upper bound on the output space needed by Huffman + * with 8 bytes buffer to handle over-writes. If the output is at least + * this large we don't need to do bounds checks during Huffman encoding. + */ +static size_t HUF_tightCompressBound(size_t srcSize, size_t tableLog) +{ + return ((srcSize * tableLog) >> 3) + 8; +} + FORCE_INLINE_TEMPLATE size_t HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) { + U32 const tableLog = (U32)CTable[0]; + HUF_CElt const* ct = CTable + 1; const BYTE* ip = (const BYTE*) src; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; - size_t n; - BIT_CStream_t bitC; + HUF_CStream_t bitC; /* init */ if (dstSize < 8) return 0; /* not enough space to compress */ - { size_t const initErr = BIT_initCStream(&bitC, op, (size_t)(oend-op)); + { size_t const initErr = HUF_initCStream(&bitC, op, (size_t)(oend-op)); if (HUF_isError(initErr)) return 0; } - n = srcSize & ~3; /* join to mod 4 */ - switch (srcSize & 3) - { - case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable); - HUF_FLUSHBITS_2(&bitC); - /* fall-through */ - case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable); - HUF_FLUSHBITS_1(&bitC); - /* fall-through */ - case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable); - HUF_FLUSHBITS(&bitC); - /* fall-through */ - case 0 : /* fall-through */ - default: break; + if (dstSize < HUF_tightCompressBound(srcSize, (size_t)tableLog) || tableLog > 11) + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ MEM_32bits() ? 2 : 4, /* kFast */ 0, /* kLastFast */ 0); + else { + if (MEM_32bits()) { + switch (tableLog) { + case 11: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 2, /* kFastFlush */ 1, /* kLastFast */ 0); + break; + case 10: ZSTD_FALLTHROUGH; + case 9: ZSTD_FALLTHROUGH; + case 8: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 2, /* kFastFlush */ 1, /* kLastFast */ 1); + break; + case 7: ZSTD_FALLTHROUGH; + default: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 3, /* kFastFlush */ 1, /* kLastFast */ 1); + break; + } + } else { + switch (tableLog) { + case 11: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 5, /* kFastFlush */ 1, /* kLastFast */ 0); + break; + case 10: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 5, /* kFastFlush */ 1, /* kLastFast */ 1); + break; + case 9: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 6, /* kFastFlush */ 1, /* kLastFast */ 0); + break; + case 8: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 7, /* kFastFlush */ 1, /* kLastFast */ 0); + break; + case 7: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 8, /* kFastFlush */ 1, /* kLastFast */ 0); + break; + case 6: ZSTD_FALLTHROUGH; + default: + HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 9, /* kFastFlush */ 1, /* kLastFast */ 1); + break; + } + } } + assert(bitC.ptr <= bitC.endPtr); - for (; n>0; n-=4) { /* note : n&3==0 at this stage */ - HUF_encodeSymbol(&bitC, ip[n- 1], CTable); - HUF_FLUSHBITS_1(&bitC); - HUF_encodeSymbol(&bitC, ip[n- 2], CTable); - HUF_FLUSHBITS_2(&bitC); - HUF_encodeSymbol(&bitC, ip[n- 3], CTable); - HUF_FLUSHBITS_1(&bitC); - HUF_encodeSymbol(&bitC, ip[n- 4], CTable); - HUF_FLUSHBITS(&bitC); - } - - return BIT_closeCStream(&bitC); + return HUF_closeCStream(&bitC); } #if DYNAMIC_BMI2 -static TARGET_ATTRIBUTE("bmi2") size_t +static BMI2_TARGET_ATTRIBUTE size_t HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) @@ -644,9 +1105,9 @@ static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, - const HUF_CElt* CTable, const int bmi2) + const HUF_CElt* CTable, const int flags) { - if (bmi2) { + if (flags & HUF_flags_bmi2) { return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable); } return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable); @@ -657,24 +1118,23 @@ static size_t HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, - const HUF_CElt* CTable, const int bmi2) + const HUF_CElt* CTable, const int flags) { - (void)bmi2; + (void)flags; return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable); } #endif -size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) +size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags) { - return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); + return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags); } - static size_t HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize, const void* src, size_t srcSize, - const HUF_CElt* CTable, int bmi2) + const HUF_CElt* CTable, int flags) { size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */ const BYTE* ip = (const BYTE*) src; @@ -688,27 +1148,24 @@ op += 6; /* jumpTable */ assert(op <= oend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); - if (cSize==0) return 0; - assert(cSize <= 65535); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) ); + if (cSize == 0 || cSize > 65535) return 0; MEM_writeLE16(ostart, (U16)cSize); op += cSize; } ip += segmentSize; assert(op <= oend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); - if (cSize==0) return 0; - assert(cSize <= 65535); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) ); + if (cSize == 0 || cSize > 65535) return 0; MEM_writeLE16(ostart+2, (U16)cSize); op += cSize; } ip += segmentSize; assert(op <= oend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) ); - if (cSize==0) return 0; - assert(cSize <= 65535); + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) ); + if (cSize == 0 || cSize > 65535) return 0; MEM_writeLE16(ostart+4, (U16)cSize); op += cSize; } @@ -716,17 +1173,17 @@ ip += segmentSize; assert(op <= oend); assert(ip <= iend); - { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, bmi2) ); - if (cSize==0) return 0; + { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, flags) ); + if (cSize == 0 || cSize > 65535) return 0; op += cSize; } return (size_t)(op-ostart); } -size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable) +size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags) { - return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0); + return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags); } typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e; @@ -734,11 +1191,11 @@ static size_t HUF_compressCTable_internal( BYTE* const ostart, BYTE* op, BYTE* const oend, const void* src, size_t srcSize, - HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2) + HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int flags) { size_t const cSize = (nbStreams==HUF_singleStream) ? - HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2) : - HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2); + HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, flags) : + HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, flags); if (HUF_isError(cSize)) { return cSize; } if (cSize==0) { return 0; } /* uncompressible */ op += cSize; @@ -750,35 +1207,111 @@ typedef struct { unsigned count[HUF_SYMBOLVALUE_MAX + 1]; - HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1]; + HUF_CElt CTable[HUF_CTABLE_SIZE_ST(HUF_SYMBOLVALUE_MAX)]; union { HUF_buildCTable_wksp_tables buildCTable_wksp; HUF_WriteCTableWksp writeCTable_wksp; + U32 hist_wksp[HIST_WKSP_SIZE_U32]; } wksps; } HUF_compress_tables_t; +#define SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE 4096 +#define SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO 10 /* Must be >= 2 */ + +unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue) +{ + unsigned cardinality = 0; + unsigned i; + + for (i = 0; i < maxSymbolValue + 1; i++) { + if (count[i] != 0) cardinality += 1; + } + + return cardinality; +} + +unsigned HUF_minTableLog(unsigned symbolCardinality) +{ + U32 minBitsSymbols = ZSTD_highbit32(symbolCardinality) + 1; + return minBitsSymbols; +} + +unsigned HUF_optimalTableLog( + unsigned maxTableLog, + size_t srcSize, + unsigned maxSymbolValue, + void* workSpace, size_t wkspSize, + HUF_CElt* table, + const unsigned* count, + int flags) +{ + assert(srcSize > 1); /* Not supported, RLE should be used instead */ + assert(wkspSize >= sizeof(HUF_buildCTable_wksp_tables)); + + if (!(flags & HUF_flags_optimalDepth)) { + /* cheap evaluation, based on FSE */ + return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + } + + { BYTE* dst = (BYTE*)workSpace + sizeof(HUF_WriteCTableWksp); + size_t dstSize = wkspSize - sizeof(HUF_WriteCTableWksp); + size_t maxBits, hSize, newSize; + const unsigned symbolCardinality = HUF_cardinality(count, maxSymbolValue); + const unsigned minTableLog = HUF_minTableLog(symbolCardinality); + size_t optSize = ((size_t) ~0) - 1; + unsigned optLog = maxTableLog, optLogGuess; + + DEBUGLOG(6, "HUF_optimalTableLog: probing huf depth (srcSize=%zu)", srcSize); + + /* Search until size increases */ + for (optLogGuess = minTableLog; optLogGuess <= maxTableLog; optLogGuess++) { + DEBUGLOG(7, "checking for huffLog=%u", optLogGuess); + maxBits = HUF_buildCTable_wksp(table, count, maxSymbolValue, optLogGuess, workSpace, wkspSize); + if (ERR_isError(maxBits)) continue; + + if (maxBits < optLogGuess && optLogGuess > minTableLog) break; + + hSize = HUF_writeCTable_wksp(dst, dstSize, table, maxSymbolValue, (U32)maxBits, workSpace, wkspSize); + + if (ERR_isError(hSize)) continue; + + newSize = HUF_estimateCompressedSize(table, count, maxSymbolValue) + hSize; + + if (newSize > optSize + 1) { + break; + } + + if (newSize < optSize) { + optSize = newSize; + optLog = optLogGuess; + } + } + assert(optLog <= HUF_TABLELOG_MAX); + return optLog; + } +} + /* HUF_compress_internal() : * `workSpace_align4` must be aligned on 4-bytes boundaries, - * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U32 unsigned */ + * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U64 unsigned */ static size_t HUF_compress_internal (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, HUF_nbStreams_e nbStreams, - void* workSpace_align4, size_t wkspSize, - HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat, - const int bmi2) + void* workSpace, size_t wkspSize, + HUF_CElt* oldHufTable, HUF_repeat* repeat, int flags) { - HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace_align4; + HUF_compress_tables_t* const table = (HUF_compress_tables_t*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(size_t)); BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; - HUF_STATIC_ASSERT(sizeof(*table) <= HUF_WORKSPACE_SIZE); - assert(((size_t)workSpace_align4 & 3) == 0); /* must be aligned on 4-bytes boundaries */ + DEBUGLOG(5, "HUF_compress_internal (srcSize=%zu)", srcSize); + HUF_STATIC_ASSERT(sizeof(*table) + HUF_WORKSPACE_MAX_ALIGNMENT <= HUF_WORKSPACE_SIZE); /* checks & inits */ - if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall); + if (wkspSize < sizeof(*table)) return ERROR(workSpace_tooSmall); if (!srcSize) return 0; /* Uncompressed */ if (!dstSize) return 0; /* cannot fit anything within dst budget */ if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */ @@ -788,17 +1321,34 @@ if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; /* Heuristic : If old table is valid, use it for small inputs */ - if (preferRepeat && repeat && *repeat == HUF_repeat_valid) { + if ((flags & HUF_flags_preferRepeat) && repeat && *repeat == HUF_repeat_valid) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, oldHufTable, bmi2); + nbStreams, oldHufTable, flags); + } + + /* If uncompressible data is suspected, do a smaller sampling first */ + DEBUG_STATIC_ASSERT(SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO >= 2); + if ((flags & HUF_flags_suspectUncompressible) && srcSize >= (SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE * SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO)) { + size_t largestTotal = 0; + DEBUGLOG(5, "input suspected incompressible : sampling to check"); + { unsigned maxSymbolValueBegin = maxSymbolValue; + CHECK_V_F(largestBegin, HIST_count_simple (table->count, &maxSymbolValueBegin, (const BYTE*)src, SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) ); + largestTotal += largestBegin; + } + { unsigned maxSymbolValueEnd = maxSymbolValue; + CHECK_V_F(largestEnd, HIST_count_simple (table->count, &maxSymbolValueEnd, (const BYTE*)src + srcSize - SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE, SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) ); + largestTotal += largestEnd; + } + if (largestTotal <= ((2 * SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) >> 7)+4) return 0; /* heuristic : probably not compressible enough */ } /* Scan input and build symbol stats */ - { CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace_align4, wkspSize) ); + { CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->wksps.hist_wksp, sizeof(table->wksps.hist_wksp)) ); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */ } + DEBUGLOG(6, "histogram detail completed (%zu symbols)", showU32(table->count, maxSymbolValue+1)); /* Check validity of previous table */ if ( repeat @@ -807,22 +1357,26 @@ *repeat = HUF_repeat_none; } /* Heuristic : use existing table for small inputs */ - if (preferRepeat && repeat && *repeat != HUF_repeat_none) { + if ((flags & HUF_flags_preferRepeat) && repeat && *repeat != HUF_repeat_none) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, oldHufTable, bmi2); + nbStreams, oldHufTable, flags); } /* Build Huffman Tree */ - huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue, &table->wksps, sizeof(table->wksps), table->CTable, table->count, flags); { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count, maxSymbolValue, huffLog, &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp)); CHECK_F(maxBits); huffLog = (U32)maxBits; - /* Zero unused symbols in CTable, so we can check it for validity */ - ZSTD_memset(table->CTable + (maxSymbolValue + 1), 0, - sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt))); + DEBUGLOG(6, "bit distribution completed (%zu symbols)", showCTableBits(table->CTable + 1, maxSymbolValue+1)); + } + /* Zero unused symbols in CTable, so we can check it for validity */ + { + size_t const ctableSize = HUF_CTABLE_SIZE_ST(maxSymbolValue); + size_t const unusedSize = sizeof(table->CTable) - ctableSize * sizeof(HUF_CElt); + ZSTD_memset(table->CTable + ctableSize, 0, unusedSize); } /* Write table description header */ @@ -835,7 +1389,7 @@ if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, oldHufTable, bmi2); + nbStreams, oldHufTable, flags); } } /* Use the new huffman table */ @@ -847,91 +1401,35 @@ } return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, - nbStreams, table->CTable, bmi2); -} - - -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize) -{ - return HUF_compress_internal(dst, dstSize, src, srcSize, - maxSymbolValue, huffLog, HUF_singleStream, - workSpace, wkspSize, - NULL, NULL, 0, 0 /*bmi2*/); + nbStreams, table->CTable, flags); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) + HUF_CElt* hufTable, HUF_repeat* repeat, int flags) { + DEBUGLOG(5, "HUF_compress1X_repeat (srcSize = %zu)", srcSize); return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_singleStream, workSpace, wkspSize, hufTable, - repeat, preferRepeat, bmi2); + repeat, flags); } /* HUF_compress4X_repeat(): * compress input using 4 streams. - * provide workspace to generate compression tables */ -size_t HUF_compress4X_wksp (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog, - void* workSpace, size_t wkspSize) -{ - return HUF_compress_internal(dst, dstSize, src, srcSize, - maxSymbolValue, huffLog, HUF_fourStreams, - workSpace, wkspSize, - NULL, NULL, 0, 0 /*bmi2*/); -} - -/* HUF_compress4X_repeat(): - * compress input using 4 streams. + * consider skipping quickly * re-use an existing huffman compression table */ size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2) + HUF_CElt* hufTable, HUF_repeat* repeat, int flags) { + DEBUGLOG(5, "HUF_compress4X_repeat (srcSize = %zu)", srcSize); return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, HUF_fourStreams, workSpace, wkspSize, - hufTable, repeat, preferRepeat, bmi2); + hufTable, repeat, flags); } - -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -/** HUF_buildCTable() : - * @return : maxNbBits - * Note : count is used before tree is written, so they can safely overlap - */ -size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits) -{ - HUF_buildCTable_wksp_tables workspace; - return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace)); -} - -size_t HUF_compress1X (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog) -{ - unsigned workSpace[HUF_WORKSPACE_SIZE_U32]; - return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); -} - -size_t HUF_compress2 (void* dst, size_t dstSize, - const void* src, size_t srcSize, - unsigned maxSymbolValue, unsigned huffLog) -{ - unsigned workSpace[HUF_WORKSPACE_SIZE_U32]; - return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace)); -} - -size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize) -{ - return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT); -} -#endif
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress.c b/Utilities/cmzstd/lib/compress/zstd_compress.c index b7ee298..266186c 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress.c +++ b/Utilities/cmzstd/lib/compress/zstd_compress.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,13 +11,12 @@ /*-************************************* * Dependencies ***************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* INT_MAX, ZSTD_memset, ZSTD_memcpy */ -#include "../common/cpu.h" #include "../common/mem.h" #include "hist.h" /* HIST_countFast_wksp */ #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "zstd_compress_internal.h" #include "zstd_compress_sequences.h" @@ -28,6 +27,7 @@ #include "zstd_opt.h" #include "zstd_ldm.h" #include "zstd_compress_superblock.h" +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_rotateRight_U64 */ /* *************************************************************** * Tuning parameters @@ -42,19 +42,34 @@ # define ZSTD_COMPRESS_HEAPMODE 0 #endif +/*! + * ZSTD_HASHLOG3_MAX : + * Maximum size of the hash table dedicated to find 3-bytes matches, + * in log format, aka 17 => 1 << 17 == 128Ki positions. + * This structure is only used in zstd_opt. + * Since allocation is centralized for all strategies, it has to be known here. + * The actual (selected) size of the hash table is then stored in ZSTD_matchState_t.hashLog3, + * so that zstd_opt.c doesn't need to know about this constant. + */ +#ifndef ZSTD_HASHLOG3_MAX +# define ZSTD_HASHLOG3_MAX 17 +#endif /*-************************************* * Helper functions ***************************************/ /* ZSTD_compressBound() - * Note that the result from this function is only compatible with the "normal" - * full-block strategy. - * When there are a lot of small blocks due to frequent flush in streaming mode - * the overhead of headers can make the compressed data to be larger than the - * return value of ZSTD_compressBound(). + * Note that the result from this function is only valid for + * the one-pass compression functions. + * When employing the streaming mode, + * if flushes are frequently altering the size of blocks, + * the overhead from block headers can make the compressed data larger + * than the return value of ZSTD_compressBound(). */ size_t ZSTD_compressBound(size_t srcSize) { - return ZSTD_COMPRESSBOUND(srcSize); + size_t const r = ZSTD_COMPRESSBOUND(srcSize); + if (r==0) return ERROR(srcSize_wrong); + return r; } @@ -72,10 +87,10 @@ ZSTD_customMem customMem; U32 dictID; int compressionLevel; /* 0 indicates that advanced API was used to select CDict params */ - ZSTD_useRowMatchFinderMode_e useRowMatchFinder; /* Indicates whether the CDict was created with params that would use - * row-based matchfinder. Unless the cdict is reloaded, we will use - * the same greedy/lazy matchfinder at compression time. - */ + ZSTD_paramSwitch_e useRowMatchFinder; /* Indicates whether the CDict was created with params that would use + * row-based matchfinder. Unless the cdict is reloaded, we will use + * the same greedy/lazy matchfinder at compression time. + */ }; /* typedef'd to ZSTD_CDict within "zstd.h" */ ZSTD_CCtx* ZSTD_createCCtx(void) @@ -88,7 +103,7 @@ assert(cctx != NULL); ZSTD_memset(cctx, 0, sizeof(*cctx)); cctx->customMem = memManager; - cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); + cctx->bmi2 = ZSTD_cpuSupportsBmi2(); { size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters); assert(!ZSTD_isError(err)); (void)err; @@ -166,12 +181,9 @@ if (cctx==NULL) return 0; /* support free on NULL */ RETURN_ERROR_IF(cctx->staticSize, memory_allocation, "not compatible with static CCtx"); - { - int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx); + { int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx); ZSTD_freeCCtxContent(cctx); - if (!cctxInWorkspace) { - ZSTD_customFree(cctx, cctx->customMem); - } + if (!cctxInWorkspace) ZSTD_customFree(cctx, cctx->customMem); } return 0; } @@ -214,55 +226,84 @@ /* Returns true if the strategy and useRowMatchFinder mode indicate that we will use the row based matchfinder * for this compression. */ -static int ZSTD_rowMatchFinderUsed(const ZSTD_strategy strategy, const ZSTD_useRowMatchFinderMode_e mode) { - assert(mode != ZSTD_urm_auto); - return ZSTD_rowMatchFinderSupported(strategy) && (mode == ZSTD_urm_enableRowMatchFinder); +static int ZSTD_rowMatchFinderUsed(const ZSTD_strategy strategy, const ZSTD_paramSwitch_e mode) { + assert(mode != ZSTD_ps_auto); + return ZSTD_rowMatchFinderSupported(strategy) && (mode == ZSTD_ps_enable); } -/* Returns row matchfinder usage enum given an initial mode and cParams */ -static ZSTD_useRowMatchFinderMode_e ZSTD_resolveRowMatchFinderMode(ZSTD_useRowMatchFinderMode_e mode, - const ZSTD_compressionParameters* const cParams) { -#if !defined(ZSTD_NO_INTRINSICS) && (defined(__SSE2__) || defined(__ARM_NEON)) +/* Returns row matchfinder usage given an initial mode and cParams */ +static ZSTD_paramSwitch_e ZSTD_resolveRowMatchFinderMode(ZSTD_paramSwitch_e mode, + const ZSTD_compressionParameters* const cParams) { +#if defined(ZSTD_ARCH_X86_SSE2) || defined(ZSTD_ARCH_ARM_NEON) int const kHasSIMD128 = 1; #else int const kHasSIMD128 = 0; #endif - if (mode != ZSTD_urm_auto) return mode; /* if requested enabled, but no SIMD, we still will use row matchfinder */ - mode = ZSTD_urm_disableRowMatchFinder; + if (mode != ZSTD_ps_auto) return mode; /* if requested enabled, but no SIMD, we still will use row matchfinder */ + mode = ZSTD_ps_disable; if (!ZSTD_rowMatchFinderSupported(cParams->strategy)) return mode; if (kHasSIMD128) { - if (cParams->windowLog > 14) mode = ZSTD_urm_enableRowMatchFinder; + if (cParams->windowLog > 14) mode = ZSTD_ps_enable; } else { - if (cParams->windowLog > 17) mode = ZSTD_urm_enableRowMatchFinder; + if (cParams->windowLog > 17) mode = ZSTD_ps_enable; } return mode; } +/* Returns block splitter usage (generally speaking, when using slower/stronger compression modes) */ +static ZSTD_paramSwitch_e ZSTD_resolveBlockSplitterMode(ZSTD_paramSwitch_e mode, + const ZSTD_compressionParameters* const cParams) { + if (mode != ZSTD_ps_auto) return mode; + return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 17) ? ZSTD_ps_enable : ZSTD_ps_disable; +} + /* Returns 1 if the arguments indicate that we should allocate a chainTable, 0 otherwise */ static int ZSTD_allocateChainTable(const ZSTD_strategy strategy, - const ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + const ZSTD_paramSwitch_e useRowMatchFinder, const U32 forDDSDict) { - assert(useRowMatchFinder != ZSTD_urm_auto); + assert(useRowMatchFinder != ZSTD_ps_auto); /* We always should allocate a chaintable if we are allocating a matchstate for a DDS dictionary matchstate. * We do not allocate a chaintable if we are using ZSTD_fast, or are using the row-based matchfinder. */ return forDDSDict || ((strategy != ZSTD_fast) && !ZSTD_rowMatchFinderUsed(strategy, useRowMatchFinder)); } -/* Returns 1 if compression parameters are such that we should +/* Returns ZSTD_ps_enable if compression parameters are such that we should * enable long distance matching (wlog >= 27, strategy >= btopt). - * Returns 0 otherwise. + * Returns ZSTD_ps_disable otherwise. */ -static U32 ZSTD_CParams_shouldEnableLdm(const ZSTD_compressionParameters* const cParams) { - return cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27; +static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm(ZSTD_paramSwitch_e mode, + const ZSTD_compressionParameters* const cParams) { + if (mode != ZSTD_ps_auto) return mode; + return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27) ? ZSTD_ps_enable : ZSTD_ps_disable; } -/* Returns 1 if compression parameters are such that we should - * enable blockSplitter (wlog >= 17, strategy >= btopt). - * Returns 0 otherwise. - */ -static U32 ZSTD_CParams_useBlockSplitter(const ZSTD_compressionParameters* const cParams) { - return cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 17; +static int ZSTD_resolveExternalSequenceValidation(int mode) { + return mode; +} + +/* Resolves maxBlockSize to the default if no value is present. */ +static size_t ZSTD_resolveMaxBlockSize(size_t maxBlockSize) { + if (maxBlockSize == 0) { + return ZSTD_BLOCKSIZE_MAX; + } else { + return maxBlockSize; + } +} + +static ZSTD_paramSwitch_e ZSTD_resolveExternalRepcodeSearch(ZSTD_paramSwitch_e value, int cLevel) { + if (value != ZSTD_ps_auto) return value; + if (cLevel < 10) { + return ZSTD_ps_disable; + } else { + return ZSTD_ps_enable; + } +} + +/* Returns 1 if compression parameters are such that CDict hashtable and chaintable indices are tagged. + * If so, the tags need to be removed in ZSTD_resetCCtx_byCopyingCDict. */ +static int ZSTD_CDictIndicesAreTagged(const ZSTD_compressionParameters* const cParams) { + return cParams->strategy == ZSTD_fast || cParams->strategy == ZSTD_dfast; } static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( @@ -274,21 +315,18 @@ cctxParams.cParams = cParams; /* Adjust advanced params according to cParams */ - if (ZSTD_CParams_shouldEnableLdm(&cParams)) { - DEBUGLOG(4, "ZSTD_makeCCtxParamsFromCParams(): Including LDM into cctx params"); - cctxParams.ldmParams.enableLdm = 1; - /* LDM is enabled by default for optimal parser and window size >= 128MB */ + cctxParams.ldmParams.enableLdm = ZSTD_resolveEnableLdm(cctxParams.ldmParams.enableLdm, &cParams); + if (cctxParams.ldmParams.enableLdm == ZSTD_ps_enable) { ZSTD_ldm_adjustParameters(&cctxParams.ldmParams, &cParams); assert(cctxParams.ldmParams.hashLog >= cctxParams.ldmParams.bucketSizeLog); assert(cctxParams.ldmParams.hashRateLog < 32); } - - if (ZSTD_CParams_useBlockSplitter(&cParams)) { - DEBUGLOG(4, "ZSTD_makeCCtxParamsFromCParams(): Including block splitting into cctx params"); - cctxParams.splitBlocks = 1; - } - + cctxParams.useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams.useBlockSplitter, &cParams); cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams.useRowMatchFinder, &cParams); + cctxParams.validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams.validateSequences); + cctxParams.maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams.maxBlockSize); + cctxParams.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(cctxParams.searchForExternalRepcodes, + cctxParams.compressionLevel); assert(!ZSTD_checkCParams(cParams)); return cctxParams; } @@ -334,10 +372,13 @@ #define ZSTD_NO_CLEVEL 0 /** - * Initializes the cctxParams from params and compressionLevel. + * Initializes `cctxParams` from `params` and `compressionLevel`. * @param compressionLevel If params are derived from a compression level then that compression level, otherwise ZSTD_NO_CLEVEL. */ -static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_parameters const* params, int compressionLevel) +static void +ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, + const ZSTD_parameters* params, + int compressionLevel) { assert(!ZSTD_checkCParams(params->cParams)); ZSTD_memset(cctxParams, 0, sizeof(*cctxParams)); @@ -348,7 +389,13 @@ */ cctxParams->compressionLevel = compressionLevel; cctxParams->useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams->useRowMatchFinder, ¶ms->cParams); - DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d", cctxParams->useRowMatchFinder); + cctxParams->useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams->useBlockSplitter, ¶ms->cParams); + cctxParams->ldmParams.enableLdm = ZSTD_resolveEnableLdm(cctxParams->ldmParams.enableLdm, ¶ms->cParams); + cctxParams->validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams->validateSequences); + cctxParams->maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams->maxBlockSize); + cctxParams->searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(cctxParams->searchForExternalRepcodes, compressionLevel); + DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d, useBlockSplitter=%d ldm=%d", + cctxParams->useRowMatchFinder, cctxParams->useBlockSplitter, cctxParams->ldmParams.enableLdm); } size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params) @@ -361,7 +408,7 @@ /** * Sets cctxParams' cParams and fParams from params, but otherwise leaves them alone. - * @param param Validated zstd parameters. + * @param params Validated zstd parameters. */ static void ZSTD_CCtxParams_setZstdParams( ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params) @@ -470,8 +517,8 @@ return bounds; case ZSTD_c_enableLongDistanceMatching: - bounds.lowerBound = 0; - bounds.upperBound = 1; + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; return bounds; case ZSTD_c_ldmHashLog: @@ -518,9 +565,9 @@ return bounds; case ZSTD_c_literalCompressionMode: - ZSTD_STATIC_ASSERT(ZSTD_lcm_auto < ZSTD_lcm_huffman && ZSTD_lcm_huffman < ZSTD_lcm_uncompressed); - bounds.lowerBound = ZSTD_lcm_auto; - bounds.upperBound = ZSTD_lcm_uncompressed; + ZSTD_STATIC_ASSERT(ZSTD_ps_auto < ZSTD_ps_enable && ZSTD_ps_enable < ZSTD_ps_disable); + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; return bounds; case ZSTD_c_targetCBlockSize: @@ -549,14 +596,14 @@ bounds.upperBound = 1; return bounds; - case ZSTD_c_splitBlocks: - bounds.lowerBound = 0; - bounds.upperBound = 1; + case ZSTD_c_useBlockSplitter: + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; return bounds; case ZSTD_c_useRowMatchFinder: - bounds.lowerBound = (int)ZSTD_urm_auto; - bounds.upperBound = (int)ZSTD_urm_enableRowMatchFinder; + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; return bounds; case ZSTD_c_deterministicRefPrefix: @@ -564,6 +611,26 @@ bounds.upperBound = 1; return bounds; + case ZSTD_c_prefetchCDictTables: + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; + return bounds; + + case ZSTD_c_enableSeqProducerFallback: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + + case ZSTD_c_maxBlockSize: + bounds.lowerBound = ZSTD_BLOCKSIZE_MAX_MIN; + bounds.upperBound = ZSTD_BLOCKSIZE_MAX; + return bounds; + + case ZSTD_c_searchForExternalRepcodes: + bounds.lowerBound = (int)ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_ps_disable; + return bounds; + default: bounds.error = ERROR(parameter_unsupported); return bounds; @@ -625,9 +692,13 @@ case ZSTD_c_stableOutBuffer: case ZSTD_c_blockDelimiters: case ZSTD_c_validateSequences: - case ZSTD_c_splitBlocks: + case ZSTD_c_useBlockSplitter: case ZSTD_c_useRowMatchFinder: case ZSTD_c_deterministicRefPrefix: + case ZSTD_c_prefetchCDictTables: + case ZSTD_c_enableSeqProducerFallback: + case ZSTD_c_maxBlockSize: + case ZSTD_c_searchForExternalRepcodes: default: return 0; } @@ -640,7 +711,7 @@ if (ZSTD_isUpdateAuthorized(param)) { cctx->cParamsChanged = 1; } else { - RETURN_ERROR(stage_wrong, "can only set params in ctx init stage"); + RETURN_ERROR(stage_wrong, "can only set params in cctx init stage"); } } switch(param) @@ -680,9 +751,13 @@ case ZSTD_c_stableOutBuffer: case ZSTD_c_blockDelimiters: case ZSTD_c_validateSequences: - case ZSTD_c_splitBlocks: + case ZSTD_c_useBlockSplitter: case ZSTD_c_useRowMatchFinder: case ZSTD_c_deterministicRefPrefix: + case ZSTD_c_prefetchCDictTables: + case ZSTD_c_enableSeqProducerFallback: + case ZSTD_c_maxBlockSize: + case ZSTD_c_searchForExternalRepcodes: break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); @@ -738,12 +813,12 @@ case ZSTD_c_minMatch : if (value!=0) /* 0 => use default */ BOUNDCHECK(ZSTD_c_minMatch, value); - CCtxParams->cParams.minMatch = value; + CCtxParams->cParams.minMatch = (U32)value; return CCtxParams->cParams.minMatch; case ZSTD_c_targetLength : BOUNDCHECK(ZSTD_c_targetLength, value); - CCtxParams->cParams.targetLength = value; + CCtxParams->cParams.targetLength = (U32)value; return CCtxParams->cParams.targetLength; case ZSTD_c_strategy : @@ -756,12 +831,12 @@ /* Content size written in frame header _when known_ (default:1) */ DEBUGLOG(4, "set content size flag = %u", (value!=0)); CCtxParams->fParams.contentSizeFlag = value != 0; - return CCtxParams->fParams.contentSizeFlag; + return (size_t)CCtxParams->fParams.contentSizeFlag; case ZSTD_c_checksumFlag : /* A 32-bits content checksum will be calculated and written at end of frame (default:0) */ CCtxParams->fParams.checksumFlag = value != 0; - return CCtxParams->fParams.checksumFlag; + return (size_t)CCtxParams->fParams.checksumFlag; case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */ DEBUGLOG(4, "set dictIDFlag = %u", (value!=0)); @@ -770,18 +845,18 @@ case ZSTD_c_forceMaxWindow : CCtxParams->forceWindow = (value != 0); - return CCtxParams->forceWindow; + return (size_t)CCtxParams->forceWindow; case ZSTD_c_forceAttachDict : { const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value; - BOUNDCHECK(ZSTD_c_forceAttachDict, pref); + BOUNDCHECK(ZSTD_c_forceAttachDict, (int)pref); CCtxParams->attachDictPref = pref; return CCtxParams->attachDictPref; } case ZSTD_c_literalCompressionMode : { - const ZSTD_literalCompressionMode_e lcm = (ZSTD_literalCompressionMode_e)value; - BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm); + const ZSTD_paramSwitch_e lcm = (ZSTD_paramSwitch_e)value; + BOUNDCHECK(ZSTD_c_literalCompressionMode, (int)lcm); CCtxParams->literalCompressionMode = lcm; return CCtxParams->literalCompressionMode; } @@ -832,47 +907,48 @@ case ZSTD_c_enableDedicatedDictSearch : CCtxParams->enableDedicatedDictSearch = (value!=0); - return CCtxParams->enableDedicatedDictSearch; + return (size_t)CCtxParams->enableDedicatedDictSearch; case ZSTD_c_enableLongDistanceMatching : - CCtxParams->ldmParams.enableLdm = (value!=0); + BOUNDCHECK(ZSTD_c_enableLongDistanceMatching, value); + CCtxParams->ldmParams.enableLdm = (ZSTD_paramSwitch_e)value; return CCtxParams->ldmParams.enableLdm; case ZSTD_c_ldmHashLog : if (value!=0) /* 0 ==> auto */ BOUNDCHECK(ZSTD_c_ldmHashLog, value); - CCtxParams->ldmParams.hashLog = value; + CCtxParams->ldmParams.hashLog = (U32)value; return CCtxParams->ldmParams.hashLog; case ZSTD_c_ldmMinMatch : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmMinMatch, value); - CCtxParams->ldmParams.minMatchLength = value; + CCtxParams->ldmParams.minMatchLength = (U32)value; return CCtxParams->ldmParams.minMatchLength; case ZSTD_c_ldmBucketSizeLog : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value); - CCtxParams->ldmParams.bucketSizeLog = value; + CCtxParams->ldmParams.bucketSizeLog = (U32)value; return CCtxParams->ldmParams.bucketSizeLog; case ZSTD_c_ldmHashRateLog : - RETURN_ERROR_IF(value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN, - parameter_outOfBound, "Param out of bounds!"); - CCtxParams->ldmParams.hashRateLog = value; + if (value!=0) /* 0 ==> default */ + BOUNDCHECK(ZSTD_c_ldmHashRateLog, value); + CCtxParams->ldmParams.hashRateLog = (U32)value; return CCtxParams->ldmParams.hashRateLog; case ZSTD_c_targetCBlockSize : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_targetCBlockSize, value); - CCtxParams->targetCBlockSize = value; + CCtxParams->targetCBlockSize = (U32)value; return CCtxParams->targetCBlockSize; case ZSTD_c_srcSizeHint : if (value!=0) /* 0 ==> default */ BOUNDCHECK(ZSTD_c_srcSizeHint, value); CCtxParams->srcSizeHint = value; - return CCtxParams->srcSizeHint; + return (size_t)CCtxParams->srcSizeHint; case ZSTD_c_stableInBuffer: BOUNDCHECK(ZSTD_c_stableInBuffer, value); @@ -894,14 +970,14 @@ CCtxParams->validateSequences = value; return CCtxParams->validateSequences; - case ZSTD_c_splitBlocks: - BOUNDCHECK(ZSTD_c_splitBlocks, value); - CCtxParams->splitBlocks = value; - return CCtxParams->splitBlocks; + case ZSTD_c_useBlockSplitter: + BOUNDCHECK(ZSTD_c_useBlockSplitter, value); + CCtxParams->useBlockSplitter = (ZSTD_paramSwitch_e)value; + return CCtxParams->useBlockSplitter; case ZSTD_c_useRowMatchFinder: BOUNDCHECK(ZSTD_c_useRowMatchFinder, value); - CCtxParams->useRowMatchFinder = (ZSTD_useRowMatchFinderMode_e)value; + CCtxParams->useRowMatchFinder = (ZSTD_paramSwitch_e)value; return CCtxParams->useRowMatchFinder; case ZSTD_c_deterministicRefPrefix: @@ -909,6 +985,27 @@ CCtxParams->deterministicRefPrefix = !!value; return CCtxParams->deterministicRefPrefix; + case ZSTD_c_prefetchCDictTables: + BOUNDCHECK(ZSTD_c_prefetchCDictTables, value); + CCtxParams->prefetchCDictTables = (ZSTD_paramSwitch_e)value; + return CCtxParams->prefetchCDictTables; + + case ZSTD_c_enableSeqProducerFallback: + BOUNDCHECK(ZSTD_c_enableSeqProducerFallback, value); + CCtxParams->enableMatchFinderFallback = value; + return CCtxParams->enableMatchFinderFallback; + + case ZSTD_c_maxBlockSize: + if (value!=0) /* 0 ==> default */ + BOUNDCHECK(ZSTD_c_maxBlockSize, value); + CCtxParams->maxBlockSize = value; + return CCtxParams->maxBlockSize; + + case ZSTD_c_searchForExternalRepcodes: + BOUNDCHECK(ZSTD_c_searchForExternalRepcodes, value); + CCtxParams->searchForExternalRepcodes = (ZSTD_paramSwitch_e)value; + return CCtxParams->searchForExternalRepcodes; + default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } } @@ -1032,8 +1129,8 @@ case ZSTD_c_validateSequences : *value = (int)CCtxParams->validateSequences; break; - case ZSTD_c_splitBlocks : - *value = (int)CCtxParams->splitBlocks; + case ZSTD_c_useBlockSplitter : + *value = (int)CCtxParams->useBlockSplitter; break; case ZSTD_c_useRowMatchFinder : *value = (int)CCtxParams->useRowMatchFinder; @@ -1041,6 +1138,18 @@ case ZSTD_c_deterministicRefPrefix: *value = (int)CCtxParams->deterministicRefPrefix; break; + case ZSTD_c_prefetchCDictTables: + *value = (int)CCtxParams->prefetchCDictTables; + break; + case ZSTD_c_enableSeqProducerFallback: + *value = CCtxParams->enableMatchFinderFallback; + break; + case ZSTD_c_maxBlockSize: + *value = (int)CCtxParams->maxBlockSize; + break; + case ZSTD_c_searchForExternalRepcodes: + *value = (int)CCtxParams->searchForExternalRepcodes; + break; default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); } return 0; @@ -1067,9 +1176,47 @@ return 0; } -ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) +size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams) { - DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); + ZSTD_STATIC_ASSERT(sizeof(cparams) == 7 * 4 /* all params are listed below */); + DEBUGLOG(4, "ZSTD_CCtx_setCParams"); + /* only update if all parameters are valid */ + FORWARD_IF_ERROR(ZSTD_checkCParams(cparams), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, cparams.windowLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_chainLog, cparams.chainLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_hashLog, cparams.hashLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_searchLog, cparams.searchLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_minMatch, cparams.minMatch), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_targetLength, cparams.targetLength), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_strategy, cparams.strategy), ""); + return 0; +} + +size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams) +{ + ZSTD_STATIC_ASSERT(sizeof(fparams) == 3 * 4 /* all params are listed below */); + DEBUGLOG(4, "ZSTD_CCtx_setFParams"); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, fparams.contentSizeFlag != 0), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, fparams.checksumFlag != 0), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_dictIDFlag, fparams.noDictIDFlag == 0), ""); + return 0; +} + +size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params) +{ + DEBUGLOG(4, "ZSTD_CCtx_setParams"); + /* First check cParams, because we want to update all or none. */ + FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), ""); + /* Next set fParams, because this could fail if the cctx isn't in init stage. */ + FORWARD_IF_ERROR(ZSTD_CCtx_setFParams(cctx, params.fParams), ""); + /* Finally set cParams, which should succeed. */ + FORWARD_IF_ERROR(ZSTD_CCtx_setCParams(cctx, params.cParams), ""); + return 0; +} + +size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) +{ + DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %llu bytes", pledgedSrcSize); RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, "Can't set pledgedSrcSize when not in init stage."); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; @@ -1085,9 +1232,9 @@ ZSTD_compressionParameters* cParams); /** - * Initializes the local dict using the requested parameters. - * NOTE: This does not use the pledged src size, because it may be used for more - * than one compression. + * Initializes the local dictionary using requested parameters. + * NOTE: Initialization does not employ the pledged src size, + * because the dictionary may be used for multiple compressions. */ static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx) { @@ -1100,8 +1247,8 @@ return 0; } if (dl->cdict != NULL) { - assert(cctx->cdict == dl->cdict); /* Local dictionary already initialized. */ + assert(cctx->cdict == dl->cdict); return 0; } assert(dl->dictSize > 0); @@ -1121,40 +1268,44 @@ } size_t ZSTD_CCtx_loadDictionary_advanced( - ZSTD_CCtx* cctx, const void* dict, size_t dictSize, - ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType) + ZSTD_CCtx* cctx, + const void* dict, size_t dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType) { - RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, - "Can't load a dictionary when ctx is not in init stage."); DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); - ZSTD_clearAllDicts(cctx); /* in case one already exists */ - if (dict == NULL || dictSize == 0) /* no dictionary mode */ + RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, + "Can't load a dictionary when cctx is not in init stage."); + ZSTD_clearAllDicts(cctx); /* erase any previously set dictionary */ + if (dict == NULL || dictSize == 0) /* no dictionary */ return 0; if (dictLoadMethod == ZSTD_dlm_byRef) { cctx->localDict.dict = dict; } else { + /* copy dictionary content inside CCtx to own its lifetime */ void* dictBuffer; RETURN_ERROR_IF(cctx->staticSize, memory_allocation, - "no malloc for static CCtx"); + "static CCtx can't allocate for an internal copy of dictionary"); dictBuffer = ZSTD_customMalloc(dictSize, cctx->customMem); - RETURN_ERROR_IF(!dictBuffer, memory_allocation, "NULL pointer!"); + RETURN_ERROR_IF(dictBuffer==NULL, memory_allocation, + "allocation failed for dictionary content"); ZSTD_memcpy(dictBuffer, dict, dictSize); - cctx->localDict.dictBuffer = dictBuffer; - cctx->localDict.dict = dictBuffer; + cctx->localDict.dictBuffer = dictBuffer; /* owned ptr to free */ + cctx->localDict.dict = dictBuffer; /* read-only reference */ } cctx->localDict.dictSize = dictSize; cctx->localDict.dictContentType = dictContentType; return 0; } -ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference( +size_t ZSTD_CCtx_loadDictionary_byReference( ZSTD_CCtx* cctx, const void* dict, size_t dictSize) { return ZSTD_CCtx_loadDictionary_advanced( cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto); } -ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize) +size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize) { return ZSTD_CCtx_loadDictionary_advanced( cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto); @@ -1210,8 +1361,9 @@ if ( (reset == ZSTD_reset_parameters) || (reset == ZSTD_reset_session_and_parameters) ) { RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, - "Can't reset parameters only when not in init stage."); + "Reset parameters is only possible during init stage."); ZSTD_clearAllDicts(cctx); + ZSTD_memset(&cctx->externalMatchCtx, 0, sizeof(cctx->externalMatchCtx)); return ZSTD_CCtxParams_reset(&cctx->requestedParams); } return 0; @@ -1308,7 +1460,8 @@ ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize, - ZSTD_cParamMode_e mode) + ZSTD_cParamMode_e mode, + ZSTD_paramSwitch_e useRowMatchFinder) { const U64 minSrcSize = 513; /* (1<<9) + 1 */ const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1); @@ -1324,7 +1477,7 @@ break; case ZSTD_cpm_createCDict: /* Assume a small source size when creating a dictionary - * with an unkown source size. + * with an unknown source size. */ if (dictSize && srcSize == ZSTD_CONTENTSIZE_UNKNOWN) srcSize = minSrcSize; @@ -1342,8 +1495,8 @@ } /* resize windowLog if input is small enough, to use less memory */ - if ( (srcSize < maxWindowResize) - && (dictSize < maxWindowResize) ) { + if ( (srcSize <= maxWindowResize) + && (dictSize <= maxWindowResize) ) { U32 const tSize = (U32)(srcSize + dictSize); static U32 const hashSizeMin = 1 << ZSTD_HASHLOG_MIN; U32 const srcLog = (tSize < hashSizeMin) ? ZSTD_HASHLOG_MIN : @@ -1361,6 +1514,42 @@ if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */ + /* We can't use more than 32 bits of hash in total, so that means that we require: + * (hashLog + 8) <= 32 && (chainLog + 8) <= 32 + */ + if (mode == ZSTD_cpm_createCDict && ZSTD_CDictIndicesAreTagged(&cPar)) { + U32 const maxShortCacheHashLog = 32 - ZSTD_SHORT_CACHE_TAG_BITS; + if (cPar.hashLog > maxShortCacheHashLog) { + cPar.hashLog = maxShortCacheHashLog; + } + if (cPar.chainLog > maxShortCacheHashLog) { + cPar.chainLog = maxShortCacheHashLog; + } + } + + + /* At this point, we aren't 100% sure if we are using the row match finder. + * Unless it is explicitly disabled, conservatively assume that it is enabled. + * In this case it will only be disabled for small sources, so shrinking the + * hash log a little bit shouldn't result in any ratio loss. + */ + if (useRowMatchFinder == ZSTD_ps_auto) + useRowMatchFinder = ZSTD_ps_enable; + + /* We can't hash more than 32-bits in total. So that means that we require: + * (hashLog - rowLog + 8) <= 32 + */ + if (ZSTD_rowMatchFinderUsed(cPar.strategy, useRowMatchFinder)) { + /* Switch to 32-entry rows if searchLog is 5 (or more) */ + U32 const rowLog = BOUNDED(4, cPar.searchLog, 6); + U32 const maxRowHashLog = 32 - ZSTD_ROW_HASH_TAG_BITS; + U32 const maxHashLog = maxRowHashLog + rowLog; + assert(cPar.hashLog >= rowLog); + if (cPar.hashLog > maxHashLog) { + cPar.hashLog = maxHashLog; + } + } + return cPar; } @@ -1371,7 +1560,7 @@ { cPar = ZSTD_clampCParams(cPar); /* resulting cPar is necessarily valid (all parameters within range) */ if (srcSize == 0) srcSize = ZSTD_CONTENTSIZE_UNKNOWN; - return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown); + return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown, ZSTD_ps_auto); } static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode); @@ -1398,16 +1587,16 @@ srcSizeHint = CCtxParams->srcSizeHint; } cParams = ZSTD_getCParams_internal(CCtxParams->compressionLevel, srcSizeHint, dictSize, mode); - if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG; + if (CCtxParams->ldmParams.enableLdm == ZSTD_ps_enable) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG; ZSTD_overrideCParams(&cParams, &CCtxParams->cParams); assert(!ZSTD_checkCParams(cParams)); /* srcSizeHint == 0 means 0 */ - return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode); + return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode, CCtxParams->useRowMatchFinder); } static size_t ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, - const ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + const ZSTD_paramSwitch_e useRowMatchFinder, const U32 enableDedicatedDictSearch, const U32 forCCtx) { @@ -1431,7 +1620,7 @@ + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t)) + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); size_t const lazyAdditionalSpace = ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder) - ? ZSTD_cwksp_aligned_alloc_size(hSize*sizeof(U16)) + ? ZSTD_cwksp_aligned_alloc_size(hSize) : 0; size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt)) ? optPotentialSpace @@ -1440,26 +1629,34 @@ /* tables are guaranteed to be sized in multiples of 64 bytes (or 16 uint32_t) */ ZSTD_STATIC_ASSERT(ZSTD_HASHLOG_MIN >= 4 && ZSTD_WINDOWLOG_MIN >= 4 && ZSTD_CHAINLOG_MIN >= 4); - assert(useRowMatchFinder != ZSTD_urm_auto); + assert(useRowMatchFinder != ZSTD_ps_auto); DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u", (U32)chainSize, (U32)hSize, (U32)h3Size); return tableSpace + optSpace + slackSpace + lazyAdditionalSpace; } +/* Helper function for calculating memory requirements. + * Gives a tighter bound than ZSTD_sequenceBound() by taking minMatch into account. */ +static size_t ZSTD_maxNbSeq(size_t blockSize, unsigned minMatch, int useSequenceProducer) { + U32 const divider = (minMatch==3 || useSequenceProducer) ? 3 : 4; + return blockSize / divider; +} + static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( const ZSTD_compressionParameters* cParams, const ldmParams_t* ldmParams, const int isStatic, - const ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + const ZSTD_paramSwitch_e useRowMatchFinder, const size_t buffInSize, const size_t buffOutSize, - const U64 pledgedSrcSize) + const U64 pledgedSrcSize, + int useSequenceProducer, + size_t maxBlockSize) { - size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << cParams->windowLog), pledgedSrcSize)); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - U32 const divider = (cParams->minMatch==3) ? 3 : 4; - size_t const maxNbSeq = blockSize / divider; + size_t const windowSize = (size_t) BOUNDED(1ULL, 1ULL << cParams->windowLog, pledgedSrcSize); + size_t const blockSize = MIN(ZSTD_resolveMaxBlockSize(maxBlockSize), windowSize); + size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, cParams->minMatch, useSequenceProducer); size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize) + ZSTD_cwksp_aligned_alloc_size(maxNbSeq * sizeof(seqDef)) + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE)); @@ -1469,7 +1666,7 @@ size_t const ldmSpace = ZSTD_ldm_getTableSize(*ldmParams); size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(*ldmParams, blockSize); - size_t const ldmSeqSpace = ldmParams->enableLdm ? + size_t const ldmSeqSpace = ldmParams->enableLdm == ZSTD_ps_enable ? ZSTD_cwksp_aligned_alloc_size(maxNbLdmSeq * sizeof(rawSeq)) : 0; @@ -1478,6 +1675,11 @@ size_t const cctxSpace = isStatic ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0; + size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + size_t const externalSeqSpace = useSequenceProducer + ? ZSTD_cwksp_aligned_alloc_size(maxNbExternalSeq * sizeof(ZSTD_Sequence)) + : 0; + size_t const neededSpace = cctxSpace + entropySpace + @@ -1486,7 +1688,8 @@ ldmSeqSpace + matchStateSize + tokenSpace + - bufferSpace; + bufferSpace + + externalSeqSpace; DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace); return neededSpace; @@ -1496,15 +1699,15 @@ { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict); - ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, - &cParams); + ZSTD_paramSwitch_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, + &cParams); RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); /* estimateCCtxSize is for one-shot compression. So no buffers should * be needed. However, we still allocate two 0-sized buffers, which can * take space under ASAN. */ return ZSTD_estimateCCtxSize_usingCCtxParams_internal( - &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN); + &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize); } size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) @@ -1514,9 +1717,9 @@ /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */ size_t noRowCCtxSize; size_t rowCCtxSize; - initialParams.useRowMatchFinder = ZSTD_urm_disableRowMatchFinder; + initialParams.useRowMatchFinder = ZSTD_ps_disable; noRowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams); - initialParams.useRowMatchFinder = ZSTD_urm_enableRowMatchFinder; + initialParams.useRowMatchFinder = ZSTD_ps_enable; rowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams); return MAX(noRowCCtxSize, rowCCtxSize); } else { @@ -1554,18 +1757,18 @@ RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only."); { ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog); + size_t const blockSize = MIN(ZSTD_resolveMaxBlockSize(params->maxBlockSize), (size_t)1 << cParams.windowLog); size_t const inBuffSize = (params->inBufferMode == ZSTD_bm_buffered) ? ((size_t)1 << cParams.windowLog) + blockSize : 0; size_t const outBuffSize = (params->outBufferMode == ZSTD_bm_buffered) ? ZSTD_compressBound(blockSize) + 1 : 0; - ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, ¶ms->cParams); + ZSTD_paramSwitch_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, ¶ms->cParams); return ZSTD_estimateCCtxSize_usingCCtxParams_internal( &cParams, ¶ms->ldmParams, 1, useRowMatchFinder, inBuffSize, outBuffSize, - ZSTD_CONTENTSIZE_UNKNOWN); + ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize); } } @@ -1576,9 +1779,9 @@ /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */ size_t noRowCCtxSize; size_t rowCCtxSize; - initialParams.useRowMatchFinder = ZSTD_urm_disableRowMatchFinder; + initialParams.useRowMatchFinder = ZSTD_ps_disable; noRowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams); - initialParams.useRowMatchFinder = ZSTD_urm_enableRowMatchFinder; + initialParams.useRowMatchFinder = ZSTD_ps_enable; rowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams); return MAX(noRowCCtxSize, rowCCtxSize); } else { @@ -1708,12 +1911,25 @@ ZSTD_resetTarget_CCtx } ZSTD_resetTarget_e; +/* Mixes bits in a 64 bits in a value, based on XXH3_rrmxmx */ +static U64 ZSTD_bitmix(U64 val, U64 len) { + val ^= ZSTD_rotateRight_U64(val, 49) ^ ZSTD_rotateRight_U64(val, 24); + val *= 0x9FB21C651E98DF25ULL; + val ^= (val >> 35) + len ; + val *= 0x9FB21C651E98DF25ULL; + return val ^ (val >> 28); +} + +/* Mixes in the hashSalt and hashSaltEntropy to create a new hashSalt */ +static void ZSTD_advanceHashSalt(ZSTD_matchState_t* ms) { + ms->hashSalt = ZSTD_bitmix(ms->hashSalt, 8) ^ ZSTD_bitmix((U64) ms->hashSaltEntropy, 4); +} static size_t ZSTD_reset_matchState(ZSTD_matchState_t* ms, ZSTD_cwksp* ws, const ZSTD_compressionParameters* cParams, - const ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + const ZSTD_paramSwitch_e useRowMatchFinder, const ZSTD_compResetPolicy_e crp, const ZSTD_indexResetPolicy_e forceResetIndex, const ZSTD_resetTarget_e forWho) @@ -1728,13 +1944,14 @@ size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0; DEBUGLOG(4, "reset indices : %u", forceResetIndex == ZSTDirp_reset); - assert(useRowMatchFinder != ZSTD_urm_auto); + assert(useRowMatchFinder != ZSTD_ps_auto); if (forceResetIndex == ZSTDirp_reset) { ZSTD_window_init(&ms->window); ZSTD_cwksp_mark_tables_dirty(ws); } ms->hashLog3 = hashLog3; + ms->lazySkipping = 0; ZSTD_invalidateMatchState(ms); @@ -1756,6 +1973,27 @@ ZSTD_cwksp_clean_tables(ws); } + if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) { + /* Row match finder needs an additional table of hashes ("tags") */ + size_t const tagTableSize = hSize; + /* We want to generate a new salt in case we reset a Cctx, but we always want to use + * 0 when we reset a Cdict */ + if(forWho == ZSTD_resetTarget_CCtx) { + ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned_init_once(ws, tagTableSize); + ZSTD_advanceHashSalt(ms); + } else { + /* When we are not salting we want to always memset the memory */ + ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned(ws, tagTableSize); + ZSTD_memset(ms->tagTable, 0, tagTableSize); + ms->hashSalt = 0; + } + { /* Switch to 32-entry rows if searchLog is 5 (or more) */ + U32 const rowLog = BOUNDED(4, cParams->searchLog, 6); + assert(cParams->hashLog >= rowLog); + ms->rowHashLog = cParams->hashLog - rowLog; + } + } + /* opt parser space */ if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) { DEBUGLOG(4, "reserving optimal parser space"); @@ -1767,19 +2005,6 @@ ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); } - if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) { - { /* Row match finder needs an additional table of hashes ("tags") */ - size_t const tagTableSize = hSize*sizeof(U16); - ms->tagTable = (U16*)ZSTD_cwksp_reserve_aligned(ws, tagTableSize); - if (ms->tagTable) ZSTD_memset(ms->tagTable, 0, tagTableSize); - } - { /* Switch to 32-entry rows if searchLog is 5 (or more) */ - U32 const rowLog = cParams->searchLog < 5 ? 4 : 5; - assert(cParams->hashLog > rowLog); - ms->rowHashLog = cParams->hashLog - rowLog; - } - } - ms->cParams = *cParams; RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation, @@ -1824,8 +2049,8 @@ ZSTD_buffered_policy_e const zbuff) { ZSTD_cwksp* const ws = &zc->workspace; - DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u, useRowMatchFinder=%d", - (U32)pledgedSrcSize, params->cParams.windowLog, (int)params->useRowMatchFinder); + DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u, useRowMatchFinder=%d useBlockSplitter=%d", + (U32)pledgedSrcSize, params->cParams.windowLog, (int)params->useRowMatchFinder, (int)params->useBlockSplitter); assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams))); zc->isFirstBlock = 1; @@ -1836,8 +2061,11 @@ zc->appliedParams = *params; params = &zc->appliedParams; - assert(params->useRowMatchFinder != ZSTD_urm_auto); - if (params->ldmParams.enableLdm) { + assert(params->useRowMatchFinder != ZSTD_ps_auto); + assert(params->useBlockSplitter != ZSTD_ps_auto); + assert(params->ldmParams.enableLdm != ZSTD_ps_auto); + assert(params->maxBlockSize != 0); + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { /* Adjust long distance matching parameters */ ZSTD_ldm_adjustParameters(&zc->appliedParams.ldmParams, ¶ms->cParams); assert(params->ldmParams.hashLog >= params->ldmParams.bucketSizeLog); @@ -1845,9 +2073,8 @@ } { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params->cParams.windowLog), pledgedSrcSize)); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - U32 const divider = (params->cParams.minMatch==3) ? 3 : 4; - size_t const maxNbSeq = blockSize / divider; + size_t const blockSize = MIN(params->maxBlockSize, windowSize); + size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, params->cParams.minMatch, params->useSequenceProducer); size_t const buffOutSize = (zbuff == ZSTDb_buffered && params->outBufferMode == ZSTD_bm_buffered) ? ZSTD_compressBound(blockSize) + 1 : 0; @@ -1864,7 +2091,7 @@ size_t const neededSpace = ZSTD_estimateCCtxSize_usingCCtxParams_internal( ¶ms->cParams, ¶ms->ldmParams, zc->staticSize != 0, params->useRowMatchFinder, - buffInSize, buffOutSize, pledgedSrcSize); + buffInSize, buffOutSize, pledgedSrcSize, params->useSequenceProducer, params->maxBlockSize); int resizeWorkspace; FORWARD_IF_ERROR(neededSpace, "cctx size estimate failed!"); @@ -1900,13 +2127,14 @@ zc->blockState.nextCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t)); RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate nextCBlock"); zc->entropyWorkspace = (U32*) ZSTD_cwksp_reserve_object(ws, ENTROPY_WORKSPACE_SIZE); - RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate entropyWorkspace"); + RETURN_ERROR_IF(zc->entropyWorkspace == NULL, memory_allocation, "couldn't allocate entropyWorkspace"); } } ZSTD_cwksp_clear(ws); /* init params */ zc->blockState.matchState.cParams = params->cParams; + zc->blockState.matchState.prefetchCDictTables = params->prefetchCDictTables == ZSTD_ps_enable; zc->pledgedSrcSizePlusOne = pledgedSrcSize+1; zc->consumedSrcSize = 0; zc->producedCSize = 0; @@ -1923,13 +2151,46 @@ ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock); + FORWARD_IF_ERROR(ZSTD_reset_matchState( + &zc->blockState.matchState, + ws, + ¶ms->cParams, + params->useRowMatchFinder, + crp, + needsIndexReset, + ZSTD_resetTarget_CCtx), ""); + + zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); + + /* ldm hash table */ + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { + /* TODO: avoid memset? */ + size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog; + zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t)); + ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t)); + zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq)); + zc->maxNbLdmSequences = maxNbLdmSeq; + + ZSTD_window_init(&zc->ldmState.window); + zc->ldmState.loadedDictEnd = 0; + } + + /* reserve space for block-level external sequences */ + if (params->useSequenceProducer) { + size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + zc->externalMatchCtx.seqBufferCapacity = maxNbExternalSeq; + zc->externalMatchCtx.seqBuffer = + (ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned(ws, maxNbExternalSeq * sizeof(ZSTD_Sequence)); + } + + /* buffers */ + /* ZSTD_wildcopy() is used to copy into the literals buffer, * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes. */ zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + WILDCOPY_OVERLENGTH); zc->seqStore.maxNbLit = blockSize; - /* buffers */ zc->bufferedPolicy = zbuff; zc->inBuffSize = buffInSize; zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize); @@ -1937,7 +2198,7 @@ zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize); /* ldm bucketOffsets table */ - if (params->ldmParams.enableLdm) { + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { /* TODO: avoid memset? */ size_t const numBuckets = ((size_t)1) << (params->ldmParams.hashLog - @@ -1952,32 +2213,9 @@ zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); - zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); - FORWARD_IF_ERROR(ZSTD_reset_matchState( - &zc->blockState.matchState, - ws, - ¶ms->cParams, - params->useRowMatchFinder, - crp, - needsIndexReset, - ZSTD_resetTarget_CCtx), ""); - - /* ldm hash table */ - if (params->ldmParams.enableLdm) { - /* TODO: avoid memset? */ - size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog; - zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t)); - ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t)); - zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq)); - zc->maxNbLdmSequences = maxNbLdmSeq; - - ZSTD_window_init(&zc->ldmState.window); - zc->ldmState.loadedDictEnd = 0; - } - - assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace, resizeWorkspace)); DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws)); + assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace)); zc->initialized = 1; @@ -2049,7 +2287,8 @@ } params.cParams = ZSTD_adjustCParams_internal(adjusted_cdict_cParams, pledgedSrcSize, - cdict->dictContentSize, ZSTD_cpm_attachDict); + cdict->dictContentSize, ZSTD_cpm_attachDict, + params.useRowMatchFinder); params.cParams.windowLog = windowLog; params.useRowMatchFinder = cdict->useRowMatchFinder; /* cdict overrides */ FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, ¶ms, pledgedSrcSize, @@ -2088,6 +2327,22 @@ return 0; } +static void ZSTD_copyCDictTableIntoCCtx(U32* dst, U32 const* src, size_t tableSize, + ZSTD_compressionParameters const* cParams) { + if (ZSTD_CDictIndicesAreTagged(cParams)){ + /* Remove tags from the CDict table if they are present. + * See docs on "short cache" in zstd_compress_internal.h for context. */ + size_t i; + for (i = 0; i < tableSize; i++) { + U32 const taggedIndex = src[i]; + U32 const index = taggedIndex >> ZSTD_SHORT_CACHE_TAG_BITS; + dst[i] = index; + } + } else { + ZSTD_memcpy(dst, src, tableSize * sizeof(U32)); + } +} + static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, @@ -2115,7 +2370,7 @@ } ZSTD_cwksp_mark_tables_dirty(&cctx->workspace); - assert(params.useRowMatchFinder != ZSTD_urm_auto); + assert(params.useRowMatchFinder != ZSTD_ps_auto); /* copy tables */ { size_t const chainSize = ZSTD_allocateChainTable(cdict_cParams->strategy, cdict->useRowMatchFinder, 0 /* DDS guaranteed disabled */) @@ -2123,21 +2378,23 @@ : 0; size_t const hSize = (size_t)1 << cdict_cParams->hashLog; - ZSTD_memcpy(cctx->blockState.matchState.hashTable, - cdict->matchState.hashTable, - hSize * sizeof(U32)); + ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.hashTable, + cdict->matchState.hashTable, + hSize, cdict_cParams); + /* Do not copy cdict's chainTable if cctx has parameters such that it would not use chainTable */ if (ZSTD_allocateChainTable(cctx->appliedParams.cParams.strategy, cctx->appliedParams.useRowMatchFinder, 0 /* forDDSDict */)) { - ZSTD_memcpy(cctx->blockState.matchState.chainTable, - cdict->matchState.chainTable, - chainSize * sizeof(U32)); + ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.chainTable, + cdict->matchState.chainTable, + chainSize, cdict_cParams); } /* copy tag table */ if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder)) { - size_t const tagTableSize = hSize*sizeof(U16); + size_t const tagTableSize = hSize; ZSTD_memcpy(cctx->blockState.matchState.tagTable, - cdict->matchState.tagTable, - tagTableSize); + cdict->matchState.tagTable, + tagTableSize); + cctx->blockState.matchState.hashSalt = cdict->matchState.hashSalt; } } @@ -2209,9 +2466,14 @@ { ZSTD_CCtx_params params = dstCCtx->requestedParams; /* Copy only compression parameters related to tables. */ params.cParams = srcCCtx->appliedParams.cParams; - assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_urm_auto); + assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_ps_auto); + assert(srcCCtx->appliedParams.useBlockSplitter != ZSTD_ps_auto); + assert(srcCCtx->appliedParams.ldmParams.enableLdm != ZSTD_ps_auto); params.useRowMatchFinder = srcCCtx->appliedParams.useRowMatchFinder; + params.useBlockSplitter = srcCCtx->appliedParams.useBlockSplitter; + params.ldmParams = srcCCtx->appliedParams.ldmParams; params.fParams = fParams; + params.maxBlockSize = srcCCtx->appliedParams.maxBlockSize; ZSTD_resetCCtx_internal(dstCCtx, ¶ms, pledgedSrcSize, /* loadedDictSize */ 0, ZSTDcrp_leaveDirty, zbuff); @@ -2296,6 +2558,8 @@ int const nbRows = (int)size / ZSTD_ROWSIZE; int cellNb = 0; int rowNb; + /* Protect special index values < ZSTD_WINDOW_START_INDEX. */ + U32 const reducerThreshold = reducerValue + ZSTD_WINDOW_START_INDEX; assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */ assert(size < (1U<<31)); /* can be casted to int */ @@ -2315,12 +2579,17 @@ for (rowNb=0 ; rowNb < nbRows ; rowNb++) { int column; for (column=0; column<ZSTD_ROWSIZE; column++) { - if (preserveMark) { - U32 const adder = (table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) ? reducerValue : 0; - table[cellNb] += adder; + U32 newVal; + if (preserveMark && table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) { + /* This write is pointless, but is required(?) for the compiler + * to auto-vectorize the loop. */ + newVal = ZSTD_DUBT_UNSORTED_MARK; + } else if (table[cellNb] < reducerThreshold) { + newVal = 0; + } else { + newVal = table[cellNb] - reducerValue; } - if (table[cellNb] < reducerValue) table[cellNb] = 0; - else table[cellNb] -= reducerValue; + table[cellNb] = newVal; cellNb++; } } } @@ -2364,7 +2633,7 @@ /* See doc/zstd_compression_format.md for detailed format description */ -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) +int ZSTD_seqToCodes(const seqStore_t* seqStorePtr) { const seqDef* const sequences = seqStorePtr->sequencesStart; BYTE* const llCodeTable = seqStorePtr->llCode; @@ -2372,18 +2641,24 @@ BYTE* const mlCodeTable = seqStorePtr->mlCode; U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); U32 u; + int longOffsets = 0; assert(nbSeq <= seqStorePtr->maxNbSeq); for (u=0; u<nbSeq; u++) { U32 const llv = sequences[u].litLength; - U32 const mlv = sequences[u].matchLength; + U32 const ofCode = ZSTD_highbit32(sequences[u].offBase); + U32 const mlv = sequences[u].mlBase; llCodeTable[u] = (BYTE)ZSTD_LLcode(llv); - ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset); + ofCodeTable[u] = (BYTE)ofCode; mlCodeTable[u] = (BYTE)ZSTD_MLcode(mlv); + assert(!(MEM_64bits() && ofCode >= STREAM_ACCUMULATOR_MIN)); + if (MEM_32bits() && ofCode >= STREAM_ACCUMULATOR_MIN) + longOffsets = 1; } if (seqStorePtr->longLengthType==ZSTD_llt_literalLength) llCodeTable[seqStorePtr->longLengthPos] = MaxLL; if (seqStorePtr->longLengthType==ZSTD_llt_matchLength) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; + return longOffsets; } /* ZSTD_useTargetCBlockSize(): @@ -2399,11 +2674,13 @@ /* ZSTD_blockSplitterEnabled(): * Returns if block splitting param is being used * If used, compression will do best effort to split a block in order to improve compression ratio. + * At the time this function is called, the parameter must be finalized. * Returns 1 if true, 0 otherwise. */ static int ZSTD_blockSplitterEnabled(ZSTD_CCtx_params* cctxParams) { - DEBUGLOG(5, "ZSTD_blockSplitterEnabled(splitBlocks=%d)", cctxParams->splitBlocks); - return (cctxParams->splitBlocks != 0); + DEBUGLOG(5, "ZSTD_blockSplitterEnabled (useBlockSplitter=%d)", cctxParams->useBlockSplitter); + assert(cctxParams->useBlockSplitter != ZSTD_ps_auto); + return (cctxParams->useBlockSplitter == ZSTD_ps_enable); } /* Type returned by ZSTD_buildSequencesStatistics containing finalized symbol encoding types @@ -2415,6 +2692,7 @@ U32 MLtype; size_t size; size_t lastCountSize; /* Accounts for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */ + int longOffsets; } ZSTD_symbolEncodingTypeStats_t; /* ZSTD_buildSequencesStatistics(): @@ -2425,11 +2703,13 @@ * entropyWkspSize must be of size at least ENTROPY_WORKSPACE_SIZE - (MaxSeq + 1)*sizeof(U32) */ static ZSTD_symbolEncodingTypeStats_t -ZSTD_buildSequencesStatistics(seqStore_t* seqStorePtr, size_t nbSeq, - const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy, - BYTE* dst, const BYTE* const dstEnd, - ZSTD_strategy strategy, unsigned* countWorkspace, - void* entropyWorkspace, size_t entropyWkspSize) { +ZSTD_buildSequencesStatistics( + const seqStore_t* seqStorePtr, size_t nbSeq, + const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy, + BYTE* dst, const BYTE* const dstEnd, + ZSTD_strategy strategy, unsigned* countWorkspace, + void* entropyWorkspace, size_t entropyWkspSize) +{ BYTE* const ostart = dst; const BYTE* const oend = dstEnd; BYTE* op = ostart; @@ -2443,7 +2723,7 @@ stats.lastCountSize = 0; /* convert length/distances into codes */ - ZSTD_seqToCodes(seqStorePtr); + stats.longOffsets = ZSTD_seqToCodes(seqStorePtr); assert(op <= oend); assert(nbSeq != 0); /* ZSTD_selectEncodingType() divides by nbSeq */ /* build CTable for Literal Lengths */ @@ -2546,23 +2826,24 @@ * compresses both literals and sequences * Returns compressed size of block, or a zstd error. */ +#define SUSPECT_UNCOMPRESSIBLE_LITERAL_RATIO 20 MEM_STATIC size_t -ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - void* dst, size_t dstCapacity, - void* entropyWorkspace, size_t entropyWkspSize, - const int bmi2) +ZSTD_entropyCompressSeqStore_internal( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + void* dst, size_t dstCapacity, + void* entropyWorkspace, size_t entropyWkspSize, + const int bmi2) { - const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; ZSTD_strategy const strategy = cctxParams->cParams.strategy; unsigned* count = (unsigned*)entropyWorkspace; FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable; FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable; const seqDef* const sequences = seqStorePtr->sequencesStart; - const size_t nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; + const size_t nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); const BYTE* const ofCodeTable = seqStorePtr->ofCode; const BYTE* const llCodeTable = seqStorePtr->llCode; const BYTE* const mlCodeTable = seqStorePtr->mlCode; @@ -2570,25 +2851,31 @@ BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; size_t lastCountSize; + int longOffsets = 0; entropyWorkspace = count + (MaxSeq + 1); entropyWkspSize -= (MaxSeq + 1) * sizeof(*count); - DEBUGLOG(4, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq); + DEBUGLOG(5, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu, dstCapacity=%zu)", nbSeq, dstCapacity); ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog))); assert(entropyWkspSize >= HUF_WORKSPACE_SIZE); /* Compress literals */ { const BYTE* const literals = seqStorePtr->litStart; + size_t const numSequences = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + size_t const numLiterals = (size_t)(seqStorePtr->lit - seqStorePtr->litStart); + /* Base suspicion of uncompressibility on ratio of literals to sequences */ + unsigned const suspectUncompressible = (numSequences == 0) || (numLiterals / numSequences >= SUSPECT_UNCOMPRESSIBLE_LITERAL_RATIO); size_t const litSize = (size_t)(seqStorePtr->lit - literals); + size_t const cSize = ZSTD_compressLiterals( - &prevEntropy->huf, &nextEntropy->huf, - cctxParams->cParams.strategy, - ZSTD_disableLiteralsCompression(cctxParams), op, dstCapacity, literals, litSize, entropyWorkspace, entropyWkspSize, - bmi2); + &prevEntropy->huf, &nextEntropy->huf, + cctxParams->cParams.strategy, + ZSTD_literalsCompressionIsDisabled(cctxParams), + suspectUncompressible, bmi2); FORWARD_IF_ERROR(cSize, "ZSTD_compressLiterals failed"); assert(cSize <= dstCapacity); op += cSize; @@ -2614,11 +2901,10 @@ ZSTD_memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse)); return (size_t)(op - ostart); } - { - ZSTD_symbolEncodingTypeStats_t stats; - BYTE* seqHead = op++; + { BYTE* const seqHead = op++; /* build stats for sequences */ - stats = ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq, + const ZSTD_symbolEncodingTypeStats_t stats = + ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq, &prevEntropy->fse, &nextEntropy->fse, op, oend, strategy, count, @@ -2627,6 +2913,7 @@ *seqHead = (BYTE)((stats.LLtype<<6) + (stats.Offtype<<4) + (stats.MLtype<<2)); lastCountSize = stats.lastCountSize; op += stats.size; + longOffsets = stats.longOffsets; } { size_t const bitstreamSize = ZSTD_encodeSequences( @@ -2661,14 +2948,15 @@ } MEM_STATIC size_t -ZSTD_entropyCompressSeqStore(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - void* dst, size_t dstCapacity, - size_t srcSize, - void* entropyWorkspace, size_t entropyWkspSize, - int bmi2) +ZSTD_entropyCompressSeqStore( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + void* dst, size_t dstCapacity, + size_t srcSize, + void* entropyWorkspace, size_t entropyWkspSize, + int bmi2) { size_t const cSize = ZSTD_entropyCompressSeqStore_internal( seqStorePtr, prevEntropy, nextEntropy, cctxParams, @@ -2678,22 +2966,28 @@ /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block. * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block. */ - if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) + if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) { + DEBUGLOG(4, "not enough dstCapacity (%zu) for ZSTD_entropyCompressSeqStore_internal()=> do not compress block", dstCapacity); return 0; /* block not compressed */ + } FORWARD_IF_ERROR(cSize, "ZSTD_entropyCompressSeqStore_internal failed"); /* Check compressibility */ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy); if (cSize >= maxCSize) return 0; /* block not compressed */ } - DEBUGLOG(4, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize); + DEBUGLOG(5, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize); + /* libzstd decoder before > v1.5.4 is not compatible with compressed blocks of size ZSTD_BLOCKSIZE_MAX exactly. + * This restriction is indirectly already fulfilled by respecting ZSTD_minGain() condition above. + */ + assert(cSize < ZSTD_BLOCKSIZE_MAX); return cSize; } /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_useRowMatchFinderMode_e useRowMatchFinder, ZSTD_dictMode_e dictMode) +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_paramSwitch_e useRowMatchFinder, ZSTD_dictMode_e dictMode) { static const ZSTD_blockCompressor blockCompressor[4][ZSTD_STRATEGY_MAX+1] = { { ZSTD_compressBlock_fast /* default for 0 */, @@ -2758,7 +3052,7 @@ ZSTD_compressBlock_lazy2_dedicatedDictSearch_row } }; DEBUGLOG(4, "Selecting a row-based matchfinder"); - assert(useRowMatchFinder != ZSTD_urm_auto); + assert(useRowMatchFinder != ZSTD_ps_auto); selectedCompressor = rowBasedBlockCompressors[(int)dictMode][(int)strat - (int)ZSTD_greedy]; } else { selectedCompressor = blockCompressor[(int)dictMode][(int)strat]; @@ -2781,6 +3075,72 @@ ssPtr->longLengthType = ZSTD_llt_none; } +/* ZSTD_postProcessSequenceProducerResult() : + * Validates and post-processes sequences obtained through the external matchfinder API: + * - Checks whether nbExternalSeqs represents an error condition. + * - Appends a block delimiter to outSeqs if one is not already present. + * See zstd.h for context regarding block delimiters. + * Returns the number of sequences after post-processing, or an error code. */ +static size_t ZSTD_postProcessSequenceProducerResult( + ZSTD_Sequence* outSeqs, size_t nbExternalSeqs, size_t outSeqsCapacity, size_t srcSize +) { + RETURN_ERROR_IF( + nbExternalSeqs > outSeqsCapacity, + sequenceProducer_failed, + "External sequence producer returned error code %lu", + (unsigned long)nbExternalSeqs + ); + + RETURN_ERROR_IF( + nbExternalSeqs == 0 && srcSize > 0, + sequenceProducer_failed, + "Got zero sequences from external sequence producer for a non-empty src buffer!" + ); + + if (srcSize == 0) { + ZSTD_memset(&outSeqs[0], 0, sizeof(ZSTD_Sequence)); + return 1; + } + + { + ZSTD_Sequence const lastSeq = outSeqs[nbExternalSeqs - 1]; + + /* We can return early if lastSeq is already a block delimiter. */ + if (lastSeq.offset == 0 && lastSeq.matchLength == 0) { + return nbExternalSeqs; + } + + /* This error condition is only possible if the external matchfinder + * produced an invalid parse, by definition of ZSTD_sequenceBound(). */ + RETURN_ERROR_IF( + nbExternalSeqs == outSeqsCapacity, + sequenceProducer_failed, + "nbExternalSeqs == outSeqsCapacity but lastSeq is not a block delimiter!" + ); + + /* lastSeq is not a block delimiter, so we need to append one. */ + ZSTD_memset(&outSeqs[nbExternalSeqs], 0, sizeof(ZSTD_Sequence)); + return nbExternalSeqs + 1; + } +} + +/* ZSTD_fastSequenceLengthSum() : + * Returns sum(litLen) + sum(matchLen) + lastLits for *seqBuf*. + * Similar to another function in zstd_compress.c (determine_blockSize), + * except it doesn't check for a block delimiter to end summation. + * Removing the early exit allows the compiler to auto-vectorize (https://godbolt.org/z/cY1cajz9P). + * This function can be deleted and replaced by determine_blockSize after we resolve issue #3456. */ +static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seqBufSize) { + size_t matchLenSum, litLenSum, i; + matchLenSum = 0; + litLenSum = 0; + for (i = 0; i < seqBufSize; i++) { + litLenSum += seqBuf[i].litLength; + matchLenSum += seqBuf[i].matchLength; + } + return litLenSum + matchLenSum; +} + typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e; static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize) @@ -2790,7 +3150,9 @@ assert(srcSize <= ZSTD_BLOCKSIZE_MAX); /* Assert that we have correctly flushed the ctx params into the ms's copy */ ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams); - if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { + /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding + * additional 1. We need to revisit and change this logic to be more consistent */ + if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) { if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) { ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize); } else { @@ -2825,7 +3187,16 @@ zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; } if (zc->externSeqStore.pos < zc->externSeqStore.size) { - assert(!zc->appliedParams.ldmParams.enableLdm); + assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable); + + /* External matchfinder + LDM is technically possible, just not implemented yet. + * We need to revisit soon and implement it. */ + RETURN_ERROR_IF( + zc->appliedParams.useSequenceProducer, + parameter_combination_unsupported, + "Long-distance matching with external sequence producer enabled is not currently supported." + ); + /* Updates ldmSeqStore.pos */ lastLLSize = ZSTD_ldm_blockCompress(&zc->externSeqStore, @@ -2834,9 +3205,17 @@ zc->appliedParams.useRowMatchFinder, src, srcSize); assert(zc->externSeqStore.pos <= zc->externSeqStore.size); - } else if (zc->appliedParams.ldmParams.enableLdm) { + } else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) { rawSeqStore_t ldmSeqStore = kNullRawSeqStore; + /* External matchfinder + LDM is technically possible, just not implemented yet. + * We need to revisit soon and implement it. */ + RETURN_ERROR_IF( + zc->appliedParams.useSequenceProducer, + parameter_combination_unsupported, + "Long-distance matching with external sequence producer enabled is not currently supported." + ); + ldmSeqStore.seq = zc->ldmSequences; ldmSeqStore.capacity = zc->maxNbLdmSequences; /* Updates ldmSeqStore.size */ @@ -2851,7 +3230,68 @@ zc->appliedParams.useRowMatchFinder, src, srcSize); assert(ldmSeqStore.pos == ldmSeqStore.size); - } else { /* not long range mode */ + } else if (zc->appliedParams.useSequenceProducer) { + assert( + zc->externalMatchCtx.seqBufferCapacity >= ZSTD_sequenceBound(srcSize) + ); + assert(zc->externalMatchCtx.mFinder != NULL); + + { U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog; + + size_t const nbExternalSeqs = (zc->externalMatchCtx.mFinder)( + zc->externalMatchCtx.mState, + zc->externalMatchCtx.seqBuffer, + zc->externalMatchCtx.seqBufferCapacity, + src, srcSize, + NULL, 0, /* dict and dictSize, currently not supported */ + zc->appliedParams.compressionLevel, + windowSize + ); + + size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult( + zc->externalMatchCtx.seqBuffer, + nbExternalSeqs, + zc->externalMatchCtx.seqBufferCapacity, + srcSize + ); + + /* Return early if there is no error, since we don't need to worry about last literals */ + if (!ZSTD_isError(nbPostProcessedSeqs)) { + ZSTD_sequencePosition seqPos = {0,0,0}; + size_t const seqLenSum = ZSTD_fastSequenceLengthSum(zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs); + RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid, "External sequences imply too large a block!"); + FORWARD_IF_ERROR( + ZSTD_copySequencesToSeqStoreExplicitBlockDelim( + zc, &seqPos, + zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs, + src, srcSize, + zc->appliedParams.searchForExternalRepcodes + ), + "Failed to copy external sequences to seqStore!" + ); + ms->ldmSeqStore = NULL; + DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.", (unsigned long)nbExternalSeqs); + return ZSTDbss_compress; + } + + /* Propagate the error if fallback is disabled */ + if (!zc->appliedParams.enableMatchFinderFallback) { + return nbPostProcessedSeqs; + } + + /* Fallback to software matchfinder */ + { ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, + zc->appliedParams.useRowMatchFinder, + dictMode); + ms->ldmSeqStore = NULL; + DEBUGLOG( + 5, + "External sequence producer returned error code %lu. Falling back to internal parser.", + (unsigned long)nbExternalSeqs + ); + lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize); + } } + } else { /* not long range mode and no external matchfinder */ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, zc->appliedParams.useRowMatchFinder, dictMode); @@ -2882,9 +3322,9 @@ assert(zc->seqCollector.maxSequences >= seqStoreSeqSize + 1); ZSTD_memcpy(updatedRepcodes.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t)); for (i = 0; i < seqStoreSeqSize; ++i) { - U32 rawOffset = seqStoreSeqs[i].offset - ZSTD_REP_NUM; + U32 rawOffset = seqStoreSeqs[i].offBase - ZSTD_REP_NUM; outSeqs[i].litLength = seqStoreSeqs[i].litLength; - outSeqs[i].matchLength = seqStoreSeqs[i].matchLength + MINMATCH; + outSeqs[i].matchLength = seqStoreSeqs[i].mlBase + MINMATCH; outSeqs[i].rep = 0; if (i == seqStore->longLengthPos) { @@ -2895,9 +3335,9 @@ } } - if (seqStoreSeqs[i].offset <= ZSTD_REP_NUM) { + if (seqStoreSeqs[i].offBase <= ZSTD_REP_NUM) { /* Derive the correct offset corresponding to a repcode */ - outSeqs[i].rep = seqStoreSeqs[i].offset; + outSeqs[i].rep = seqStoreSeqs[i].offBase; if (outSeqs[i].litLength != 0) { rawOffset = updatedRepcodes.rep[outSeqs[i].rep - 1]; } else { @@ -2911,9 +3351,9 @@ outSeqs[i].offset = rawOffset; /* seqStoreSeqs[i].offset == offCode+1, and ZSTD_updateRep() expects offCode so we provide seqStoreSeqs[i].offset - 1 */ - updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, - seqStoreSeqs[i].offset - 1, - seqStoreSeqs[i].litLength == 0); + ZSTD_updateRep(updatedRepcodes.rep, + seqStoreSeqs[i].offBase, + seqStoreSeqs[i].litLength == 0); literalsRead += outSeqs[i].litLength; } /* Insert last literals (if any exist) in the block as a sequence with ml == off == 0. @@ -2928,6 +3368,10 @@ zc->seqCollector.seqIndex += seqStoreSeqSize; } +size_t ZSTD_sequenceBound(size_t srcSize) { + return (srcSize / ZSTD_MINMATCH_MIN) + 1; +} + size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs, size_t outSeqsSize, const void* src, size_t srcSize) { @@ -2973,19 +3417,17 @@ const size_t unrollMask = unrollSize - 1; const size_t prefixLength = length & unrollMask; size_t i; - size_t u; if (length == 1) return 1; /* Check if prefix is RLE first before using unrolled loop */ if (prefixLength && ZSTD_count(ip+1, ip, ip+prefixLength) != prefixLength-1) { return 0; } for (i = prefixLength; i != length; i += unrollSize) { + size_t u; for (u = 0; u < unrollSize; u += sizeof(size_t)) { if (MEM_readST(ip + i + u) != valueST) { return 0; - } - } - } + } } } return 1; } @@ -3001,7 +3443,8 @@ return nbSeqs < 4 && nbLits < 10; } -static void ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs) +static void +ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs) { ZSTD_compressedBlockState_t* const tmp = bs->prevCBlock; bs->prevCBlock = bs->nextCBlock; @@ -3009,7 +3452,9 @@ } /* Writes the block header */ -static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock) { +static void +writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock) +{ U32 const cBlockHeader = cSize == 1 ? lastBlock + (((U32)bt_rle)<<1) + (U32)(blockSize << 3) : lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); @@ -3022,13 +3467,16 @@ * Stores literals block type (raw, rle, compressed, repeat) and * huffman description table to hufMetadata. * Requires ENTROPY_WORKSPACE_SIZE workspace - * @return : size of huffman description table or error code */ -static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize, - const ZSTD_hufCTables_t* prevHuf, - ZSTD_hufCTables_t* nextHuf, - ZSTD_hufCTablesMetadata_t* hufMetadata, - const int disableLiteralsCompression, - void* workspace, size_t wkspSize) + * @return : size of huffman description table, or an error code + */ +static size_t +ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize, + const ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_hufCTablesMetadata_t* hufMetadata, + const int literalsCompressionIsDisabled, + void* workspace, size_t wkspSize, + int hufFlags) { BYTE* const wkspStart = (BYTE*)workspace; BYTE* const wkspEnd = wkspStart + wkspSize; @@ -3036,16 +3484,16 @@ unsigned* const countWksp = (unsigned*)workspace; const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned); BYTE* const nodeWksp = countWkspStart + countWkspSize; - const size_t nodeWkspSize = wkspEnd-nodeWksp; + const size_t nodeWkspSize = (size_t)(wkspEnd - nodeWksp); unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX; - unsigned huffLog = HUF_TABLELOG_DEFAULT; + unsigned huffLog = LitHufLog; HUF_repeat repeat = prevHuf->repeatMode; DEBUGLOG(5, "ZSTD_buildBlockEntropyStats_literals (srcSize=%zu)", srcSize); /* Prepare nextEntropy assuming reusing the existing table */ ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - if (disableLiteralsCompression) { + if (literalsCompressionIsDisabled) { DEBUGLOG(5, "set_basic - disabled"); hufMetadata->hType = set_basic; return 0; @@ -3053,73 +3501,77 @@ /* small ? don't even attempt compression (speed opt) */ #ifndef COMPRESS_LITERALS_SIZE_MIN -#define COMPRESS_LITERALS_SIZE_MIN 63 +# define COMPRESS_LITERALS_SIZE_MIN 63 /* heuristic */ #endif { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; if (srcSize <= minLitSize) { DEBUGLOG(5, "set_basic - too small"); hufMetadata->hType = set_basic; return 0; - } - } + } } /* Scan input and build symbol stats */ - { size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)src, srcSize, workspace, wkspSize); + { size_t const largest = + HIST_count_wksp (countWksp, &maxSymbolValue, + (const BYTE*)src, srcSize, + workspace, wkspSize); FORWARD_IF_ERROR(largest, "HIST_count_wksp failed"); if (largest == srcSize) { + /* only one literal symbol */ DEBUGLOG(5, "set_rle"); hufMetadata->hType = set_rle; return 0; } if (largest <= (srcSize >> 7)+4) { + /* heuristic: likely not compressible */ DEBUGLOG(5, "set_basic - no gain"); hufMetadata->hType = set_basic; return 0; - } - } + } } /* Validate the previous Huffman table */ - if (repeat == HUF_repeat_check && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) { + if (repeat == HUF_repeat_check + && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) { repeat = HUF_repeat_none; } /* Build Huffman Tree */ ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable)); - huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); + huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue, nodeWksp, nodeWkspSize, nextHuf->CTable, countWksp, hufFlags); + assert(huffLog <= LitHufLog); { size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue, huffLog, nodeWksp, nodeWkspSize); FORWARD_IF_ERROR(maxBits, "HUF_buildCTable_wksp"); huffLog = (U32)maxBits; - { /* Build and write the CTable */ - size_t const newCSize = HUF_estimateCompressedSize( - (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue); - size_t const hSize = HUF_writeCTable_wksp( - hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer), - (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog, - nodeWksp, nodeWkspSize); - /* Check against repeating the previous CTable */ - if (repeat != HUF_repeat_none) { - size_t const oldCSize = HUF_estimateCompressedSize( - (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue); - if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) { - DEBUGLOG(5, "set_repeat - smaller"); - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - hufMetadata->hType = set_repeat; - return 0; - } - } - if (newCSize + hSize >= srcSize) { - DEBUGLOG(5, "set_basic - no gains"); + } + { /* Build and write the CTable */ + size_t const newCSize = HUF_estimateCompressedSize( + (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue); + size_t const hSize = HUF_writeCTable_wksp( + hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer), + (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog, + nodeWksp, nodeWkspSize); + /* Check against repeating the previous CTable */ + if (repeat != HUF_repeat_none) { + size_t const oldCSize = HUF_estimateCompressedSize( + (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue); + if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) { + DEBUGLOG(5, "set_repeat - smaller"); ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - hufMetadata->hType = set_basic; + hufMetadata->hType = set_repeat; return 0; - } - DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize); - hufMetadata->hType = set_compressed; - nextHuf->repeatMode = HUF_repeat_check; - return hSize; + } } + if (newCSize + hSize >= srcSize) { + DEBUGLOG(5, "set_basic - no gains"); + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + hufMetadata->hType = set_basic; + return 0; } + DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize); + hufMetadata->hType = set_compressed; + nextHuf->repeatMode = HUF_repeat_check; + return hSize; } } @@ -3129,8 +3581,9 @@ * and updates nextEntropy to the appropriate repeatMode. */ static ZSTD_symbolEncodingTypeStats_t -ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) { - ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0}; +ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) +{ + ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0, 0}; nextEntropy->litlength_repeatMode = FSE_repeat_none; nextEntropy->offcode_repeatMode = FSE_repeat_none; nextEntropy->matchlength_repeatMode = FSE_repeat_none; @@ -3141,16 +3594,18 @@ * Builds entropy for the sequences. * Stores symbol compression modes and fse table to fseMetadata. * Requires ENTROPY_WORKSPACE_SIZE wksp. - * @return : size of fse tables or error code */ -static size_t ZSTD_buildBlockEntropyStats_sequences(seqStore_t* seqStorePtr, - const ZSTD_fseCTables_t* prevEntropy, - ZSTD_fseCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - ZSTD_fseCTablesMetadata_t* fseMetadata, - void* workspace, size_t wkspSize) + * @return : size of fse tables or error code */ +static size_t +ZSTD_buildBlockEntropyStats_sequences( + const seqStore_t* seqStorePtr, + const ZSTD_fseCTables_t* prevEntropy, + ZSTD_fseCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, size_t wkspSize) { ZSTD_strategy const strategy = cctxParams->cParams.strategy; - size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; + size_t const nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart); BYTE* const ostart = fseMetadata->fseTablesBuffer; BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer); BYTE* op = ostart; @@ -3177,23 +3632,28 @@ /** ZSTD_buildBlockEntropyStats() : * Builds entropy for the block. * Requires workspace size ENTROPY_WORKSPACE_SIZE - * - * @return : 0 on success or error code + * @return : 0 on success, or an error code + * Note : also employed in superblock */ -size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - ZSTD_entropyCTablesMetadata_t* entropyMetadata, - void* workspace, size_t wkspSize) +size_t ZSTD_buildBlockEntropyStats( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, size_t wkspSize) { - size_t const litSize = seqStorePtr->lit - seqStorePtr->litStart; + size_t const litSize = (size_t)(seqStorePtr->lit - seqStorePtr->litStart); + int const huf_useOptDepth = (cctxParams->cParams.strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD); + int const hufFlags = huf_useOptDepth ? HUF_flags_optimalDepth : 0; + entropyMetadata->hufMetadata.hufDesSize = ZSTD_buildBlockEntropyStats_literals(seqStorePtr->litStart, litSize, &prevEntropy->huf, &nextEntropy->huf, &entropyMetadata->hufMetadata, - ZSTD_disableLiteralsCompression(cctxParams), - workspace, wkspSize); + ZSTD_literalsCompressionIsDisabled(cctxParams), + workspace, wkspSize, hufFlags); + FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildBlockEntropyStats_literals failed"); entropyMetadata->fseMetadata.fseTablesSize = ZSTD_buildBlockEntropyStats_sequences(seqStorePtr, @@ -3206,11 +3666,12 @@ } /* Returns the size estimate for the literals section (header + content) of a block */ -static size_t ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize, - const ZSTD_hufCTables_t* huf, - const ZSTD_hufCTablesMetadata_t* hufMetadata, - void* workspace, size_t wkspSize, - int writeEntropy) +static size_t +ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize, + const ZSTD_hufCTables_t* huf, + const ZSTD_hufCTablesMetadata_t* hufMetadata, + void* workspace, size_t wkspSize, + int writeEntropy) { unsigned* const countWksp = (unsigned*)workspace; unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX; @@ -3232,12 +3693,13 @@ } /* Returns the size estimate for the FSE-compressed symbols (of, ml, ll) of a block */ -static size_t ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type, - const BYTE* codeTable, size_t nbSeq, unsigned maxCode, - const FSE_CTable* fseCTable, - const U32* additionalBits, - short const* defaultNorm, U32 defaultNormLog, U32 defaultMax, - void* workspace, size_t wkspSize) +static size_t +ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type, + const BYTE* codeTable, size_t nbSeq, unsigned maxCode, + const FSE_CTable* fseCTable, + const U8* additionalBits, + short const* defaultNorm, U32 defaultNormLog, U32 defaultMax, + void* workspace, size_t wkspSize) { unsigned* const countWksp = (unsigned*)workspace; const BYTE* ctp = codeTable; @@ -3269,98 +3731,107 @@ } /* Returns the size estimate for the sequences section (header + content) of a block */ -static size_t ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable, - const BYTE* llCodeTable, - const BYTE* mlCodeTable, - size_t nbSeq, - const ZSTD_fseCTables_t* fseTables, - const ZSTD_fseCTablesMetadata_t* fseMetadata, - void* workspace, size_t wkspSize, - int writeEntropy) +static size_t +ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable, + const BYTE* llCodeTable, + const BYTE* mlCodeTable, + size_t nbSeq, + const ZSTD_fseCTables_t* fseTables, + const ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, size_t wkspSize, + int writeEntropy) { size_t sequencesSectionHeaderSize = 1 /* seqHead */ + 1 /* min seqSize size */ + (nbSeq >= 128) + (nbSeq >= LONGNBSEQ); size_t cSeqSizeEstimate = 0; cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, nbSeq, MaxOff, - fseTables->offcodeCTable, NULL, - OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, - workspace, wkspSize); + fseTables->offcodeCTable, NULL, + OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, + workspace, wkspSize); cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->llType, llCodeTable, nbSeq, MaxLL, - fseTables->litlengthCTable, LL_bits, - LL_defaultNorm, LL_defaultNormLog, MaxLL, - workspace, wkspSize); + fseTables->litlengthCTable, LL_bits, + LL_defaultNorm, LL_defaultNormLog, MaxLL, + workspace, wkspSize); cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, nbSeq, MaxML, - fseTables->matchlengthCTable, ML_bits, - ML_defaultNorm, ML_defaultNormLog, MaxML, - workspace, wkspSize); + fseTables->matchlengthCTable, ML_bits, + ML_defaultNorm, ML_defaultNormLog, MaxML, + workspace, wkspSize); if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize; return cSeqSizeEstimate + sequencesSectionHeaderSize; } /* Returns the size estimate for a given stream of literals, of, ll, ml */ -static size_t ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize, - const BYTE* ofCodeTable, - const BYTE* llCodeTable, - const BYTE* mlCodeTable, - size_t nbSeq, - const ZSTD_entropyCTables_t* entropy, - const ZSTD_entropyCTablesMetadata_t* entropyMetadata, - void* workspace, size_t wkspSize, - int writeLitEntropy, int writeSeqEntropy) { +static size_t +ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize, + const BYTE* ofCodeTable, + const BYTE* llCodeTable, + const BYTE* mlCodeTable, + size_t nbSeq, + const ZSTD_entropyCTables_t* entropy, + const ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, size_t wkspSize, + int writeLitEntropy, int writeSeqEntropy) +{ size_t const literalsSize = ZSTD_estimateBlockSize_literal(literals, litSize, - &entropy->huf, &entropyMetadata->hufMetadata, - workspace, wkspSize, writeLitEntropy); + &entropy->huf, &entropyMetadata->hufMetadata, + workspace, wkspSize, writeLitEntropy); size_t const seqSize = ZSTD_estimateBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable, - nbSeq, &entropy->fse, &entropyMetadata->fseMetadata, - workspace, wkspSize, writeSeqEntropy); + nbSeq, &entropy->fse, &entropyMetadata->fseMetadata, + workspace, wkspSize, writeSeqEntropy); return seqSize + literalsSize + ZSTD_blockHeaderSize; } /* Builds entropy statistics and uses them for blocksize estimation. * - * Returns the estimated compressed size of the seqStore, or a zstd error. + * @return: estimated compressed size of the seqStore, or a zstd error. */ -static size_t ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, const ZSTD_CCtx* zc) { - ZSTD_entropyCTablesMetadata_t entropyMetadata; +static size_t +ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, ZSTD_CCtx* zc) +{ + ZSTD_entropyCTablesMetadata_t* const entropyMetadata = &zc->blockSplitCtx.entropyMetadata; + DEBUGLOG(6, "ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize()"); FORWARD_IF_ERROR(ZSTD_buildBlockEntropyStats(seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams, - &entropyMetadata, - zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */), ""); - return ZSTD_estimateBlockSize(seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart), + entropyMetadata, + zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE), ""); + return ZSTD_estimateBlockSize( + seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart), seqStore->ofCode, seqStore->llCode, seqStore->mlCode, (size_t)(seqStore->sequences - seqStore->sequencesStart), - &zc->blockState.nextCBlock->entropy, &entropyMetadata, zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE, - (int)(entropyMetadata.hufMetadata.hType == set_compressed), 1); + &zc->blockState.nextCBlock->entropy, + entropyMetadata, + zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE, + (int)(entropyMetadata->hufMetadata.hType == set_compressed), 1); } /* Returns literals bytes represented in a seqStore */ -static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore) { +static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore) +{ size_t literalsBytes = 0; - size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart; + size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart); size_t i; for (i = 0; i < nbSeqs; ++i) { - seqDef seq = seqStore->sequencesStart[i]; + seqDef const seq = seqStore->sequencesStart[i]; literalsBytes += seq.litLength; if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_literalLength) { literalsBytes += 0x10000; - } - } + } } return literalsBytes; } /* Returns match bytes represented in a seqStore */ -static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) { +static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) +{ size_t matchBytes = 0; - size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart; + size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart); size_t i; for (i = 0; i < nbSeqs; ++i) { seqDef seq = seqStore->sequencesStart[i]; - matchBytes += seq.matchLength + MINMATCH; + matchBytes += seq.mlBase + MINMATCH; if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_matchLength) { matchBytes += 0x10000; - } - } + } } return matchBytes; } @@ -3369,15 +3840,12 @@ */ static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore, const seqStore_t* originalSeqStore, - size_t startIdx, size_t endIdx) { - BYTE* const litEnd = originalSeqStore->lit; - size_t literalsBytes; - size_t literalsBytesPreceding = 0; - + size_t startIdx, size_t endIdx) +{ *resultSeqStore = *originalSeqStore; if (startIdx > 0) { resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx; - literalsBytesPreceding = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); + resultSeqStore->litStart += ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); } /* Move longLengthPos into the correct position if necessary */ @@ -3390,13 +3858,12 @@ } resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx; resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx; - literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); - resultSeqStore->litStart += literalsBytesPreceding; if (endIdx == (size_t)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) { /* This accounts for possible last literals if the derived chunk reaches the end of the block */ - resultSeqStore->lit = litEnd; + assert(resultSeqStore->lit == originalSeqStore->lit); } else { - resultSeqStore->lit = resultSeqStore->litStart+literalsBytes; + size_t const literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); + resultSeqStore->lit = resultSeqStore->litStart + literalsBytes; } resultSeqStore->llCode += startIdx; resultSeqStore->mlCode += startIdx; @@ -3404,52 +3871,68 @@ } /** - * Returns the raw offset represented by the combination of offCode, ll0, and repcode history. - * offCode must be an offCode representing a repcode, therefore in the range of [0, 2]. + * Returns the raw offset represented by the combination of offBase, ll0, and repcode history. + * offBase must represent a repcode in the numeric representation of ZSTD_storeSeq(). */ -static U32 ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offCode, const U32 ll0) { - U32 const adjustedOffCode = offCode + ll0; - assert(offCode < ZSTD_REP_NUM); - if (adjustedOffCode == ZSTD_REP_NUM) { - /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 */ - assert(rep[0] > 0); +static U32 +ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, const U32 ll0) +{ + U32 const adjustedRepCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0; /* [ 0 - 3 ] */ + assert(OFFBASE_IS_REPCODE(offBase)); + if (adjustedRepCode == ZSTD_REP_NUM) { + assert(ll0); + /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 + * This is only valid if it results in a valid offset value, aka > 0. + * Note : it may happen that `rep[0]==1` in exceptional circumstances. + * In which case this function will return 0, which is an invalid offset. + * It's not an issue though, since this value will be + * compared and discarded within ZSTD_seqStore_resolveOffCodes(). + */ return rep[0] - 1; } - return rep[adjustedOffCode]; + return rep[adjustedRepCode]; } /** * ZSTD_seqStore_resolveOffCodes() reconciles any possible divergences in offset history that may arise - * due to emission of RLE/raw blocks that disturb the offset history, and replaces any repcodes within - * the seqStore that may be invalid. + * due to emission of RLE/raw blocks that disturb the offset history, + * and replaces any repcodes within the seqStore that may be invalid. * - * dRepcodes are updated as would be on the decompression side. cRepcodes are updated exactly in - * accordance with the seqStore. + * dRepcodes are updated as would be on the decompression side. + * cRepcodes are updated exactly in accordance with the seqStore. + * + * Note : this function assumes seq->offBase respects the following numbering scheme : + * 0 : invalid + * 1-3 : repcode 1-3 + * 4+ : real_offset+3 */ -static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes, - seqStore_t* const seqStore, U32 const nbSeq) { +static void +ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes, + const seqStore_t* const seqStore, U32 const nbSeq) +{ U32 idx = 0; + U32 const longLitLenIdx = seqStore->longLengthType == ZSTD_llt_literalLength ? seqStore->longLengthPos : nbSeq; for (; idx < nbSeq; ++idx) { seqDef* const seq = seqStore->sequencesStart + idx; - U32 const ll0 = (seq->litLength == 0); - U32 offCode = seq->offset - 1; - assert(seq->offset > 0); - if (offCode <= ZSTD_REP_MOVE) { - U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offCode, ll0); - U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offCode, ll0); + U32 const ll0 = (seq->litLength == 0) && (idx != longLitLenIdx); + U32 const offBase = seq->offBase; + assert(offBase > 0); + if (OFFBASE_IS_REPCODE(offBase)) { + U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offBase, ll0); + U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offBase, ll0); /* Adjust simulated decompression repcode history if we come across a mismatch. Replace * the repcode with the offset it actually references, determined by the compression * repcode history. */ if (dRawOffset != cRawOffset) { - seq->offset = cRawOffset + ZSTD_REP_NUM; + seq->offBase = OFFSET_TO_OFFBASE(cRawOffset); } } /* Compression repcode history is always updated with values directly from the unmodified seqStore. * Decompression repcode history may use modified seq->offset value taken from compression repcode history. */ - *dRepcodes = ZSTD_updateRep(dRepcodes->rep, seq->offset - 1, ll0); - *cRepcodes = ZSTD_updateRep(cRepcodes->rep, offCode, ll0); + ZSTD_updateRep(dRepcodes->rep, seq->offBase, ll0); + ZSTD_updateRep(cRepcodes->rep, offBase, ll0); } } @@ -3458,11 +3941,14 @@ * * Returns the total size of that block (including header) or a ZSTD error code. */ -static size_t ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc, seqStore_t* const seqStore, - repcodes_t* const dRep, repcodes_t* const cRep, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - U32 lastBlock, U32 isPartition) { +static size_t +ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc, + const seqStore_t* const seqStore, + repcodes_t* const dRep, repcodes_t* const cRep, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + U32 lastBlock, U32 isPartition) +{ const U32 rleMaxLength = 25; BYTE* op = (BYTE*)dst; const BYTE* ip = (const BYTE*)src; @@ -3471,9 +3957,11 @@ /* In case of an RLE or raw block, the simulated decompression repcode history must be reset */ repcodes_t const dRepOriginal = *dRep; + DEBUGLOG(5, "ZSTD_compressSeqStore_singleBlock"); if (isPartition) ZSTD_seqStore_resolveOffCodes(dRep, cRep, seqStore, (U32)(seqStore->sequences - seqStore->sequencesStart)); + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall, "Block header doesn't fit"); cSeqsSize = ZSTD_entropyCompressSeqStore(seqStore, &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy, &zc->appliedParams, @@ -3499,9 +3987,6 @@ return 0; } - if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid) - zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check; - if (cSeqsSize == 0) { cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock); FORWARD_IF_ERROR(cSize, "Nocompress block failed"); @@ -3518,6 +4003,10 @@ cSize = ZSTD_blockHeaderSize + cSeqsSize; DEBUGLOG(4, "Writing out compressed block, size: %zu", cSize); } + + if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid) + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check; + return cSize; } @@ -3528,45 +4017,52 @@ } seqStoreSplits; #define MIN_SEQUENCES_BLOCK_SPLITTING 300 -#define MAX_NB_SPLITS 196 /* Helper function to perform the recursive search for block splits. * Estimates the cost of seqStore prior to split, and estimates the cost of splitting the sequences in half. - * If advantageous to split, then we recurse down the two sub-blocks. If not, or if an error occurred in estimation, then - * we do not recurse. + * If advantageous to split, then we recurse down the two sub-blocks. + * If not, or if an error occurred in estimation, then we do not recurse. * - * Note: The recursion depth is capped by a heuristic minimum number of sequences, defined by MIN_SEQUENCES_BLOCK_SPLITTING. + * Note: The recursion depth is capped by a heuristic minimum number of sequences, + * defined by MIN_SEQUENCES_BLOCK_SPLITTING. * In theory, this means the absolute largest recursion depth is 10 == log2(maxNbSeqInBlock/MIN_SEQUENCES_BLOCK_SPLITTING). * In practice, recursion depth usually doesn't go beyond 4. * - * Furthermore, the number of splits is capped by MAX_NB_SPLITS. At MAX_NB_SPLITS == 196 with the current existing blockSize + * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS. + * At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize * maximum of 128 KB, this value is actually impossible to reach. */ -static void ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t endIdx, - const ZSTD_CCtx* zc, const seqStore_t* origSeqStore) { - seqStore_t fullSeqStoreChunk; - seqStore_t firstHalfSeqStore; - seqStore_t secondHalfSeqStore; +static void +ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t endIdx, + ZSTD_CCtx* zc, const seqStore_t* origSeqStore) +{ + seqStore_t* const fullSeqStoreChunk = &zc->blockSplitCtx.fullSeqStoreChunk; + seqStore_t* const firstHalfSeqStore = &zc->blockSplitCtx.firstHalfSeqStore; + seqStore_t* const secondHalfSeqStore = &zc->blockSplitCtx.secondHalfSeqStore; size_t estimatedOriginalSize; size_t estimatedFirstHalfSize; size_t estimatedSecondHalfSize; size_t midIdx = (startIdx + endIdx)/2; - if (endIdx - startIdx < MIN_SEQUENCES_BLOCK_SPLITTING || splits->idx >= MAX_NB_SPLITS) { + DEBUGLOG(5, "ZSTD_deriveBlockSplitsHelper: startIdx=%zu endIdx=%zu", startIdx, endIdx); + assert(endIdx >= startIdx); + if (endIdx - startIdx < MIN_SEQUENCES_BLOCK_SPLITTING || splits->idx >= ZSTD_MAX_NB_BLOCK_SPLITS) { + DEBUGLOG(6, "ZSTD_deriveBlockSplitsHelper: Too few sequences (%zu)", endIdx - startIdx); return; } - ZSTD_deriveSeqStoreChunk(&fullSeqStoreChunk, origSeqStore, startIdx, endIdx); - ZSTD_deriveSeqStoreChunk(&firstHalfSeqStore, origSeqStore, startIdx, midIdx); - ZSTD_deriveSeqStoreChunk(&secondHalfSeqStore, origSeqStore, midIdx, endIdx); - estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&fullSeqStoreChunk, zc); - estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&firstHalfSeqStore, zc); - estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&secondHalfSeqStore, zc); + ZSTD_deriveSeqStoreChunk(fullSeqStoreChunk, origSeqStore, startIdx, endIdx); + ZSTD_deriveSeqStoreChunk(firstHalfSeqStore, origSeqStore, startIdx, midIdx); + ZSTD_deriveSeqStoreChunk(secondHalfSeqStore, origSeqStore, midIdx, endIdx); + estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(fullSeqStoreChunk, zc); + estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(firstHalfSeqStore, zc); + estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(secondHalfSeqStore, zc); DEBUGLOG(5, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu", estimatedOriginalSize, estimatedFirstHalfSize, estimatedSecondHalfSize); if (ZSTD_isError(estimatedOriginalSize) || ZSTD_isError(estimatedFirstHalfSize) || ZSTD_isError(estimatedSecondHalfSize)) { return; } if (estimatedFirstHalfSize + estimatedSecondHalfSize < estimatedOriginalSize) { + DEBUGLOG(5, "split decided at seqNb:%zu", midIdx); ZSTD_deriveBlockSplitsHelper(splits, startIdx, midIdx, zc, origSeqStore); splits->splitLocations[splits->idx] = (U32)midIdx; splits->idx++; @@ -3574,14 +4070,18 @@ } } -/* Base recursive function. Populates a table with intra-block partition indices that can improve compression ratio. +/* Base recursive function. + * Populates a table with intra-block partition indices that can improve compression ratio. * - * Returns the number of splits made (which equals the size of the partition table - 1). + * @return: number of splits made (which equals the size of the partition table - 1). */ -static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) { - seqStoreSplits splits = {partitions, 0}; +static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) +{ + seqStoreSplits splits; + splits.splitLocations = partitions; + splits.idx = 0; if (nbSeq <= 4) { - DEBUGLOG(4, "ZSTD_deriveBlockSplits: Too few sequences to split"); + DEBUGLOG(5, "ZSTD_deriveBlockSplits: Too few sequences to split (%u <= 4)", nbSeq); /* Refuse to try and split anything with less than 4 sequences */ return 0; } @@ -3596,17 +4096,21 @@ * * Returns combined size of all blocks (which includes headers), or a ZSTD error code. */ -static size_t ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, - const void* src, size_t blockSize, U32 lastBlock, U32 nbSeq) { +static size_t +ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, + void* dst, size_t dstCapacity, + const void* src, size_t blockSize, + U32 lastBlock, U32 nbSeq) +{ size_t cSize = 0; const BYTE* ip = (const BYTE*)src; BYTE* op = (BYTE*)dst; - U32 partitions[MAX_NB_SPLITS]; size_t i = 0; size_t srcBytesTotal = 0; - size_t numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq); - seqStore_t nextSeqStore; - seqStore_t currSeqStore; + U32* const partitions = zc->blockSplitCtx.partitions; /* size == ZSTD_MAX_NB_BLOCK_SPLITS */ + seqStore_t* const nextSeqStore = &zc->blockSplitCtx.nextSeqStore; + seqStore_t* const currSeqStore = &zc->blockSplitCtx.currSeqStore; + size_t const numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq); /* If a block is split and some partitions are emitted as RLE/uncompressed, then repcode history * may become invalid. In order to reconcile potentially invalid repcodes, we keep track of two @@ -3626,77 +4130,81 @@ repcodes_t cRep; ZSTD_memcpy(dRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t)); ZSTD_memcpy(cRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t)); + ZSTD_memset(nextSeqStore, 0, sizeof(seqStore_t)); - DEBUGLOG(4, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", + DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)", (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate); if (numSplits == 0) { - size_t cSizeSingleBlock = ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore, - &dRep, &cRep, - op, dstCapacity, - ip, blockSize, - lastBlock, 0 /* isPartition */); + size_t cSizeSingleBlock = + ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore, + &dRep, &cRep, + op, dstCapacity, + ip, blockSize, + lastBlock, 0 /* isPartition */); FORWARD_IF_ERROR(cSizeSingleBlock, "Compressing single block from splitBlock_internal() failed!"); DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal: No splits"); - assert(cSizeSingleBlock <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize); + assert(zc->blockSize <= ZSTD_BLOCKSIZE_MAX); + assert(cSizeSingleBlock <= zc->blockSize + ZSTD_blockHeaderSize); return cSizeSingleBlock; } - ZSTD_deriveSeqStoreChunk(&currSeqStore, &zc->seqStore, 0, partitions[0]); + ZSTD_deriveSeqStoreChunk(currSeqStore, &zc->seqStore, 0, partitions[0]); for (i = 0; i <= numSplits; ++i) { - size_t srcBytes; size_t cSizeChunk; U32 const lastPartition = (i == numSplits); U32 lastBlockEntireSrc = 0; - srcBytes = ZSTD_countSeqStoreLiteralsBytes(&currSeqStore) + ZSTD_countSeqStoreMatchBytes(&currSeqStore); + size_t srcBytes = ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + ZSTD_countSeqStoreMatchBytes(currSeqStore); srcBytesTotal += srcBytes; if (lastPartition) { /* This is the final partition, need to account for possible last literals */ srcBytes += blockSize - srcBytesTotal; lastBlockEntireSrc = lastBlock; } else { - ZSTD_deriveSeqStoreChunk(&nextSeqStore, &zc->seqStore, partitions[i], partitions[i+1]); + ZSTD_deriveSeqStoreChunk(nextSeqStore, &zc->seqStore, partitions[i], partitions[i+1]); } - cSizeChunk = ZSTD_compressSeqStore_singleBlock(zc, &currSeqStore, + cSizeChunk = ZSTD_compressSeqStore_singleBlock(zc, currSeqStore, &dRep, &cRep, op, dstCapacity, ip, srcBytes, lastBlockEntireSrc, 1 /* isPartition */); - DEBUGLOG(5, "Estimated size: %zu actual size: %zu", ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&currSeqStore, zc), cSizeChunk); + DEBUGLOG(5, "Estimated size: %zu vs %zu : actual size", + ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(currSeqStore, zc), cSizeChunk); FORWARD_IF_ERROR(cSizeChunk, "Compressing chunk failed!"); ip += srcBytes; op += cSizeChunk; dstCapacity -= cSizeChunk; cSize += cSizeChunk; - currSeqStore = nextSeqStore; - assert(cSizeChunk <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize); + *currSeqStore = *nextSeqStore; + assert(cSizeChunk <= zc->blockSize + ZSTD_blockHeaderSize); } - /* cRep and dRep may have diverged during the compression. If so, we use the dRep repcodes - * for the next block. + /* cRep and dRep may have diverged during the compression. + * If so, we use the dRep repcodes for the next block. */ ZSTD_memcpy(zc->blockState.prevCBlock->rep, dRep.rep, sizeof(repcodes_t)); return cSize; } -static size_t ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, U32 lastBlock) { - const BYTE* ip = (const BYTE*)src; - BYTE* op = (BYTE*)dst; +static size_t +ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, U32 lastBlock) +{ U32 nbSeq; size_t cSize; DEBUGLOG(4, "ZSTD_compressBlock_splitBlock"); + assert(zc->appliedParams.useBlockSplitter == ZSTD_ps_enable); { const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize); FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed"); if (bss == ZSTDbss_noCompress) { if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid) zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check; - cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock); + cSize = ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock); FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed"); DEBUGLOG(4, "ZSTD_compressBlock_splitBlock: Nocompress block"); return cSize; @@ -3704,19 +4212,19 @@ nbSeq = (U32)(zc->seqStore.sequences - zc->seqStore.sequencesStart); } - assert(zc->appliedParams.splitBlocks == 1); cSize = ZSTD_compressBlock_splitBlock_internal(zc, dst, dstCapacity, src, srcSize, lastBlock, nbSeq); FORWARD_IF_ERROR(cSize, "Splitting blocks failed!"); return cSize; } -static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, U32 frame) +static size_t +ZSTD_compressBlock_internal(ZSTD_CCtx* zc, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, U32 frame) { - /* This the upper bound for the length of an rle block. - * This isn't the actual upper bound. Finding the real threshold - * needs further investigation. + /* This is an estimated upper bound for the length of an rle block. + * This isn't the actual upper bound. + * Finding the real threshold needs further investigation. */ const U32 rleMaxLength = 25; size_t cSize; @@ -3746,12 +4254,6 @@ zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */, zc->bmi2); - if (zc->seqCollector.collectSequences) { - ZSTD_copyBlockSequences(zc); - return 0; - } - - if (frame && /* We don't want to emit our first block as a RLE even if it qualifies because * doing so will cause the decoder (cli only) to throw a "should consume all input error." @@ -3814,10 +4316,11 @@ * * cSize >= blockBound(srcSize): We have expanded the block too much so * emit an uncompressed block. */ - { - size_t const cSize = ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock); + { size_t const cSize = + ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock); if (cSize != ERROR(dstSize_tooSmall)) { - size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy); + size_t const maxCSize = + srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy); FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed"); if (cSize != 0 && cSize < maxCSize + ZSTD_blockHeaderSize) { ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); @@ -3825,7 +4328,7 @@ } } } - } + } /* if (bss == ZSTDbss_compress)*/ DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()"); /* Superblock compression failed, attempt to emit a single no compress block. @@ -3883,7 +4386,7 @@ * All blocks will be terminated, all input will be consumed. * Function will issue an error if there is not enough `dstCapacity` to hold the compressed content. * Frame is supposed already started (header already produced) -* @return : compressed size, or an error code +* @return : compressed size, or an error code */ static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, @@ -3907,7 +4410,9 @@ ZSTD_matchState_t* const ms = &cctx->blockState.matchState; U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); - RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE, + /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding + * additional 1. We need to revisit and change this logic to be more consistent */ + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE + 1, dstSize_tooSmall, "not enough space to store compressed block"); if (remaining < blockSize) blockSize = remaining; @@ -3915,6 +4420,7 @@ ZSTD_overflowCorrectIfNeeded( ms, &cctx->workspace, &cctx->appliedParams, ip, ip + blockSize); ZSTD_checkDictValidity(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState); + ZSTD_window_enforceMaxDist(&ms->window, ip, maxDist, &ms->loadedDictEnd, &ms->dictMatchState); /* Ensure hash/chain table insertion resumes no sooner than lowlimit */ if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit; @@ -3945,7 +4451,7 @@ MEM_writeLE24(op, cBlockHeader); cSize += ZSTD_blockHeaderSize; } - } + } /* if (ZSTD_useTargetCBlockSize(&cctx->appliedParams))*/ ip += blockSize; @@ -3991,7 +4497,9 @@ if (!singleSegment) op[pos++] = windowLogByte; switch(dictIDSizeCode) { - default: assert(0); /* impossible */ + default: + assert(0); /* impossible */ + ZSTD_FALLTHROUGH; case 0 : break; case 1 : op[pos] = (BYTE)(dictID); pos++; break; case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break; @@ -3999,7 +4507,9 @@ } switch(fcsCode) { - default: assert(0); /* impossible */ + default: + assert(0); /* impossible */ + ZSTD_FALLTHROUGH; case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break; case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break; case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break; @@ -4047,7 +4557,7 @@ { RETURN_ERROR_IF(cctx->stage != ZSTDcs_init, stage_wrong, "wrong cctx stage"); - RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm, + RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable, parameter_unsupported, "incompatible with ldm"); cctx->externSeqStore.seq = seq; @@ -4088,7 +4598,7 @@ ms->forceNonContiguous = 0; ms->nextToUpdate = ms->window.dictLimit; } - if (cctx->appliedParams.ldmParams.enableLdm) { + if (cctx->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) { ZSTD_window_update(&cctx->ldmState.window, src, srcSize, /* forceNonContiguous */ 0); } @@ -4120,31 +4630,51 @@ } } -size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize); return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */); } +/* NOTE: Must just wrap ZSTD_compressContinue_public() */ +size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_compressContinue_public(cctx, dst, dstCapacity, src, srcSize); +} -size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) +static size_t ZSTD_getBlockSize_deprecated(const ZSTD_CCtx* cctx) { ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams; assert(!ZSTD_checkCParams(cParams)); - return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog); + return MIN(cctx->appliedParams.maxBlockSize, (size_t)1 << cParams.windowLog); } -size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +/* NOTE: Must just wrap ZSTD_getBlockSize_deprecated() */ +size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) +{ + return ZSTD_getBlockSize_deprecated(cctx); +} + +/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ +size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock: srcSize = %u", (unsigned)srcSize); - { size_t const blockSizeMax = ZSTD_getBlockSize(cctx); + { size_t const blockSizeMax = ZSTD_getBlockSize_deprecated(cctx); RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong, "input is larger than a block"); } return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */); } +/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ +size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +{ + return ZSTD_compressBlock_deprecated(cctx, dst, dstCapacity, src, srcSize); +} + /*! ZSTD_loadDictionaryContent() : * @return : 0, or an error code */ @@ -4153,25 +4683,36 @@ ZSTD_cwksp* ws, ZSTD_CCtx_params const* params, const void* src, size_t srcSize, - ZSTD_dictTableLoadMethod_e dtlm) + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp) { const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; - int const loadLdmDict = params->ldmParams.enableLdm && ls != NULL; + int const loadLdmDict = params->ldmParams.enableLdm == ZSTD_ps_enable && ls != NULL; - /* Assert that we the ms params match the params we're being given */ + /* Assert that the ms params match the params we're being given */ ZSTD_assertEqualCParams(params->cParams, ms->cParams); - if (srcSize > ZSTD_CHUNKSIZE_MAX) { + { /* Ensure large dictionaries can't cause index overflow */ + /* Allow the dictionary to set indices up to exactly ZSTD_CURRENT_MAX. * Dictionaries right at the edge will immediately trigger overflow * correction, but I don't want to insert extra constraints here. */ - U32 const maxDictSize = ZSTD_CURRENT_MAX - 1; - /* We must have cleared our windows when our source is this large. */ - assert(ZSTD_window_isEmpty(ms->window)); - if (loadLdmDict) - assert(ZSTD_window_isEmpty(ls->window)); + U32 maxDictSize = ZSTD_CURRENT_MAX - ZSTD_WINDOW_START_INDEX; + + int const CDictTaggedIndices = ZSTD_CDictIndicesAreTagged(¶ms->cParams); + if (CDictTaggedIndices && tfp == ZSTD_tfp_forCDict) { + /* Some dictionary matchfinders in zstd use "short cache", + * which treats the lower ZSTD_SHORT_CACHE_TAG_BITS of each + * CDict hashtable entry as a tag rather than as part of an index. + * When short cache is used, we need to truncate the dictionary + * so that its indices don't overlap with the tag. */ + U32 const shortCacheMaxDictSize = (1u << (32 - ZSTD_SHORT_CACHE_TAG_BITS)) - ZSTD_WINDOW_START_INDEX; + maxDictSize = MIN(maxDictSize, shortCacheMaxDictSize); + assert(!loadLdmDict); + } + /* If the dictionary is too large, only load the suffix of the dictionary. */ if (srcSize > maxDictSize) { ip = iend - maxDictSize; @@ -4180,30 +4721,46 @@ } } - DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder); + if (srcSize > ZSTD_CHUNKSIZE_MAX) { + /* We must have cleared our windows when our source is this large. */ + assert(ZSTD_window_isEmpty(ms->window)); + if (loadLdmDict) assert(ZSTD_window_isEmpty(ls->window)); + } ZSTD_window_update(&ms->window, src, srcSize, /* forceNonContiguous */ 0); - ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); - ms->forceNonContiguous = params->deterministicRefPrefix; - if (loadLdmDict) { + DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder); + + if (loadLdmDict) { /* Load the entire dict into LDM matchfinders. */ ZSTD_window_update(&ls->window, src, srcSize, /* forceNonContiguous */ 0); ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base); + ZSTD_ldm_fillHashTable(ls, ip, iend, ¶ms->ldmParams); } + /* If the dict is larger than we can reasonably index in our tables, only load the suffix. */ + if (params->cParams.strategy < ZSTD_btultra) { + U32 maxDictSize = 8U << MIN(MAX(params->cParams.hashLog, params->cParams.chainLog), 28); + if (srcSize > maxDictSize) { + ip = iend - maxDictSize; + /* src = ip; deadcode.DeadStores */ + srcSize = maxDictSize; + } + } + + ms->nextToUpdate = (U32)(ip - ms->window.base); + ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); + ms->forceNonContiguous = params->deterministicRefPrefix; + if (srcSize <= HASH_READ_SIZE) return 0; ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, iend); - if (loadLdmDict) - ZSTD_ldm_fillHashTable(ls, ip, iend, ¶ms->ldmParams); - switch(params->cParams.strategy) { case ZSTD_fast: - ZSTD_fillHashTable(ms, iend, dtlm); + ZSTD_fillHashTable(ms, iend, dtlm, tfp); break; case ZSTD_dfast: - ZSTD_fillDoubleHashTable(ms, iend, dtlm); + ZSTD_fillDoubleHashTable(ms, iend, dtlm, tfp); break; case ZSTD_greedy: @@ -4214,9 +4771,9 @@ assert(ms->chainTable != NULL); ZSTD_dedicatedDictSearch_lazy_loadDictionary(ms, iend-HASH_READ_SIZE); } else { - assert(params->useRowMatchFinder != ZSTD_urm_auto); - if (params->useRowMatchFinder == ZSTD_urm_enableRowMatchFinder) { - size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog) * sizeof(U16); + assert(params->useRowMatchFinder != ZSTD_ps_auto); + if (params->useRowMatchFinder == ZSTD_ps_enable) { + size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog); ZSTD_memset(ms->tagTable, 0, tagTableSize); ZSTD_row_update(ms, iend-HASH_READ_SIZE); DEBUGLOG(4, "Using row-based hash table for lazy dict"); @@ -4369,6 +4926,7 @@ ZSTD_CCtx_params const* params, const void* dict, size_t dictSize, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp, void* workspace) { const BYTE* dictPtr = (const BYTE*)dict; @@ -4387,7 +4945,7 @@ { size_t const dictContentSize = (size_t)(dictEnd - dictPtr); FORWARD_IF_ERROR(ZSTD_loadDictionaryContent( - ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), ""); + ms, NULL, ws, params, dictPtr, dictContentSize, dtlm, tfp), ""); } return dictID; } @@ -4403,6 +4961,7 @@ const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp, void* workspace) { DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize); @@ -4415,13 +4974,13 @@ /* dict restricted modes */ if (dictContentType == ZSTD_dct_rawContent) - return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm); + return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm, tfp); if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) { if (dictContentType == ZSTD_dct_auto) { DEBUGLOG(4, "raw content dictionary detected"); return ZSTD_loadDictionaryContent( - ms, ls, ws, params, dict, dictSize, dtlm); + ms, ls, ws, params, dict, dictSize, dtlm, tfp); } RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, ""); assert(0); /* impossible */ @@ -4429,13 +4988,14 @@ /* dict as full zstd dictionary */ return ZSTD_loadZstdDictionary( - bs, ms, ws, params, dict, dictSize, dtlm, workspace); + bs, ms, ws, params, dict, dictSize, dtlm, tfp, workspace); } #define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB) #define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6ULL) /*! ZSTD_compressBegin_internal() : + * Assumption : either @dict OR @cdict (or none) is non-NULL, never both * @return : 0, or an error code */ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, @@ -4471,11 +5031,11 @@ cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent, cdict->dictContentSize, cdict->dictContentType, dtlm, - cctx->entropyWorkspace) + ZSTD_tfp_forCCtx, cctx->entropyWorkspace) : ZSTD_compress_insertDictionary( cctx->blockState.prevCBlock, &cctx->blockState.matchState, &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, dict, dictSize, - dictContentType, dtlm, cctx->entropyWorkspace); + dictContentType, dtlm, ZSTD_tfp_forCCtx, cctx->entropyWorkspace); FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed"); assert(dictID <= UINT_MAX); cctx->dictID = (U32)dictID; @@ -4516,11 +5076,11 @@ &cctxParams, pledgedSrcSize); } -size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) +static size_t +ZSTD_compressBegin_usingDict_deprecated(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) { ZSTD_CCtx_params cctxParams; - { - ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict); + { ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict); ZSTD_CCtxParams_init_internal(&cctxParams, ¶ms, (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel); } DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize); @@ -4528,9 +5088,15 @@ &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } +size_t +ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) +{ + return ZSTD_compressBegin_usingDict_deprecated(cctx, dict, dictSize, compressionLevel); +} + size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) { - return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel); + return ZSTD_compressBegin_usingDict_deprecated(cctx, NULL, 0, compressionLevel); } @@ -4600,9 +5166,9 @@ #endif } -size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { size_t endResult; size_t const cSize = ZSTD_compressContinue_internal(cctx, @@ -4626,6 +5192,14 @@ return cSize + endResult; } +/* NOTE: Must just wrap ZSTD_compressEnd_public() */ +size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); +} + size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -4654,7 +5228,7 @@ FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL, params, srcSize, ZSTDb_not_buffered) , ""); - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); } size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, @@ -4715,7 +5289,7 @@ + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE) /* enableDedicatedDictSearch == 1 ensures that CDict estimation will not be too small * in case we are using DDS with row-hash. */ - + ZSTD_sizeof_matchState(&cParams, ZSTD_resolveRowMatchFinderMode(ZSTD_urm_auto, &cParams), + + ZSTD_sizeof_matchState(&cParams, ZSTD_resolveRowMatchFinderMode(ZSTD_ps_auto, &cParams), /* enableDedicatedDictSearch */ 1, /* forCCtx */ 0) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void *)))); @@ -4779,7 +5353,7 @@ { size_t const dictID = ZSTD_compress_insertDictionary( &cdict->cBlockState, &cdict->matchState, NULL, &cdict->workspace, ¶ms, cdict->dictContent, cdict->dictContentSize, - dictContentType, ZSTD_dtlm_full, cdict->entropyWorkspace); + dictContentType, ZSTD_dtlm_full, ZSTD_tfp_forCDict, cdict->entropyWorkspace); FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed"); assert(dictID <= (size_t)(U32)-1); cdict->dictID = (U32)dictID; @@ -4792,7 +5366,7 @@ static ZSTD_CDict* ZSTD_createCDict_advanced_internal(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_compressionParameters cParams, - ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + ZSTD_paramSwitch_e useRowMatchFinder, U32 enableDedicatedDictSearch, ZSTD_customMem customMem) { @@ -4842,7 +5416,7 @@ &cctxParams, customMem); } -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced2( +ZSTD_CDict* ZSTD_createCDict_advanced2( const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, @@ -4947,7 +5521,7 @@ ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams) { - ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(ZSTD_urm_auto, &cParams); + ZSTD_paramSwitch_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(ZSTD_ps_auto, &cParams); /* enableDedicatedDictSearch == 1 ensures matchstate is not too small in case this CDict will be used for DDS + row hash */ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, useRowMatchFinder, /* enableDedicatedDictSearch */ 1, /* forCCtx */ 0); size_t const neededSize = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict)) @@ -4976,6 +5550,7 @@ params.cParams = cParams; params.useRowMatchFinder = useRowMatchFinder; cdict->useRowMatchFinder = useRowMatchFinder; + cdict->compressionLevel = ZSTD_NO_CLEVEL; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dict, dictSize, @@ -5055,12 +5630,17 @@ /* ZSTD_compressBegin_usingCDict() : * cdict must be != NULL */ -size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) +size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); } +size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) +{ + return ZSTD_compressBegin_usingCDict_deprecated(cctx, cdict); +} + /*! ZSTD_compress_usingCDict_internal(): * Implementation of various ZSTD_compress_usingCDict* functions. */ @@ -5070,7 +5650,7 @@ const ZSTD_CDict* cdict, ZSTD_frameParameters fParams) { FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */ - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); } /*! ZSTD_compress_usingCDict_advanced(): @@ -5267,30 +5847,41 @@ static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx) { - size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos; - if (hintInSize==0) hintInSize = cctx->blockSize; - return hintInSize; + if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { + return cctx->blockSize - cctx->stableIn_notConsumed; + } + assert(cctx->appliedParams.inBufferMode == ZSTD_bm_buffered); + { size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos; + if (hintInSize==0) hintInSize = cctx->blockSize; + return hintInSize; + } } /** ZSTD_compressStream_generic(): * internal function for all *compressStream*() variants - * non-static, because can be called from zstdmt_compress.c - * @return : hint size for next input */ + * @return : hint size for next input to complete ongoing block */ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input, ZSTD_EndDirective const flushMode) { - const char* const istart = (const char*)input->src; - const char* const iend = input->size != 0 ? istart + input->size : istart; - const char* ip = input->pos != 0 ? istart + input->pos : istart; - char* const ostart = (char*)output->dst; - char* const oend = output->size != 0 ? ostart + output->size : ostart; - char* op = output->pos != 0 ? ostart + output->pos : ostart; + const char* const istart = (assert(input != NULL), (const char*)input->src); + const char* const iend = (istart != NULL) ? istart + input->size : istart; + const char* ip = (istart != NULL) ? istart + input->pos : istart; + char* const ostart = (assert(output != NULL), (char*)output->dst); + char* const oend = (ostart != NULL) ? ostart + output->size : ostart; + char* op = (ostart != NULL) ? ostart + output->pos : ostart; U32 someMoreWork = 1; /* check expectations */ - DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode); + DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i, srcSize = %zu", (int)flushMode, input->size - input->pos); + assert(zcs != NULL); + if (zcs->appliedParams.inBufferMode == ZSTD_bm_stable) { + assert(input->pos >= zcs->stableIn_notConsumed); + input->pos -= zcs->stableIn_notConsumed; + ip -= zcs->stableIn_notConsumed; + zcs->stableIn_notConsumed = 0; + } if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) { assert(zcs->inBuff != NULL); assert(zcs->inBuffSize > 0); @@ -5299,8 +5890,10 @@ assert(zcs->outBuff != NULL); assert(zcs->outBuffSize > 0); } - assert(output->pos <= output->size); + if (input->src == NULL) assert(input->size == 0); assert(input->pos <= input->size); + if (output->dst == NULL) assert(output->size == 0); + assert(output->pos <= output->size); assert((U32)flushMode <= (U32)ZSTD_e_end); while (someMoreWork) { @@ -5315,7 +5908,7 @@ || zcs->appliedParams.outBufferMode == ZSTD_bm_stable) /* OR we are allowed to return dstSizeTooSmall */ && (zcs->inBuffPos == 0) ) { /* shortcut to compression pass directly into output buffer */ - size_t const cSize = ZSTD_compressEnd(zcs, + size_t const cSize = ZSTD_compressEnd_public(zcs, op, oend-op, ip, iend-ip); DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize); FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed"); @@ -5332,8 +5925,7 @@ zcs->inBuff + zcs->inBuffPos, toLoad, ip, iend-ip); zcs->inBuffPos += loaded; - if (loaded != 0) - ip += loaded; + if (ip) ip += loaded; if ( (flushMode == ZSTD_e_continue) && (zcs->inBuffPos < zcs->inBuffTarget) ) { /* not enough input to fill full block : stop here */ @@ -5344,6 +5936,20 @@ /* empty */ someMoreWork = 0; break; } + } else { + assert(zcs->appliedParams.inBufferMode == ZSTD_bm_stable); + if ( (flushMode == ZSTD_e_continue) + && ( (size_t)(iend - ip) < zcs->blockSize) ) { + /* can't compress a full block : stop here */ + zcs->stableIn_notConsumed = (size_t)(iend - ip); + ip = iend; /* pretend to have consumed input */ + someMoreWork = 0; break; + } + if ( (flushMode == ZSTD_e_flush) + && (ip == iend) ) { + /* empty */ + someMoreWork = 0; break; + } } /* compress current block (note : this stage cannot be stopped in the middle) */ DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode); @@ -5351,9 +5957,8 @@ void* cDst; size_t cSize; size_t oSize = oend-op; - size_t const iSize = inputBuffered - ? zcs->inBuffPos - zcs->inToCompress - : MIN((size_t)(iend - ip), zcs->blockSize); + size_t const iSize = inputBuffered ? zcs->inBuffPos - zcs->inToCompress + : MIN((size_t)(iend - ip), zcs->blockSize); if (oSize >= ZSTD_compressBound(iSize) || zcs->appliedParams.outBufferMode == ZSTD_bm_stable) cDst = op; /* compress into output buffer, to skip flush stage */ else @@ -5361,9 +5966,9 @@ if (inputBuffered) { unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend); cSize = lastBlock ? - ZSTD_compressEnd(zcs, cDst, oSize, + ZSTD_compressEnd_public(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) : - ZSTD_compressContinue(zcs, cDst, oSize, + ZSTD_compressContinue_public(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize); FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); zcs->frameEnded = lastBlock; @@ -5376,19 +5981,16 @@ if (!lastBlock) assert(zcs->inBuffTarget <= zcs->inBuffSize); zcs->inToCompress = zcs->inBuffPos; - } else { - unsigned const lastBlock = (ip + iSize == iend); - assert(flushMode == ZSTD_e_end /* Already validated */); + } else { /* !inputBuffered, hence ZSTD_bm_stable */ + unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip + iSize == iend); cSize = lastBlock ? - ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) : - ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize); + ZSTD_compressEnd_public(zcs, cDst, oSize, ip, iSize) : + ZSTD_compressContinue_public(zcs, cDst, oSize, ip, iSize); /* Consume the input prior to error checking to mirror buffered mode. */ - if (iSize > 0) - ip += iSize; + if (ip) ip += iSize; FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); zcs->frameEnded = lastBlock; - if (lastBlock) - assert(ip == iend); + if (lastBlock) assert(ip == iend); } if (cDst == op) { /* no need to flush */ op += cSize; @@ -5403,7 +6005,7 @@ zcs->outBuffFlushedSize = 0; zcs->streamStage = zcss_flush; /* pass-through to flush stage */ } - /* fall-through */ + ZSTD_FALLTHROUGH; case zcss_flush: DEBUGLOG(5, "flush stage"); assert(zcs->appliedParams.outBufferMode == ZSTD_bm_buffered); @@ -5464,8 +6066,10 @@ /* After a compression call set the expected input/output buffer. * This is validated at the start of the next compression call. */ -static void ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, ZSTD_outBuffer const* output, ZSTD_inBuffer const* input) +static void +ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const ZSTD_inBuffer* input) { + DEBUGLOG(5, "ZSTD_setBufferExpectations (for advanced stable in/out modes)"); if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { cctx->expectedInBuffer = *input; } @@ -5484,22 +6088,22 @@ { if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { ZSTD_inBuffer const expect = cctx->expectedInBuffer; - if (expect.src != input->src || expect.pos != input->pos || expect.size != input->size) - RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer enabled but input differs!"); - if (endOp != ZSTD_e_end) - RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer can only be used with ZSTD_e_end!"); + if (expect.src != input->src || expect.pos != input->pos) + RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableInBuffer enabled but input differs!"); } + (void)endOp; if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) { size_t const outBufferSize = output->size - output->pos; if (cctx->expectedOutBufferSize != outBufferSize) - RETURN_ERROR(dstBuffer_wrong, "ZSTD_c_stableOutBuffer enabled but output size differs!"); + RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableOutBuffer enabled but output size differs!"); } return 0; } static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx, ZSTD_EndDirective endOp, - size_t inSize) { + size_t inSize) +{ ZSTD_CCtx_params params = cctx->requestedParams; ZSTD_prefixDict const prefixDict = cctx->prefixDict; FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */ @@ -5513,9 +6117,9 @@ params.compressionLevel = cctx->cdict->compressionLevel; } DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage"); - if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1; /* auto-fix pledgedSrcSize */ - { - size_t const dictSize = prefixDict.dict + if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1; /* auto-determine pledgedSrcSize */ + + { size_t const dictSize = prefixDict.dict ? prefixDict.dictSize : (cctx->cdict ? cctx->cdict->dictContentSize : 0); ZSTD_cParamMode_e const mode = ZSTD_getCParamMode(cctx->cdict, ¶ms, cctx->pledgedSrcSizePlusOne - 1); @@ -5524,20 +6128,21 @@ dictSize, mode); } - if (ZSTD_CParams_shouldEnableLdm(¶ms.cParams)) { - /* Enable LDM by default for optimal parser and window size >= 128MB */ - DEBUGLOG(4, "LDM enabled by default (window size >= 128MB, strategy >= btopt)"); - params.ldmParams.enableLdm = 1; - } - - if (ZSTD_CParams_useBlockSplitter(¶ms.cParams)) { - DEBUGLOG(4, "Block splitter enabled by default (window size >= 128K, strategy >= btopt)"); - params.splitBlocks = 1; - } - + params.useBlockSplitter = ZSTD_resolveBlockSplitterMode(params.useBlockSplitter, ¶ms.cParams); + params.ldmParams.enableLdm = ZSTD_resolveEnableLdm(params.ldmParams.enableLdm, ¶ms.cParams); params.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params.useRowMatchFinder, ¶ms.cParams); + params.validateSequences = ZSTD_resolveExternalSequenceValidation(params.validateSequences); + params.maxBlockSize = ZSTD_resolveMaxBlockSize(params.maxBlockSize); + params.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(params.searchForExternalRepcodes, params.compressionLevel); #ifdef ZSTD_MULTITHREAD + /* If external matchfinder is enabled, make sure to fail before checking job size (for consistency) */ + RETURN_ERROR_IF( + params.useSequenceProducer == 1 && params.nbWorkers >= 1, + parameter_combination_unsupported, + "External sequence producer isn't supported with nbWorkers >= 1" + ); + if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) { params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */ } @@ -5565,7 +6170,7 @@ cctx->streamStage = zcss_load; cctx->appliedParams = params; } else -#endif +#endif /* ZSTD_MULTITHREAD */ { U64 const pledgedSrcSize = cctx->pledgedSrcSizePlusOne - 1; assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, @@ -5591,6 +6196,8 @@ return 0; } +/* @return provides a minimum amount of data remaining to be flushed from internal buffers + */ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input, @@ -5605,8 +6212,27 @@ /* transparent initialization stage */ if (cctx->streamStage == zcss_init) { - FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed"); - ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */ + size_t const inputSize = input->size - input->pos; /* no obligation to start from pos==0 */ + size_t const totalInputSize = inputSize + cctx->stableIn_notConsumed; + if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */ + && (endOp == ZSTD_e_continue) /* no flush requested, more input to come */ + && (totalInputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */ + if (cctx->stableIn_notConsumed) { /* not the first time */ + /* check stable source guarantees */ + RETURN_ERROR_IF(input->src != cctx->expectedInBuffer.src, stabilityCondition_notRespected, "stableInBuffer condition not respected: wrong src pointer"); + RETURN_ERROR_IF(input->pos != cctx->expectedInBuffer.size, stabilityCondition_notRespected, "stableInBuffer condition not respected: externally modified pos"); + } + /* pretend input was consumed, to give a sense forward progress */ + input->pos = input->size; + /* save stable inBuffer, for later control, and flush/end */ + cctx->expectedInBuffer = *input; + /* but actually input wasn't consumed, so keep track of position from where compression shall resume */ + cctx->stableIn_notConsumed += inputSize; + /* don't initialize yet, wait for the first block of flush() order, for better parameters adaptation */ + return ZSTD_FRAMEHEADERSIZE_MIN(cctx->requestedParams.format); /* at least some header to produce */ + } + FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, totalInputSize), "compressStream2 initialization failed"); + ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */ } /* end of transparent initialization stage */ @@ -5619,6 +6245,13 @@ ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams); cctx->cParamsChanged = 0; } + if (cctx->stableIn_notConsumed) { + assert(cctx->appliedParams.inBufferMode == ZSTD_bm_stable); + /* some early data was skipped - make it available for consumption */ + assert(input->pos >= cctx->stableIn_notConsumed); + input->pos -= cctx->stableIn_notConsumed; + cctx->stableIn_notConsumed = 0; + } for (;;) { size_t const ipos = input->pos; size_t const opos = output->pos; @@ -5657,7 +6290,7 @@ ZSTD_setBufferExpectations(cctx, output, input); return flushMin; } -#endif +#endif /* ZSTD_MULTITHREAD */ FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , ""); DEBUGLOG(5, "completed ZSTD_compressStream2"); ZSTD_setBufferExpectations(cctx, output, input); @@ -5670,13 +6303,20 @@ const void* src, size_t srcSize, size_t* srcPos, ZSTD_EndDirective endOp) { - ZSTD_outBuffer output = { dst, dstCapacity, *dstPos }; - ZSTD_inBuffer input = { src, srcSize, *srcPos }; + ZSTD_outBuffer output; + ZSTD_inBuffer input; + output.dst = dst; + output.size = dstCapacity; + output.pos = *dstPos; + input.src = src; + input.size = srcSize; + input.pos = *srcPos; /* ZSTD_compressStream2() will check validity of dstPos and srcPos */ - size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp); - *dstPos = output.pos; - *srcPos = input.pos; - return cErr; + { size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp); + *dstPos = output.pos; + *srcPos = input.pos; + return cErr; + } } size_t ZSTD_compress2(ZSTD_CCtx* cctx, @@ -5699,6 +6339,7 @@ /* Reset to the original values. */ cctx->requestedParams.inBufferMode = originalInBufferMode; cctx->requestedParams.outBufferMode = originalOutBufferMode; + FORWARD_IF_ERROR(result, "ZSTD_compressStream2_simpleArgs failed"); if (result != 0) { /* compression not completed, due to lack of output space */ assert(oPos == dstCapacity); @@ -5709,64 +6350,60 @@ } } -typedef struct { - U32 idx; /* Index in array of ZSTD_Sequence */ - U32 posInSequence; /* Position within sequence at idx */ - size_t posInSrc; /* Number of bytes given by sequences provided so far */ -} ZSTD_sequencePosition; - -/* Returns a ZSTD error code if sequence is not valid */ -static size_t ZSTD_validateSequence(U32 offCode, U32 matchLength, - size_t posInSrc, U32 windowLog, size_t dictSize, U32 minMatch) { - size_t offsetBound; - U32 windowSize = 1 << windowLog; - /* posInSrc represents the amount of data the the decoder would decode up to this point. +/* ZSTD_validateSequence() : + * @offCode : is presumed to follow format required by ZSTD_storeSeq() + * @returns a ZSTD error code if sequence is not valid + */ +static size_t +ZSTD_validateSequence(U32 offCode, U32 matchLength, U32 minMatch, + size_t posInSrc, U32 windowLog, size_t dictSize, int useSequenceProducer) +{ + U32 const windowSize = 1u << windowLog; + /* posInSrc represents the amount of data the decoder would decode up to this point. * As long as the amount of data decoded is less than or equal to window size, offsets may be * larger than the total length of output decoded in order to reference the dict, even larger than * window size. After output surpasses windowSize, we're limited to windowSize offsets again. */ - offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize; - RETURN_ERROR_IF(offCode > offsetBound + ZSTD_REP_MOVE, corruption_detected, "Offset too large!"); - RETURN_ERROR_IF(matchLength < minMatch, corruption_detected, "Matchlength too small"); + size_t const offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize; + size_t const matchLenLowerBound = (minMatch == 3 || useSequenceProducer) ? 3 : 4; + RETURN_ERROR_IF(offCode > OFFSET_TO_OFFBASE(offsetBound), externalSequences_invalid, "Offset too large!"); + /* Validate maxNbSeq is large enough for the given matchLength and minMatch */ + RETURN_ERROR_IF(matchLength < matchLenLowerBound, externalSequences_invalid, "Matchlength too small for the minMatch"); return 0; } /* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */ -static U32 ZSTD_finalizeOffCode(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0) { - U32 offCode = rawOffset + ZSTD_REP_MOVE; - U32 repCode = 0; +static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0) +{ + U32 offBase = OFFSET_TO_OFFBASE(rawOffset); if (!ll0 && rawOffset == rep[0]) { - repCode = 1; + offBase = REPCODE1_TO_OFFBASE; } else if (rawOffset == rep[1]) { - repCode = 2 - ll0; + offBase = REPCODE_TO_OFFBASE(2 - ll0); } else if (rawOffset == rep[2]) { - repCode = 3 - ll0; + offBase = REPCODE_TO_OFFBASE(3 - ll0); } else if (ll0 && rawOffset == rep[0] - 1) { - repCode = 3; + offBase = REPCODE3_TO_OFFBASE; } - if (repCode) { - /* ZSTD_storeSeq expects a number in the range [0, 2] to represent a repcode */ - offCode = repCode - 1; - } - return offCode; + return offBase; } -/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of - * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter. - */ -static size_t ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, - const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, - const void* src, size_t blockSize) { +size_t +ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, + ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize, + ZSTD_paramSwitch_e externalRepSearch) +{ U32 idx = seqPos->idx; + U32 const startIdx = idx; BYTE const* ip = (BYTE const*)(src); const BYTE* const iend = ip + blockSize; repcodes_t updatedRepcodes; U32 dictSize; - U32 litLength; - U32 matchLength; - U32 ll0; - U32 offCode; + + DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreExplicitBlockDelim (blockSize = %zu)", blockSize); if (cctx->cdict) { dictSize = (U32)cctx->cdict->dictContentSize; @@ -5776,26 +6413,55 @@ dictSize = 0; } ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t)); - for (; (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0) && idx < inSeqsSize; ++idx) { - litLength = inSeqs[idx].litLength; - matchLength = inSeqs[idx].matchLength; - ll0 = litLength == 0; - offCode = ZSTD_finalizeOffCode(inSeqs[idx].offset, updatedRepcodes.rep, ll0); - updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0); + for (; idx < inSeqsSize && (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0); ++idx) { + U32 const litLength = inSeqs[idx].litLength; + U32 const matchLength = inSeqs[idx].matchLength; + U32 offBase; - DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength); + if (externalRepSearch == ZSTD_ps_disable) { + offBase = OFFSET_TO_OFFBASE(inSeqs[idx].offset); + } else { + U32 const ll0 = (litLength == 0); + offBase = ZSTD_finalizeOffBase(inSeqs[idx].offset, updatedRepcodes.rep, ll0); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); + } + + DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength); if (cctx->appliedParams.validateSequences) { seqPos->posInSrc += litLength + matchLength; - FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc, - cctx->appliedParams.cParams.windowLog, dictSize, - cctx->appliedParams.cParams.minMatch), + FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc, + cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer), "Sequence validation failed"); } - RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation, + RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid, "Not enough memory allocated. Try adjusting ZSTD_c_minMatch."); - ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength - MINMATCH); + ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength); ip += matchLength + litLength; } + + /* If we skipped repcode search while parsing, we need to update repcodes now */ + assert(externalRepSearch != ZSTD_ps_auto); + assert(idx >= startIdx); + if (externalRepSearch == ZSTD_ps_disable && idx != startIdx) { + U32* const rep = updatedRepcodes.rep; + U32 lastSeqIdx = idx - 1; /* index of last non-block-delimiter sequence */ + + if (lastSeqIdx >= startIdx + 2) { + rep[2] = inSeqs[lastSeqIdx - 2].offset; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } else if (lastSeqIdx == startIdx + 1) { + rep[2] = rep[0]; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } else { + assert(lastSeqIdx == startIdx); + rep[2] = rep[1]; + rep[1] = rep[0]; + rep[0] = inSeqs[lastSeqIdx].offset; + } + } + ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(repcodes_t)); if (inSeqs[idx].litLength) { @@ -5804,25 +6470,16 @@ ip += inSeqs[idx].litLength; seqPos->posInSrc += inSeqs[idx].litLength; } - RETURN_ERROR_IF(ip != iend, corruption_detected, "Blocksize doesn't agree with block delimiter!"); + RETURN_ERROR_IF(ip != iend, externalSequences_invalid, "Blocksize doesn't agree with block delimiter!"); seqPos->idx = idx+1; return 0; } -/* Returns the number of bytes to move the current read position back by. Only non-zero - * if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something - * went wrong. - * - * This function will attempt to scan through blockSize bytes represented by the sequences - * in inSeqs, storing any (partial) sequences. - * - * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to - * avoid splitting a match, or to avoid splitting a match such that it would produce a match - * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. - */ -static size_t ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, - const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, - const void* src, size_t blockSize) { +size_t +ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch) +{ U32 idx = seqPos->idx; U32 startPosInSequence = seqPos->posInSequence; U32 endPosInSequence = seqPos->posInSequence + (U32)blockSize; @@ -5832,10 +6489,9 @@ repcodes_t updatedRepcodes; U32 bytesAdjustment = 0; U32 finalMatchSplit = 0; - U32 litLength; - U32 matchLength; - U32 rawOffset; - U32 offCode; + + /* TODO(embg) support fast parsing mode in noBlockDelim mode */ + (void)externalRepSearch; if (cctx->cdict) { dictSize = cctx->cdict->dictContentSize; @@ -5844,14 +6500,15 @@ } else { dictSize = 0; } - DEBUGLOG(5, "ZSTD_copySequencesToSeqStore: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize); + DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreNoBlockDelim: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize); DEBUGLOG(5, "Start seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength); ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t)); while (endPosInSequence && idx < inSeqsSize && !finalMatchSplit) { const ZSTD_Sequence currSeq = inSeqs[idx]; - litLength = currSeq.litLength; - matchLength = currSeq.matchLength; - rawOffset = currSeq.offset; + U32 litLength = currSeq.litLength; + U32 matchLength = currSeq.matchLength; + U32 const rawOffset = currSeq.offset; + U32 offBase; /* Modify the sequence depending on where endPosInSequence lies */ if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) { @@ -5865,7 +6522,6 @@ /* Move to the next sequence */ endPosInSequence -= currSeq.litLength + currSeq.matchLength; startPosInSequence = 0; - idx++; } else { /* This is the final (partial) sequence we're adding from inSeqs, and endPosInSequence does not reach the end of the match. So, we have to split the sequence */ @@ -5904,23 +6560,24 @@ } } /* Check if this offset can be represented with a repcode */ - { U32 ll0 = (litLength == 0); - offCode = ZSTD_finalizeOffCode(rawOffset, updatedRepcodes.rep, ll0); - updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0); + { U32 const ll0 = (litLength == 0); + offBase = ZSTD_finalizeOffBase(rawOffset, updatedRepcodes.rep, ll0); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); } if (cctx->appliedParams.validateSequences) { seqPos->posInSrc += litLength + matchLength; - FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc, - cctx->appliedParams.cParams.windowLog, dictSize, - cctx->appliedParams.cParams.minMatch), + FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc, + cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer), "Sequence validation failed"); } - DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength); - RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation, + DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength); + RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid, "Not enough memory allocated. Try adjusting ZSTD_c_minMatch."); - ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength - MINMATCH); + ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength); ip += matchLength + litLength; + if (!finalMatchSplit) + idx++; /* Next Sequence */ } DEBUGLOG(5, "Ending seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength); assert(idx == inSeqsSize || endPosInSequence <= inSeqs[idx].litLength + inSeqs[idx].matchLength); @@ -5943,8 +6600,9 @@ typedef size_t (*ZSTD_sequenceCopier) (ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, - const void* src, size_t blockSize); -static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) { + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); +static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) +{ ZSTD_sequenceCopier sequenceCopier = NULL; assert(ZSTD_cParam_withinBounds(ZSTD_c_blockDelimiters, mode)); if (mode == ZSTD_sf_explicitBlockDelimiters) { @@ -5956,24 +6614,75 @@ return sequenceCopier; } +/* Discover the size of next block by searching for the delimiter. + * Note that a block delimiter **must** exist in this mode, + * otherwise it's an input error. + * The block size retrieved will be later compared to ensure it remains within bounds */ +static size_t +blockSize_explicitDelimiter(const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos) +{ + int end = 0; + size_t blockSize = 0; + size_t spos = seqPos.idx; + DEBUGLOG(6, "blockSize_explicitDelimiter : seq %zu / %zu", spos, inSeqsSize); + assert(spos <= inSeqsSize); + while (spos < inSeqsSize) { + end = (inSeqs[spos].offset == 0); + blockSize += inSeqs[spos].litLength + inSeqs[spos].matchLength; + if (end) { + if (inSeqs[spos].matchLength != 0) + RETURN_ERROR(externalSequences_invalid, "delimiter format error : both matchlength and offset must be == 0"); + break; + } + spos++; + } + if (!end) + RETURN_ERROR(externalSequences_invalid, "Reached end of sequences without finding a block delimiter"); + return blockSize; +} + +/* More a "target" block size */ +static size_t blockSize_noDelimiter(size_t blockSize, size_t remaining) +{ + int const lastBlock = (remaining <= blockSize); + return lastBlock ? remaining : blockSize; +} + +static size_t determine_blockSize(ZSTD_sequenceFormat_e mode, + size_t blockSize, size_t remaining, + const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos) +{ + DEBUGLOG(6, "determine_blockSize : remainingSize = %zu", remaining); + if (mode == ZSTD_sf_noBlockDelimiters) + return blockSize_noDelimiter(blockSize, remaining); + { size_t const explicitBlockSize = blockSize_explicitDelimiter(inSeqs, inSeqsSize, seqPos); + FORWARD_IF_ERROR(explicitBlockSize, "Error while determining block size with explicit delimiters"); + if (explicitBlockSize > blockSize) + RETURN_ERROR(externalSequences_invalid, "sequences incorrectly define a too large block"); + if (explicitBlockSize > remaining) + RETURN_ERROR(externalSequences_invalid, "sequences define a frame longer than source"); + return explicitBlockSize; + } +} + /* Compress, block-by-block, all of the sequences given. * - * Returns the cumulative size of all compressed blocks (including their headers), otherwise a ZSTD error. + * Returns the cumulative size of all compressed blocks (including their headers), + * otherwise a ZSTD error. */ -static size_t ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const ZSTD_Sequence* inSeqs, size_t inSeqsSize, - const void* src, size_t srcSize) { +static size_t +ZSTD_compressSequences_internal(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const ZSTD_Sequence* inSeqs, size_t inSeqsSize, + const void* src, size_t srcSize) +{ size_t cSize = 0; - U32 lastBlock; - size_t blockSize; - size_t compressedSeqsSize; size_t remaining = srcSize; ZSTD_sequencePosition seqPos = {0, 0, 0}; BYTE const* ip = (BYTE const*)src; BYTE* op = (BYTE*)dst; - ZSTD_sequenceCopier sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters); + ZSTD_sequenceCopier const sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters); DEBUGLOG(4, "ZSTD_compressSequences_internal srcSize: %zu, inSeqsSize: %zu", srcSize, inSeqsSize); /* Special case: empty frame */ @@ -5987,22 +6696,29 @@ } while (remaining) { + size_t compressedSeqsSize; size_t cBlockSize; size_t additionalByteAdjustment; - lastBlock = remaining <= cctx->blockSize; - blockSize = lastBlock ? (U32)remaining : (U32)cctx->blockSize; + size_t blockSize = determine_blockSize(cctx->appliedParams.blockDelimiters, + cctx->blockSize, remaining, + inSeqs, inSeqsSize, seqPos); + U32 const lastBlock = (blockSize == remaining); + FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size"); + assert(blockSize <= remaining); ZSTD_resetSeqStore(&cctx->seqStore); - DEBUGLOG(4, "Working on new block. Blocksize: %zu", blockSize); + DEBUGLOG(5, "Working on new block. Blocksize: %zu (total:%zu)", blockSize, (ip - (const BYTE*)src) + blockSize); - additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize); + additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize, cctx->appliedParams.searchForExternalRepcodes); FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy"); blockSize -= additionalByteAdjustment; /* If blocks are too small, emit as a nocompress block */ - if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) { + /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding + * additional 1. We need to revisit and change this logic to be more consistent */ + if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) { cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed"); - DEBUGLOG(4, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize); + DEBUGLOG(5, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize); cSize += cBlockSize; ip += blockSize; op += cBlockSize; @@ -6011,6 +6727,7 @@ continue; } + RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall, "not enough dstCapacity to write a new compressed block"); compressedSeqsSize = ZSTD_entropyCompressSeqStore(&cctx->seqStore, &cctx->blockState.prevCBlock->entropy, &cctx->blockState.nextCBlock->entropy, &cctx->appliedParams, @@ -6019,11 +6736,11 @@ cctx->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */, cctx->bmi2); FORWARD_IF_ERROR(compressedSeqsSize, "Compressing sequences of block failed"); - DEBUGLOG(4, "Compressed sequences size: %zu", compressedSeqsSize); + DEBUGLOG(5, "Compressed sequences size: %zu", compressedSeqsSize); if (!cctx->isFirstBlock && ZSTD_maybeRLE(&cctx->seqStore) && - ZSTD_isRLE((BYTE const*)src, srcSize)) { + ZSTD_isRLE(ip, blockSize)) { /* We don't want to emit our first block as a RLE even if it qualifies because * doing so will cause the decoder (cli only) to throw a "should consume all input error." * This is only an issue for zstd <= v1.4.3 @@ -6034,12 +6751,12 @@ if (compressedSeqsSize == 0) { /* ZSTD_noCompressBlock writes the block header as well */ cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); - FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed"); - DEBUGLOG(4, "Writing out nocompress block, size: %zu", cBlockSize); + FORWARD_IF_ERROR(cBlockSize, "ZSTD_noCompressBlock failed"); + DEBUGLOG(5, "Writing out nocompress block, size: %zu", cBlockSize); } else if (compressedSeqsSize == 1) { cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock); - FORWARD_IF_ERROR(cBlockSize, "RLE compress block failed"); - DEBUGLOG(4, "Writing out RLE block, size: %zu", cBlockSize); + FORWARD_IF_ERROR(cBlockSize, "ZSTD_rleCompressBlock failed"); + DEBUGLOG(5, "Writing out RLE block, size: %zu", cBlockSize); } else { U32 cBlockHeader; /* Error checking and repcodes update */ @@ -6051,11 +6768,10 @@ cBlockHeader = lastBlock + (((U32)bt_compressed)<<1) + (U32)(compressedSeqsSize << 3); MEM_writeLE24(op, cBlockHeader); cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize; - DEBUGLOG(4, "Writing out compressed block, size: %zu", cBlockSize); + DEBUGLOG(5, "Writing out compressed block, size: %zu", cBlockSize); } cSize += cBlockSize; - DEBUGLOG(4, "cSize running total: %zu", cSize); if (lastBlock) { break; @@ -6066,21 +6782,25 @@ dstCapacity -= cBlockSize; cctx->isFirstBlock = 0; } + DEBUGLOG(5, "cSize running total: %zu (remaining dstCapacity=%zu)", cSize, dstCapacity); } + DEBUGLOG(4, "cSize final total: %zu", cSize); return cSize; } -size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapacity, +size_t ZSTD_compressSequences(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, const ZSTD_Sequence* inSeqs, size_t inSeqsSize, - const void* src, size_t srcSize) { + const void* src, size_t srcSize) +{ BYTE* op = (BYTE*)dst; size_t cSize = 0; size_t compressedBlocksSize = 0; size_t frameHeaderSize = 0; /* Transparent initialization stage, same as compressStream2() */ - DEBUGLOG(3, "ZSTD_compressSequences()"); + DEBUGLOG(4, "ZSTD_compressSequences (dstCapacity=%zu)", dstCapacity); assert(cctx != NULL); FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, ZSTD_e_end, srcSize), "CCtx initialization failed"); /* Begin writing output, starting with frame header */ @@ -6108,26 +6828,34 @@ cSize += 4; } - DEBUGLOG(3, "Final compressed size: %zu", cSize); + DEBUGLOG(4, "Final compressed size: %zu", cSize); return cSize; } /*====== Finalize ======*/ +static ZSTD_inBuffer inBuffer_forEndFlush(const ZSTD_CStream* zcs) +{ + const ZSTD_inBuffer nullInput = { NULL, 0, 0 }; + const int stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable); + return stableInput ? zcs->expectedInBuffer : nullInput; +} + /*! ZSTD_flushStream() : * @return : amount of data remaining to flush */ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { - ZSTD_inBuffer input = { NULL, 0, 0 }; + ZSTD_inBuffer input = inBuffer_forEndFlush(zcs); + input.size = input.pos; /* do not ingest more input during flush */ return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush); } size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output) { - ZSTD_inBuffer input = { NULL, 0, 0 }; + ZSTD_inBuffer input = inBuffer_forEndFlush(zcs); size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end); - FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 failed"); + FORWARD_IF_ERROR(remainingToFlush , "ZSTD_compressStream2(,,ZSTD_e_end) failed"); if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */ /* single thread mode : attempt to calculate remaining to flush more precisely */ { size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE; @@ -6140,119 +6868,12 @@ /*-===== Pre-defined compression levels =====-*/ +#include "clevels.h" -#define ZSTD_MAX_CLEVEL 22 int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; } int ZSTD_defaultCLevel(void) { return ZSTD_CLEVEL_DEFAULT; } -static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { -{ /* "default" - for any srcSize > 256 KB */ - /* W, C, H, S, L, TL, strat */ - { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */ - { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */ - { 20, 15, 16, 1, 6, 0, ZSTD_fast }, /* level 2 */ - { 21, 16, 17, 1, 5, 0, ZSTD_dfast }, /* level 3 */ - { 21, 18, 18, 1, 5, 0, ZSTD_dfast }, /* level 4 */ - { 21, 18, 19, 2, 5, 2, ZSTD_greedy }, /* level 5 */ - { 21, 19, 19, 3, 5, 4, ZSTD_greedy }, /* level 6 */ - { 21, 19, 19, 3, 5, 8, ZSTD_lazy }, /* level 7 */ - { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */ - { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */ - { 22, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */ - { 22, 21, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */ - { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ - { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 13 */ - { 22, 22, 23, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */ - { 22, 23, 23, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */ - { 22, 22, 22, 5, 5, 48, ZSTD_btopt }, /* level 16 */ - { 23, 23, 22, 5, 4, 64, ZSTD_btopt }, /* level 17 */ - { 23, 23, 22, 6, 3, 64, ZSTD_btultra }, /* level 18 */ - { 23, 24, 22, 7, 3,256, ZSTD_btultra2}, /* level 19 */ - { 25, 25, 23, 7, 3,256, ZSTD_btultra2}, /* level 20 */ - { 26, 26, 24, 7, 3,512, ZSTD_btultra2}, /* level 21 */ - { 27, 27, 25, 9, 3,999, ZSTD_btultra2}, /* level 22 */ -}, -{ /* for srcSize <= 256 KB */ - /* W, C, H, S, L, T, strat */ - { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ - { 18, 13, 14, 1, 6, 0, ZSTD_fast }, /* level 1 */ - { 18, 14, 14, 1, 5, 0, ZSTD_dfast }, /* level 2 */ - { 18, 16, 16, 1, 4, 0, ZSTD_dfast }, /* level 3 */ - { 18, 16, 17, 2, 5, 2, ZSTD_greedy }, /* level 4.*/ - { 18, 18, 18, 3, 5, 2, ZSTD_greedy }, /* level 5.*/ - { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/ - { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */ - { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ - { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ - { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ - { 18, 18, 19, 5, 4, 12, ZSTD_btlazy2 }, /* level 11.*/ - { 18, 19, 19, 7, 4, 12, ZSTD_btlazy2 }, /* level 12.*/ - { 18, 18, 19, 4, 4, 16, ZSTD_btopt }, /* level 13 */ - { 18, 18, 19, 4, 3, 32, ZSTD_btopt }, /* level 14.*/ - { 18, 18, 19, 6, 3,128, ZSTD_btopt }, /* level 15.*/ - { 18, 19, 19, 6, 3,128, ZSTD_btultra }, /* level 16.*/ - { 18, 19, 19, 8, 3,256, ZSTD_btultra }, /* level 17.*/ - { 18, 19, 19, 6, 3,128, ZSTD_btultra2}, /* level 18.*/ - { 18, 19, 19, 8, 3,256, ZSTD_btultra2}, /* level 19.*/ - { 18, 19, 19, 10, 3,512, ZSTD_btultra2}, /* level 20.*/ - { 18, 19, 19, 12, 3,512, ZSTD_btultra2}, /* level 21.*/ - { 18, 19, 19, 13, 3,999, ZSTD_btultra2}, /* level 22.*/ -}, -{ /* for srcSize <= 128 KB */ - /* W, C, H, S, L, T, strat */ - { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ - { 17, 12, 13, 1, 6, 0, ZSTD_fast }, /* level 1 */ - { 17, 13, 15, 1, 5, 0, ZSTD_fast }, /* level 2 */ - { 17, 15, 16, 2, 5, 0, ZSTD_dfast }, /* level 3 */ - { 17, 17, 17, 2, 4, 0, ZSTD_dfast }, /* level 4 */ - { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */ - { 17, 17, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */ - { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */ - { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */ - { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */ - { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */ - { 17, 17, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 11 */ - { 17, 18, 17, 7, 4, 12, ZSTD_btlazy2 }, /* level 12 */ - { 17, 18, 17, 3, 4, 12, ZSTD_btopt }, /* level 13.*/ - { 17, 18, 17, 4, 3, 32, ZSTD_btopt }, /* level 14.*/ - { 17, 18, 17, 6, 3,256, ZSTD_btopt }, /* level 15.*/ - { 17, 18, 17, 6, 3,128, ZSTD_btultra }, /* level 16.*/ - { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 17.*/ - { 17, 18, 17, 10, 3,512, ZSTD_btultra }, /* level 18.*/ - { 17, 18, 17, 5, 3,256, ZSTD_btultra2}, /* level 19.*/ - { 17, 18, 17, 7, 3,512, ZSTD_btultra2}, /* level 20.*/ - { 17, 18, 17, 9, 3,512, ZSTD_btultra2}, /* level 21.*/ - { 17, 18, 17, 11, 3,999, ZSTD_btultra2}, /* level 22.*/ -}, -{ /* for srcSize <= 16 KB */ - /* W, C, H, S, L, T, strat */ - { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */ - { 14, 14, 15, 1, 5, 0, ZSTD_fast }, /* level 1 */ - { 14, 14, 15, 1, 4, 0, ZSTD_fast }, /* level 2 */ - { 14, 14, 15, 2, 4, 0, ZSTD_dfast }, /* level 3 */ - { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4 */ - { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/ - { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */ - { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */ - { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/ - { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/ - { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/ - { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/ - { 14, 15, 14, 4, 3, 24, ZSTD_btopt }, /* level 12.*/ - { 14, 15, 14, 5, 3, 32, ZSTD_btultra }, /* level 13.*/ - { 14, 15, 15, 6, 3, 64, ZSTD_btultra }, /* level 14.*/ - { 14, 15, 15, 7, 3,256, ZSTD_btultra }, /* level 15.*/ - { 14, 15, 15, 5, 3, 48, ZSTD_btultra2}, /* level 16.*/ - { 14, 15, 15, 6, 3,128, ZSTD_btultra2}, /* level 17.*/ - { 14, 15, 15, 7, 3,256, ZSTD_btultra2}, /* level 18.*/ - { 14, 15, 15, 8, 3,256, ZSTD_btultra2}, /* level 19.*/ - { 14, 15, 15, 8, 3,512, ZSTD_btultra2}, /* level 20.*/ - { 14, 15, 15, 9, 3,512, ZSTD_btultra2}, /* level 21.*/ - { 14, 15, 15, 10, 3,999, ZSTD_btultra2}, /* level 22.*/ -}, -}; - static ZSTD_compressionParameters ZSTD_dedicatedDictSearch_getCParams(int const compressionLevel, size_t const dictSize) { ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, 0, dictSize, ZSTD_cpm_createCDict); @@ -6356,7 +6977,7 @@ cp.targetLength = (unsigned)(-clampedCompressionLevel); } /* refine parameters based on srcSize & dictSize */ - return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode); + return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode, ZSTD_ps_auto); } } @@ -6391,3 +7012,21 @@ if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown); } + +void ZSTD_registerSequenceProducer( + ZSTD_CCtx* zc, void* mState, + ZSTD_sequenceProducer_F* mFinder +) { + if (mFinder != NULL) { + ZSTD_externalMatchCtx emctx; + emctx.mState = mState; + emctx.mFinder = mFinder; + emctx.seqBuffer = NULL; + emctx.seqBufferCapacity = 0; + zc->externalMatchCtx = emctx; + zc->requestedParams.useSequenceProducer = 1; + } else { + ZSTD_memset(&zc->externalMatchCtx, 0, sizeof(zc->externalMatchCtx)); + zc->requestedParams.useSequenceProducer = 0; + } +}
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h index 3b04fd0..10f68d0 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h +++ b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -23,6 +23,7 @@ #ifdef ZSTD_MULTITHREAD # include "zstdmt_compress.h" #endif +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_NbCommonBytes */ #if defined (__cplusplus) extern "C" { @@ -63,7 +64,7 @@ } ZSTD_localDict; typedef struct { - HUF_CElt CTable[HUF_CTABLE_SIZE_U32(255)]; + HUF_CElt CTable[HUF_CTABLE_SIZE_ST(255)]; HUF_repeat repeatMode; } ZSTD_hufCTables_t; @@ -117,19 +118,20 @@ /** ZSTD_buildBlockEntropyStats() : * Builds entropy for the block. * @return : 0 on success or error code */ -size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr, - const ZSTD_entropyCTables_t* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, - const ZSTD_CCtx_params* cctxParams, - ZSTD_entropyCTablesMetadata_t* entropyMetadata, - void* workspace, size_t wkspSize); +size_t ZSTD_buildBlockEntropyStats( + const seqStore_t* seqStorePtr, + const ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + const ZSTD_CCtx_params* cctxParams, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, size_t wkspSize); /********************************* * Compression internals structs * *********************************/ typedef struct { - U32 off; /* Offset code (offset + ZSTD_REP_MOVE) for the match */ + U32 off; /* Offset sumtype code for the match, using ZSTD_storeSeq() format */ U32 len; /* Raw length of match */ } ZSTD_match_t; @@ -148,6 +150,12 @@ size_t capacity; /* The capacity starting from `seq` pointer */ } rawSeqStore_t; +typedef struct { + U32 idx; /* Index in array of ZSTD_Sequence */ + U32 posInSequence; /* Position within sequence at idx */ + size_t posInSrc; /* Number of bytes given by sequences provided so far */ +} ZSTD_sequencePosition; + UNUSED_ATTR static const rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0, 0}; typedef struct { @@ -179,7 +187,7 @@ U32 offCodeSumBasePrice; /* to compare to log2(offreq) */ ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow a pre-defined cost structure */ const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated dictionary statistics */ - ZSTD_literalCompressionMode_e literalCompressionMode; + ZSTD_paramSwitch_e literalCompressionMode; } optState_t; typedef struct { @@ -199,6 +207,8 @@ */ } ZSTD_window_t; +#define ZSTD_WINDOW_START_INDEX 2 + typedef struct ZSTD_matchState_t ZSTD_matchState_t; #define ZSTD_ROW_HASH_CACHE_SIZE 8 /* Size of prefetching hash cache for row-based matchfinder */ @@ -216,8 +226,10 @@ U32 hashLog3; /* dispatch table for matches of len==3 : larger == faster, more memory */ U32 rowHashLog; /* For row-based matchfinder: Hashlog based on nb of rows in the hashTable.*/ - U16* tagTable; /* For row-based matchFinder: A row-based table containing the hashes and head index. */ + BYTE* tagTable; /* For row-based matchFinder: A row-based table containing the hashes and head index. */ U32 hashCache[ZSTD_ROW_HASH_CACHE_SIZE]; /* For row-based matchFinder: a cache of hashes to improve speed */ + U64 hashSalt; /* For row-based matchFinder: salts the hash for re-use of tag table */ + U32 hashSaltEntropy; /* For row-based matchFinder: collects entropy for salt generation */ U32* hashTable; U32* hashTable3; @@ -232,6 +244,18 @@ const ZSTD_matchState_t* dictMatchState; ZSTD_compressionParameters cParams; const rawSeqStore_t* ldmSeqStore; + + /* Controls prefetching in some dictMatchState matchfinders. + * This behavior is controlled from the cctx ms. + * This parameter has no effect in the cdict ms. */ + int prefetchCDictTables; + + /* When == 0, lazy match finders insert every position. + * When != 0, lazy match finders only insert positions they search. + * This allows them to skip much faster over incompressible data, + * at a small cost to compression ratio. + */ + int lazySkipping; }; typedef struct { @@ -264,7 +288,7 @@ } ldmState_t; typedef struct { - U32 enableLdm; /* 1 if enable long distance matching */ + ZSTD_paramSwitch_e enableLdm; /* ZSTD_ps_enable to enable LDM. ZSTD_ps_auto by default */ U32 hashLog; /* Log size of hashTable */ U32 bucketSizeLog; /* Log bucket size for collision resolution, at most 8 */ U32 minMatchLength; /* Minimum match length */ @@ -295,7 +319,7 @@ * There is no guarantee that hint is close to actual source size */ ZSTD_dictAttachPref_e attachDictPref; - ZSTD_literalCompressionMode_e literalCompressionMode; + ZSTD_paramSwitch_e literalCompressionMode; /* Multithreading: used to pass parameters to mtctx */ int nbWorkers; @@ -318,16 +342,34 @@ int validateSequences; /* Block splitting */ - int splitBlocks; + ZSTD_paramSwitch_e useBlockSplitter; /* Param for deciding whether to use row-based matchfinder */ - ZSTD_useRowMatchFinderMode_e useRowMatchFinder; + ZSTD_paramSwitch_e useRowMatchFinder; /* Always load a dictionary in ext-dict mode (not prefix mode)? */ int deterministicRefPrefix; /* Internal use, for createCCtxParams() and freeCCtxParams() only */ ZSTD_customMem customMem; + + /* Controls prefetching in some dictMatchState matchfinders */ + ZSTD_paramSwitch_e prefetchCDictTables; + + /* Controls whether zstd will fall back to an internal matchfinder + * if the external matchfinder returns an error code. */ + int enableMatchFinderFallback; + + /* Indicates whether an external matchfinder has been referenced. + * Users can't set this externally. + * It is set internally in ZSTD_registerSequenceProducer(). */ + int useSequenceProducer; + + /* Adjust the max block size*/ + size_t maxBlockSize; + + /* Controls repcode search in external sequence parsing */ + ZSTD_paramSwitch_e searchForExternalRepcodes; }; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */ #define COMPRESS_SEQUENCES_WORKSPACE_SIZE (sizeof(unsigned) * (MaxSeq + 2)) @@ -343,6 +385,30 @@ ZSTDb_buffered } ZSTD_buffered_policy_e; +/** + * Struct that contains all elements of block splitter that should be allocated + * in a wksp. + */ +#define ZSTD_MAX_NB_BLOCK_SPLITS 196 +typedef struct { + seqStore_t fullSeqStoreChunk; + seqStore_t firstHalfSeqStore; + seqStore_t secondHalfSeqStore; + seqStore_t currSeqStore; + seqStore_t nextSeqStore; + + U32 partitions[ZSTD_MAX_NB_BLOCK_SPLITS]; + ZSTD_entropyCTablesMetadata_t entropyMetadata; +} ZSTD_blockSplitCtx; + +/* Context for block-level external matchfinder API */ +typedef struct { + void* mState; + ZSTD_sequenceProducer_F* mFinder; + ZSTD_Sequence* seqBuffer; + size_t seqBufferCapacity; +} ZSTD_externalMatchCtx; + struct ZSTD_CCtx_s { ZSTD_compressionStage_e stage; int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */ @@ -374,7 +440,7 @@ ZSTD_blockState_t blockState; U32* entropyWorkspace; /* entropy workspace of ENTROPY_WORKSPACE_SIZE bytes */ - /* Wether we are streaming or not */ + /* Whether we are streaming or not */ ZSTD_buffered_policy_e bufferedPolicy; /* streaming */ @@ -392,6 +458,7 @@ /* Stable in/out buffer verification */ ZSTD_inBuffer expectedInBuffer; + size_t stableIn_notConsumed; /* nb bytes within stable input buffer that are said to be consumed but are not */ size_t expectedOutBufferSize; /* Dictionary */ @@ -408,9 +475,16 @@ #if ZSTD_TRACE ZSTD_TraceCtx traceCtx; #endif + + /* Workspace for block splitter */ + ZSTD_blockSplitCtx blockSplitCtx; + + /* Workspace for external matchfinder */ + ZSTD_externalMatchCtx externalMatchCtx; }; typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; +typedef enum { ZSTD_tfp_forCCtx, ZSTD_tfp_forCDict } ZSTD_tableFillPurpose_e; typedef enum { ZSTD_noDict = 0, @@ -432,7 +506,7 @@ * In this mode we take both the source size and the dictionary size * into account when selecting and adjusting the parameters. */ - ZSTD_cpm_unknown = 3, /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams. + ZSTD_cpm_unknown = 3 /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams. * We don't know what these parameters are for. We default to the legacy * behavior of taking both the source size and the dict size into account * when selecting and adjusting parameters. @@ -442,7 +516,7 @@ typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_useRowMatchFinderMode_e rowMatchfinderMode, ZSTD_dictMode_e dictMode); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_paramSwitch_e rowMatchfinderMode, ZSTD_dictMode_e dictMode); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) @@ -476,31 +550,6 @@ return (mlBase > 127) ? ZSTD_highbit32(mlBase) + ML_deltaCode : ML_Code[mlBase]; } -typedef struct repcodes_s { - U32 rep[3]; -} repcodes_t; - -MEM_STATIC repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0) -{ - repcodes_t newReps; - if (offset >= ZSTD_REP_NUM) { /* full offset */ - newReps.rep[2] = rep[1]; - newReps.rep[1] = rep[0]; - newReps.rep[0] = offset - ZSTD_REP_MOVE; - } else { /* repcode */ - U32 const repCode = offset + ll0; - if (repCode > 0) { /* note : if repCode==0, no change */ - U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; - newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2]; - newReps.rep[1] = rep[0]; - newReps.rep[0] = currentOffset; - } else { /* repCode == 0 */ - ZSTD_memcpy(&newReps, rep, sizeof(newReps)); - } - } - return newReps; -} - /* ZSTD_cParam_withinBounds: * @return 1 if value is within cParam bounds, * 0 otherwise */ @@ -516,9 +565,11 @@ /* ZSTD_noCompressBlock() : * Writes uncompressed block to dst buffer from given src. * Returns the size of the block */ -MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) +MEM_STATIC size_t +ZSTD_noCompressBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock) { U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3); + DEBUGLOG(5, "ZSTD_noCompressBlock (srcSize=%zu, dstCapacity=%zu)", srcSize, dstCapacity); RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity, dstSize_tooSmall, "dst buf too small for uncompressed block"); MEM_writeLE24(dst, cBlockHeader24); @@ -526,7 +577,8 @@ return ZSTD_blockHeaderSize + srcSize; } -MEM_STATIC size_t ZSTD_rleCompressBlock (void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock) +MEM_STATIC size_t +ZSTD_rleCompressBlock(void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock) { BYTE* const op = (BYTE*)dst; U32 const cBlockHeader = lastBlock + (((U32)bt_rle)<<1) + (U32)(srcSize << 3); @@ -545,21 +597,21 @@ { U32 const minlog = (strat>=ZSTD_btultra) ? (U32)(strat) - 1 : 6; ZSTD_STATIC_ASSERT(ZSTD_btultra == 8); - assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat)); + assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, (int)strat)); return (srcSize >> minlog) + 2; } -MEM_STATIC int ZSTD_disableLiteralsCompression(const ZSTD_CCtx_params* cctxParams) +MEM_STATIC int ZSTD_literalsCompressionIsDisabled(const ZSTD_CCtx_params* cctxParams) { switch (cctxParams->literalCompressionMode) { - case ZSTD_lcm_huffman: + case ZSTD_ps_enable: return 0; - case ZSTD_lcm_uncompressed: + case ZSTD_ps_disable: return 1; default: assert(0 /* impossible: pre-validated */); - /* fall-through */ - case ZSTD_lcm_auto: + ZSTD_FALLTHROUGH; + case ZSTD_ps_auto: return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0); } } @@ -569,7 +621,9 @@ * Only called when the sequence ends past ilimit_w, so it only needs to be optimized for single * large copies. */ -static void ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE const* ilimit_w) { +static void +ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE const* ilimit_w) +{ assert(iend > ilimit_w); if (ip <= ilimit_w) { ZSTD_wildcopy(op, ip, ilimit_w - ip, ZSTD_no_overlap); @@ -579,14 +633,28 @@ while (ip < iend) *op++ = *ip++; } + +#define REPCODE1_TO_OFFBASE REPCODE_TO_OFFBASE(1) +#define REPCODE2_TO_OFFBASE REPCODE_TO_OFFBASE(2) +#define REPCODE3_TO_OFFBASE REPCODE_TO_OFFBASE(3) +#define REPCODE_TO_OFFBASE(r) (assert((r)>=1), assert((r)<=ZSTD_REP_NUM), (r)) /* accepts IDs 1,2,3 */ +#define OFFSET_TO_OFFBASE(o) (assert((o)>0), o + ZSTD_REP_NUM) +#define OFFBASE_IS_OFFSET(o) ((o) > ZSTD_REP_NUM) +#define OFFBASE_IS_REPCODE(o) ( 1 <= (o) && (o) <= ZSTD_REP_NUM) +#define OFFBASE_TO_OFFSET(o) (assert(OFFBASE_IS_OFFSET(o)), (o) - ZSTD_REP_NUM) +#define OFFBASE_TO_REPCODE(o) (assert(OFFBASE_IS_REPCODE(o)), (o)) /* returns ID 1,2,3 */ + /*! ZSTD_storeSeq() : - * Store a sequence (litlen, litPtr, offCode and mlBase) into seqStore_t. - * `offCode` : distance to match + ZSTD_REP_MOVE (values <= ZSTD_REP_MOVE are repCodes). - * `mlBase` : matchLength - MINMATCH - * Allowed to overread literals up to litLimit. + * Store a sequence (litlen, litPtr, offBase and matchLength) into seqStore_t. + * @offBase : Users should employ macros REPCODE_TO_OFFBASE() and OFFSET_TO_OFFBASE(). + * @matchLength : must be >= MINMATCH + * Allowed to over-read literals up to litLimit. */ -HINT_INLINE UNUSED_ATTR -void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const BYTE* literals, const BYTE* litLimit, U32 offCode, size_t mlBase) +HINT_INLINE UNUSED_ATTR void +ZSTD_storeSeq(seqStore_t* seqStorePtr, + size_t litLength, const BYTE* literals, const BYTE* litLimit, + U32 offBase, + size_t matchLength) { BYTE const* const litLimit_w = litLimit - WILDCOPY_OVERLENGTH; BYTE const* const litEnd = literals + litLength; @@ -594,8 +662,8 @@ static const BYTE* g_start = NULL; if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */ { U32 const pos = (U32)((const BYTE*)literals - g_start); - DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u", - pos, (U32)litLength, (U32)mlBase+MINMATCH, (U32)offCode); + DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offBase%7u", + pos, (U32)litLength, (U32)matchLength, (U32)offBase); } #endif assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq); @@ -605,9 +673,9 @@ assert(literals + litLength <= litLimit); if (litEnd <= litLimit_w) { /* Common case we can use wildcopy. - * First copy 16 bytes, because literals are likely short. - */ - assert(WILDCOPY_OVERLENGTH >= 16); + * First copy 16 bytes, because literals are likely short. + */ + ZSTD_STATIC_ASSERT(WILDCOPY_OVERLENGTH >= 16); ZSTD_copy16(seqStorePtr->lit, literals); if (litLength > 16) { ZSTD_wildcopy(seqStorePtr->lit+16, literals+16, (ptrdiff_t)litLength-16, ZSTD_no_overlap); @@ -626,96 +694,63 @@ seqStorePtr->sequences[0].litLength = (U16)litLength; /* match offset */ - seqStorePtr->sequences[0].offset = offCode + 1; + seqStorePtr->sequences[0].offBase = offBase; /* match Length */ - if (mlBase>0xFFFF) { - assert(seqStorePtr->longLengthType == ZSTD_llt_none); /* there can only be a single long length */ - seqStorePtr->longLengthType = ZSTD_llt_matchLength; - seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + assert(matchLength >= MINMATCH); + { size_t const mlBase = matchLength - MINMATCH; + if (mlBase>0xFFFF) { + assert(seqStorePtr->longLengthType == ZSTD_llt_none); /* there can only be a single long length */ + seqStorePtr->longLengthType = ZSTD_llt_matchLength; + seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + } + seqStorePtr->sequences[0].mlBase = (U16)mlBase; } - seqStorePtr->sequences[0].matchLength = (U16)mlBase; seqStorePtr->sequences++; } +/* ZSTD_updateRep() : + * updates in-place @rep (array of repeat offsets) + * @offBase : sum-type, using numeric representation of ZSTD_storeSeq() + */ +MEM_STATIC void +ZSTD_updateRep(U32 rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0) +{ + if (OFFBASE_IS_OFFSET(offBase)) { /* full offset */ + rep[2] = rep[1]; + rep[1] = rep[0]; + rep[0] = OFFBASE_TO_OFFSET(offBase); + } else { /* repcode */ + U32 const repCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0; + if (repCode > 0) { /* note : if repCode==0, no change */ + U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode]; + rep[2] = (repCode >= 2) ? rep[1] : rep[2]; + rep[1] = rep[0]; + rep[0] = currentOffset; + } else { /* repCode == 0 */ + /* nothing to do */ + } + } +} + +typedef struct repcodes_s { + U32 rep[3]; +} repcodes_t; + +MEM_STATIC repcodes_t +ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0) +{ + repcodes_t newReps; + ZSTD_memcpy(&newReps, rep, sizeof(newReps)); + ZSTD_updateRep(newReps.rep, offBase, ll0); + return newReps; +} + /*-************************************* * Match length counter ***************************************/ -static unsigned ZSTD_NbCommonBytes (size_t val) -{ - if (MEM_isLittleEndian()) { - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) -# if STATIC_BMI2 - return _tzcnt_u64(val) >> 3; -# else - unsigned long r = 0; - return _BitScanForward64( &r, (U64)val ) ? (unsigned)(r >> 3) : 0; -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) - return (__builtin_ctzll((U64)val) >> 3); -# else - static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, - 0, 3, 1, 3, 1, 4, 2, 7, - 0, 2, 3, 6, 1, 5, 3, 5, - 1, 3, 4, 4, 2, 5, 6, 7, - 7, 0, 1, 2, 3, 3, 4, 6, - 2, 6, 5, 5, 3, 4, 5, 6, - 7, 1, 2, 4, 6, 4, 4, 5, - 7, 2, 6, 5, 7, 6, 7, 7 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - unsigned long r=0; - return _BitScanForward( &r, (U32)val ) ? (unsigned)(r >> 3) : 0; -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_ctz((U32)val) >> 3); -# else - static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, - 3, 2, 2, 1, 3, 2, 0, 1, - 3, 3, 1, 2, 2, 2, 2, 0, - 3, 1, 2, 0, 1, 0, 1, 1 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; -# endif - } - } else { /* Big Endian CPU */ - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) -# if STATIC_BMI2 - return _lzcnt_u64(val) >> 3; -# else - unsigned long r = 0; - return _BitScanReverse64(&r, (U64)val) ? (unsigned)(r >> 3) : 0; -# endif -# elif defined(__GNUC__) && (__GNUC__ >= 4) - return (__builtin_clzll(val) >> 3); -# else - unsigned r; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - unsigned long r = 0; - return _BitScanReverse( &r, (unsigned long)val ) ? (unsigned)(r >> 3) : 0; -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_clz((U32)val) >> 3); -# else - unsigned r; - if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } - r += (!val); - return r; -# endif - } } -} - - MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit) { const BYTE* const pStart = pIn; @@ -761,32 +796,43 @@ * Hashes ***************************************/ static const U32 prime3bytes = 506832829U; -static U32 ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes) >> (32-h) ; } -MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */ +static U32 ZSTD_hash3(U32 u, U32 h, U32 s) { assert(h <= 32); return (((u << (32-24)) * prime3bytes) ^ s) >> (32-h) ; } +MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h, 0); } /* only in zstd_opt.h */ +MEM_STATIC size_t ZSTD_hash3PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash3(MEM_readLE32(ptr), h, s); } static const U32 prime4bytes = 2654435761U; -static U32 ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; } -static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); } +static U32 ZSTD_hash4(U32 u, U32 h, U32 s) { assert(h <= 32); return ((u * prime4bytes) ^ s) >> (32-h) ; } +static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_readLE32(ptr), h, 0); } +static size_t ZSTD_hash4PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash4(MEM_readLE32(ptr), h, s); } static const U64 prime5bytes = 889523592379ULL; -static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; } -static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); } +static size_t ZSTD_hash5(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-40)) * prime5bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash5PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash5(MEM_readLE64(p), h, s); } static const U64 prime6bytes = 227718039650203ULL; -static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } -static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } +static size_t ZSTD_hash6(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-48)) * prime6bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash6PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash6(MEM_readLE64(p), h, s); } static const U64 prime7bytes = 58295818150454627ULL; -static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; } -static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); } +static size_t ZSTD_hash7(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-56)) * prime7bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash7PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash7(MEM_readLE64(p), h, s); } static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; -static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } -static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } +static size_t ZSTD_hash8(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u) * prime8bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash8PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash8(MEM_readLE64(p), h, s); } + MEM_STATIC FORCE_INLINE_ATTR size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) { + /* Although some of these hashes do support hBits up to 64, some do not. + * To be on the safe side, always avoid hBits > 32. */ + assert(hBits <= 32); + switch(mls) { default: @@ -798,6 +844,24 @@ } } +MEM_STATIC FORCE_INLINE_ATTR +size_t ZSTD_hashPtrSalted(const void* p, U32 hBits, U32 mls, const U64 hashSalt) { + /* Although some of these hashes do support hBits up to 64, some do not. + * To be on the safe side, always avoid hBits > 32. */ + assert(hBits <= 32); + + switch(mls) + { + default: + case 4: return ZSTD_hash4PtrS(p, hBits, (U32)hashSalt); + case 5: return ZSTD_hash5PtrS(p, hBits, hashSalt); + case 6: return ZSTD_hash6PtrS(p, hBits, hashSalt); + case 7: return ZSTD_hash7PtrS(p, hBits, hashSalt); + case 8: return ZSTD_hash8PtrS(p, hBits, hashSalt); + } +} + + /** ZSTD_ipow() : * Return base^exponent. */ @@ -884,9 +948,9 @@ MEM_STATIC U32 ZSTD_window_isEmpty(ZSTD_window_t const window) { - return window.dictLimit == 1 && - window.lowLimit == 1 && - (window.nextSrc - window.base) == 1; + return window.dictLimit == ZSTD_WINDOW_START_INDEX && + window.lowLimit == ZSTD_WINDOW_START_INDEX && + (window.nextSrc - window.base) == ZSTD_WINDOW_START_INDEX; } /** @@ -937,7 +1001,9 @@ { U32 const cycleSize = 1u << cycleLog; U32 const curr = (U32)((BYTE const*)src - window.base); - U32 const minIndexToOverflowCorrect = cycleSize + MAX(maxDist, cycleSize); + U32 const minIndexToOverflowCorrect = cycleSize + + MAX(maxDist, cycleSize) + + ZSTD_WINDOW_START_INDEX; /* Adjust the min index to backoff the overflow correction frequency, * so we don't waste too much CPU in overflow correction. If this @@ -1012,10 +1078,14 @@ U32 const cycleSize = 1u << cycleLog; U32 const cycleMask = cycleSize - 1; U32 const curr = (U32)((BYTE const*)src - window->base); - U32 const currentCycle0 = curr & cycleMask; - /* Exclude zero so that newCurrent - maxDist >= 1. */ - U32 const currentCycle1 = currentCycle0 == 0 ? cycleSize : currentCycle0; - U32 const newCurrent = currentCycle1 + MAX(maxDist, cycleSize); + U32 const currentCycle = curr & cycleMask; + /* Ensure newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX. */ + U32 const currentCycleCorrection = currentCycle < ZSTD_WINDOW_START_INDEX + ? MAX(cycleSize, ZSTD_WINDOW_START_INDEX) + : 0; + U32 const newCurrent = currentCycle + + currentCycleCorrection + + MAX(maxDist, cycleSize); U32 const correction = curr - newCurrent; /* maxDist must be a power of two so that: * (newCurrent & cycleMask) == (curr & cycleMask) @@ -1031,14 +1101,20 @@ window->base += correction; window->dictBase += correction; - if (window->lowLimit <= correction) window->lowLimit = 1; - else window->lowLimit -= correction; - if (window->dictLimit <= correction) window->dictLimit = 1; - else window->dictLimit -= correction; + if (window->lowLimit < correction + ZSTD_WINDOW_START_INDEX) { + window->lowLimit = ZSTD_WINDOW_START_INDEX; + } else { + window->lowLimit -= correction; + } + if (window->dictLimit < correction + ZSTD_WINDOW_START_INDEX) { + window->dictLimit = ZSTD_WINDOW_START_INDEX; + } else { + window->dictLimit -= correction; + } /* Ensure we can still reference the full window. */ assert(newCurrent >= maxDist); - assert(newCurrent - maxDist >= 1); + assert(newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX); /* Ensure that lowLimit and dictLimit didn't underflow. */ assert(window->lowLimit <= newCurrent); assert(window->dictLimit <= newCurrent); @@ -1133,10 +1209,15 @@ (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd); assert(blockEndIdx >= loadedDictEnd); - if (blockEndIdx > loadedDictEnd + maxDist) { + if (blockEndIdx > loadedDictEnd + maxDist || loadedDictEnd != window->dictLimit) { /* On reaching window size, dictionaries are invalidated. * For simplification, if window size is reached anywhere within next block, * the dictionary is invalidated for the full block. + * + * We also have to invalidate the dictionary if ZSTD_window_update() has detected + * non-contiguous segments, which means that loadedDictEnd != window->dictLimit. + * loadedDictEnd may be 0, if forceWindow is true, but in that case we never use + * dictMatchState, so setting it to NULL is not a problem. */ DEBUGLOG(6, "invalidating dictionary for current block (distance > windowSize)"); *loadedDictEndPtr = 0; @@ -1149,11 +1230,12 @@ MEM_STATIC void ZSTD_window_init(ZSTD_window_t* window) { ZSTD_memset(window, 0, sizeof(*window)); - window->base = (BYTE const*)""; - window->dictBase = (BYTE const*)""; - window->dictLimit = 1; /* start from 1, so that 1st position is valid */ - window->lowLimit = 1; /* it ensures first and later CCtx usages compress the same */ - window->nextSrc = window->base + 1; /* see issue #1241 */ + window->base = (BYTE const*)" "; + window->dictBase = (BYTE const*)" "; + ZSTD_STATIC_ASSERT(ZSTD_DUBT_UNSORTED_MARK < ZSTD_WINDOW_START_INDEX); /* Start above ZSTD_DUBT_UNSORTED_MARK */ + window->dictLimit = ZSTD_WINDOW_START_INDEX; /* start from >0, so that 1st position is valid */ + window->lowLimit = ZSTD_WINDOW_START_INDEX; /* it ensures first and later CCtx usages compress the same */ + window->nextSrc = window->base + ZSTD_WINDOW_START_INDEX; /* see issue #1241 */ window->nbOverflowCorrections = 0; } @@ -1206,15 +1288,15 @@ */ MEM_STATIC U32 ZSTD_getLowestMatchIndex(const ZSTD_matchState_t* ms, U32 curr, unsigned windowLog) { - U32 const maxDistance = 1U << windowLog; - U32 const lowestValid = ms->window.lowLimit; - U32 const withinWindow = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid; - U32 const isDictionary = (ms->loadedDictEnd != 0); + U32 const maxDistance = 1U << windowLog; + U32 const lowestValid = ms->window.lowLimit; + U32 const withinWindow = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid; + U32 const isDictionary = (ms->loadedDictEnd != 0); /* When using a dictionary the entire dictionary is valid if a single byte of the dictionary * is within the window. We invalidate the dictionary (and set loadedDictEnd to 0) when it isn't * valid for the entire block. So this check is sufficient to find the lowest valid match index. */ - U32 const matchLowest = isDictionary ? lowestValid : withinWindow; + U32 const matchLowest = isDictionary ? lowestValid : withinWindow; return matchLowest; } @@ -1267,6 +1349,42 @@ #endif +/* Short Cache */ + +/* Normally, zstd matchfinders follow this flow: + * 1. Compute hash at ip + * 2. Load index from hashTable[hash] + * 3. Check if *ip == *(base + index) + * In dictionary compression, loading *(base + index) is often an L2 or even L3 miss. + * + * Short cache is an optimization which allows us to avoid step 3 most of the time + * when the data doesn't actually match. With short cache, the flow becomes: + * 1. Compute (hash, currentTag) at ip. currentTag is an 8-bit independent hash at ip. + * 2. Load (index, matchTag) from hashTable[hash]. See ZSTD_writeTaggedIndex to understand how this works. + * 3. Only if currentTag == matchTag, check *ip == *(base + index). Otherwise, continue. + * + * Currently, short cache is only implemented in CDict hashtables. Thus, its use is limited to + * dictMatchState matchfinders. + */ +#define ZSTD_SHORT_CACHE_TAG_BITS 8 +#define ZSTD_SHORT_CACHE_TAG_MASK ((1u << ZSTD_SHORT_CACHE_TAG_BITS) - 1) + +/* Helper function for ZSTD_fillHashTable and ZSTD_fillDoubleHashTable. + * Unpacks hashAndTag into (hash, tag), then packs (index, tag) into hashTable[hash]. */ +MEM_STATIC void ZSTD_writeTaggedIndex(U32* const hashTable, size_t hashAndTag, U32 index) { + size_t const hash = hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS; + U32 const tag = (U32)(hashAndTag & ZSTD_SHORT_CACHE_TAG_MASK); + assert(index >> (32 - ZSTD_SHORT_CACHE_TAG_BITS) == 0); + hashTable[hash] = (index << ZSTD_SHORT_CACHE_TAG_BITS) | tag; +} + +/* Helper function for short cache matchfinders. + * Unpacks tag1 and tag2 from lower bits of packedTag1 and packedTag2, then checks if the tags match. */ +MEM_STATIC int ZSTD_comparePackedTags(size_t packedTag1, size_t packedTag2) { + U32 const tag1 = packedTag1 & ZSTD_SHORT_CACHE_TAG_MASK; + U32 const tag2 = packedTag2 & ZSTD_SHORT_CACHE_TAG_MASK; + return tag1 == tag2; +} #if defined (__cplusplus) } @@ -1364,4 +1482,51 @@ */ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize); +/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of + * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter. + * Note that the block delimiter must include the last literals of the block. + */ +size_t +ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, + ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); + +/* Returns the number of bytes to move the current read position back by. + * Only non-zero if we ended up splitting a sequence. + * Otherwise, it may return a ZSTD error if something went wrong. + * + * This function will attempt to scan through blockSize bytes + * represented by the sequences in @inSeqs, + * storing any (partial) sequences. + * + * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to + * avoid splitting a match, or to avoid splitting a match such that it would produce a match + * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. + */ +size_t +ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos, + const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, + const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); + + +/* =============================================================== + * Deprecated definitions that are still used internally to avoid + * deprecation warnings. These functions are exactly equivalent to + * their public variants, but avoid the deprecation warnings. + * =============================================================== */ + +size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); + +size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + +size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + +size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + + #endif /* ZSTD_COMPRESS_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_literals.c b/Utilities/cmzstd/lib/compress/zstd_compress_literals.c index 008337b..bfd4f11 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_literals.c +++ b/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -13,11 +13,36 @@ ***************************************/ #include "zstd_compress_literals.h" + +/* ************************************************************** +* Debug Traces +****************************************************************/ +#if DEBUGLEVEL >= 2 + +static size_t showHexa(const void* src, size_t srcSize) +{ + const BYTE* const ip = (const BYTE*)src; + size_t u; + for (u=0; u<srcSize; u++) { + RAWLOG(5, " %02X", ip[u]); (void)ip; + } + RAWLOG(5, " \n"); + return srcSize; +} + +#endif + + +/* ************************************************************** +* Literals compression - special cases +****************************************************************/ size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { BYTE* const ostart = (BYTE*)dst; U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); + DEBUGLOG(5, "ZSTD_noCompressLiterals: srcSize=%zu, dstCapacity=%zu", srcSize, dstCapacity); + RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, ""); switch(flSize) @@ -36,16 +61,30 @@ } ZSTD_memcpy(ostart + flSize, src, srcSize); - DEBUGLOG(5, "Raw literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize)); + DEBUGLOG(5, "Raw (uncompressed) literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize)); return srcSize + flSize; } +static int allBytesIdentical(const void* src, size_t srcSize) +{ + assert(srcSize >= 1); + assert(src != NULL); + { const BYTE b = ((const BYTE*)src)[0]; + size_t p; + for (p=1; p<srcSize; p++) { + if (((const BYTE*)src)[p] != b) return 0; + } + return 1; + } +} + size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { BYTE* const ostart = (BYTE*)dst; U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); - (void)dstCapacity; /* dstCapacity already guaranteed to be >=4, hence large enough */ + assert(dstCapacity >= 4); (void)dstCapacity; + assert(allBytesIdentical(src, srcSize)); switch(flSize) { @@ -63,27 +102,51 @@ } ostart[flSize] = *(const BYTE*)src; - DEBUGLOG(5, "RLE literals: %u -> %u", (U32)srcSize, (U32)flSize + 1); + DEBUGLOG(5, "RLE : Repeated Literal (%02X: %u times) -> %u bytes encoded", ((const BYTE*)src)[0], (U32)srcSize, (U32)flSize + 1); return flSize+1; } -size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, - ZSTD_hufCTables_t* nextHuf, - ZSTD_strategy strategy, int disableLiteralCompression, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - void* entropyWorkspace, size_t entropyWorkspaceSize, - const int bmi2) +/* ZSTD_minLiteralsToCompress() : + * returns minimal amount of literals + * for literal compression to even be attempted. + * Minimum is made tighter as compression strategy increases. + */ +static size_t +ZSTD_minLiteralsToCompress(ZSTD_strategy strategy, HUF_repeat huf_repeat) { - size_t const minGain = ZSTD_minGain(srcSize, strategy); + assert((int)strategy >= 0); + assert((int)strategy <= 9); + /* btultra2 : min 8 bytes; + * then 2x larger for each successive compression strategy + * max threshold 64 bytes */ + { int const shift = MIN(9-(int)strategy, 3); + size_t const mintc = (huf_repeat == HUF_repeat_valid) ? 6 : (size_t)8 << shift; + DEBUGLOG(7, "minLiteralsToCompress = %zu", mintc); + return mintc; + } +} + +size_t ZSTD_compressLiterals ( + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + void* entropyWorkspace, size_t entropyWorkspaceSize, + const ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_strategy strategy, + int disableLiteralCompression, + int suspectUncompressible, + int bmi2) +{ size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); BYTE* const ostart = (BYTE*)dst; U32 singleStream = srcSize < 256; symbolEncodingType_e hType = set_compressed; size_t cLitSize; - DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i srcSize=%u)", - disableLiteralCompression, (U32)srcSize); + DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i, srcSize=%u, dstCapacity=%zu)", + disableLiteralCompression, (U32)srcSize, dstCapacity); + + DEBUGLOG(6, "Completed literals listing (%zu bytes)", showHexa(src, srcSize)); /* Prepare nextEntropy assuming reusing the existing table */ ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); @@ -91,40 +154,51 @@ if (disableLiteralCompression) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - /* small ? don't even attempt compression (speed opt) */ -# define COMPRESS_LITERALS_SIZE_MIN 63 - { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; - if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - } + /* if too small, don't even attempt compression (speed opt) */ + if (srcSize < ZSTD_minLiteralsToCompress(strategy, prevHuf->repeatMode)) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression"); { HUF_repeat repeat = prevHuf->repeatMode; - int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; + int const flags = 0 + | (bmi2 ? HUF_flags_bmi2 : 0) + | (strategy < ZSTD_lazy && srcSize <= 1024 ? HUF_flags_preferRepeat : 0) + | (strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD ? HUF_flags_optimalDepth : 0) + | (suspectUncompressible ? HUF_flags_suspectUncompressible : 0); + + typedef size_t (*huf_compress_f)(void*, size_t, const void*, size_t, unsigned, unsigned, void*, size_t, HUF_CElt*, HUF_repeat*, int); + huf_compress_f huf_compress; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; - cLitSize = singleStream ? - HUF_compress1X_repeat( - ostart+lhSize, dstCapacity-lhSize, src, srcSize, - HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, - (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2) : - HUF_compress4X_repeat( - ostart+lhSize, dstCapacity-lhSize, src, srcSize, - HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize, - (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2); + huf_compress = singleStream ? HUF_compress1X_repeat : HUF_compress4X_repeat; + cLitSize = huf_compress(ostart+lhSize, dstCapacity-lhSize, + src, srcSize, + HUF_SYMBOLVALUE_MAX, LitHufLog, + entropyWorkspace, entropyWorkspaceSize, + (HUF_CElt*)nextHuf->CTable, + &repeat, flags); + DEBUGLOG(5, "%zu literals compressed into %zu bytes (before header)", srcSize, cLitSize); if (repeat != HUF_repeat_none) { /* reused the existing table */ - DEBUGLOG(5, "Reusing previous huffman table"); + DEBUGLOG(5, "reusing statistics from previous huffman block"); hType = set_repeat; } } - if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) { - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - } + { size_t const minGain = ZSTD_minGain(srcSize, strategy); + if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) { + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + } } if (cLitSize==1) { - ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); - return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); - } + /* A return value of 1 signals that the alphabet consists of a single symbol. + * However, in some rare circumstances, it could be the compressed size (a single byte). + * For that outcome to have a chance to happen, it's necessary that `srcSize < 8`. + * (it's also necessary to not generate statistics). + * Therefore, in such a case, actively check that all bytes are identical. */ + if ((srcSize >= 8) || allBytesIdentical(src, srcSize)) { + ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); + return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); + } } if (hType == set_compressed) { /* using a newly constructed table */ @@ -135,16 +209,19 @@ switch(lhSize) { case 3: /* 2 - 2 - 10 - 10 */ - { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14); + if (!singleStream) assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS); + { U32 const lhc = hType + ((U32)(!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14); MEM_writeLE24(ostart, lhc); break; } case 4: /* 2 - 2 - 14 - 14 */ + assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS); { U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18); MEM_writeLE32(ostart, lhc); break; } case 5: /* 2 - 2 - 18 - 18 */ + assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS); { U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22); MEM_writeLE32(ostart, lhc); ostart[4] = (BYTE)(cLitSize >> 10);
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_literals.h b/Utilities/cmzstd/lib/compress/zstd_compress_literals.h index 9904c0c..b060c8a 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_literals.h +++ b/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -16,14 +16,24 @@ size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize); +/* ZSTD_compressRleLiteralsBlock() : + * Conditions : + * - All bytes in @src are identical + * - dstCapacity >= 4 */ size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize); -size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, - ZSTD_hufCTables_t* nextHuf, - ZSTD_strategy strategy, int disableLiteralCompression, - void* dst, size_t dstCapacity, +/* ZSTD_compressLiterals(): + * @entropyWorkspace: must be aligned on 4-bytes boundaries + * @entropyWorkspaceSize : must be >= HUF_WORKSPACE_SIZE + * @suspectUncompressible: sampling checks, to potentially skip huffman coding + */ +size_t ZSTD_compressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize, void* entropyWorkspace, size_t entropyWorkspaceSize, - const int bmi2); + const ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_strategy strategy, int disableLiteralCompression, + int suspectUncompressible, + int bmi2); #endif /* ZSTD_COMPRESS_LITERALS_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c index 611eabd..8872d4d 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c +++ b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -58,7 +58,7 @@ { /* Heuristic: This should cover most blocks <= 16K and * start to fade out after 16K to about 32K depending on - * comprssibility. + * compressibility. */ return nbSeq >= 2048; } @@ -166,7 +166,7 @@ if (mostFrequent == nbSeq) { *repeatMode = FSE_repeat_none; if (isDefaultAllowed && nbSeq <= 2) { - /* Prefer set_basic over set_rle when there are 2 or less symbols, + /* Prefer set_basic over set_rle when there are 2 or fewer symbols, * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. * If basic encoding isn't possible, always choose RLE. */ @@ -275,10 +275,11 @@ assert(nbSeq_1 > 1); assert(entropyWorkspaceSize >= sizeof(ZSTD_BuildCTableWksp)); (void)entropyWorkspaceSize; - FORWARD_IF_ERROR(FSE_normalizeCount(wksp->norm, tableLog, count, nbSeq_1, max, ZSTD_useLowProbCount(nbSeq_1)), ""); - { size_t const NCountSize = FSE_writeNCount(op, oend - op, wksp->norm, max, tableLog); /* overflow protected */ + FORWARD_IF_ERROR(FSE_normalizeCount(wksp->norm, tableLog, count, nbSeq_1, max, ZSTD_useLowProbCount(nbSeq_1)), "FSE_normalizeCount failed"); + assert(oend >= op); + { size_t const NCountSize = FSE_writeNCount(op, (size_t)(oend - op), wksp->norm, max, tableLog); /* overflow protected */ FORWARD_IF_ERROR(NCountSize, "FSE_writeNCount failed"); - FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, wksp->norm, max, tableLog, wksp->wksp, sizeof(wksp->wksp)), ""); + FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, wksp->norm, max, tableLog, wksp->wksp, sizeof(wksp->wksp)), "FSE_buildCTable_wksp failed"); return NCountSize; } } @@ -312,19 +313,19 @@ FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); + BIT_addBits(&blockStream, sequences[nbSeq-1].mlBase, ML_bits[mlCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); if (longOffsets) { U32 const ofBits = ofCodeTable[nbSeq-1]; unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); if (extraBits) { - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); + BIT_addBits(&blockStream, sequences[nbSeq-1].offBase, extraBits); BIT_flushBits(&blockStream); } - BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, + BIT_addBits(&blockStream, sequences[nbSeq-1].offBase >> extraBits, ofBits - extraBits); } else { - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); + BIT_addBits(&blockStream, sequences[nbSeq-1].offBase, ofCodeTable[nbSeq-1]); } BIT_flushBits(&blockStream); @@ -338,8 +339,8 @@ U32 const mlBits = ML_bits[mlCode]; DEBUGLOG(6, "encoding: litlen:%2u - matchlen:%2u - offCode:%7u", (unsigned)sequences[n].litLength, - (unsigned)sequences[n].matchLength + MINMATCH, - (unsigned)sequences[n].offset); + (unsigned)sequences[n].mlBase + MINMATCH, + (unsigned)sequences[n].offBase); /* 32b*/ /* 64b*/ /* (7)*/ /* (7)*/ FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */ @@ -350,18 +351,18 @@ BIT_flushBits(&blockStream); /* (7)*/ BIT_addBits(&blockStream, sequences[n].litLength, llBits); if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); + BIT_addBits(&blockStream, sequences[n].mlBase, mlBits); if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream); if (longOffsets) { unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); if (extraBits) { - BIT_addBits(&blockStream, sequences[n].offset, extraBits); + BIT_addBits(&blockStream, sequences[n].offBase, extraBits); BIT_flushBits(&blockStream); /* (7)*/ } - BIT_addBits(&blockStream, sequences[n].offset >> extraBits, + BIT_addBits(&blockStream, sequences[n].offBase >> extraBits, ofBits - extraBits); /* 31 */ } else { - BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ + BIT_addBits(&blockStream, sequences[n].offBase, ofBits); /* 31 */ } BIT_flushBits(&blockStream); /* (7)*/ DEBUGLOG(7, "remaining space : %i", (int)(blockStream.endPtr - blockStream.ptr)); @@ -398,7 +399,7 @@ #if DYNAMIC_BMI2 -static TARGET_ATTRIBUTE("bmi2") size_t +static BMI2_TARGET_ATTRIBUTE size_t ZSTD_encodeSequences_bmi2( void* dst, size_t dstCapacity, FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h index 7991364..4a3a05d 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h +++ b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c index e4e4506..638c4ac 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c +++ b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -36,13 +36,14 @@ * If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block * and the following sub-blocks' literals sections will be Treeless_Literals_Block. * @return : compressed size of literals section of a sub-block - * Or 0 if it unable to compress. + * Or 0 if unable to compress. * Or error code */ -static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, - const ZSTD_hufCTablesMetadata_t* hufMetadata, - const BYTE* literals, size_t litSize, - void* dst, size_t dstSize, - const int bmi2, int writeEntropy, int* entropyWritten) +static size_t +ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable, + const ZSTD_hufCTablesMetadata_t* hufMetadata, + const BYTE* literals, size_t litSize, + void* dst, size_t dstSize, + const int bmi2, int writeEntropy, int* entropyWritten) { size_t const header = writeEntropy ? 200 : 0; size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header)); @@ -53,8 +54,6 @@ symbolEncodingType_e hType = writeEntropy ? hufMetadata->hType : set_repeat; size_t cLitSize = 0; - (void)bmi2; /* TODO bmi2... */ - DEBUGLOG(5, "ZSTD_compressSubBlock_literal (litSize=%zu, lhSize=%zu, writeEntropy=%d)", litSize, lhSize, writeEntropy); *entropyWritten = 0; @@ -76,9 +75,9 @@ DEBUGLOG(5, "ZSTD_compressSubBlock_literal (hSize=%zu)", hufMetadata->hufDesSize); } - /* TODO bmi2 */ - { const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable) - : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable); + { int const flags = bmi2 ? HUF_flags_bmi2 : 0; + const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable, flags) + : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable, flags); op += cSize; cLitSize += cSize; if (cSize == 0 || ERR_isError(cSize)) { @@ -126,12 +125,17 @@ return op-ostart; } -static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef* sequences, size_t nbSeq, size_t litSize, int lastSequence) { +static size_t +ZSTD_seqDecompressedSize(seqStore_t const* seqStore, + const seqDef* sequences, size_t nbSeq, + size_t litSize, int lastSequence) +{ const seqDef* const sstart = sequences; const seqDef* const send = sequences + nbSeq; const seqDef* sp = sstart; size_t matchLengthSum = 0; size_t litLengthSum = 0; + (void)(litLengthSum); /* suppress unused variable warning on some environments */ while (send-sp > 0) { ZSTD_sequenceLength const seqLen = ZSTD_getSequenceLength(seqStore, sp); litLengthSum += seqLen.litLength; @@ -155,13 +159,14 @@ * @return : compressed size of sequences section of a sub-block * Or 0 if it is unable to compress * Or error code. */ -static size_t ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables, - const ZSTD_fseCTablesMetadata_t* fseMetadata, - const seqDef* sequences, size_t nbSeq, - const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, - const ZSTD_CCtx_params* cctxParams, - void* dst, size_t dstCapacity, - const int bmi2, int writeEntropy, int* entropyWritten) +static size_t +ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables, + const ZSTD_fseCTablesMetadata_t* fseMetadata, + const seqDef* sequences, size_t nbSeq, + const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode, + const ZSTD_CCtx_params* cctxParams, + void* dst, size_t dstCapacity, + const int bmi2, int writeEntropy, int* entropyWritten) { const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; BYTE* const ostart = (BYTE*)dst; @@ -324,7 +329,7 @@ static size_t ZSTD_estimateSubBlockSize_symbolType(symbolEncodingType_e type, const BYTE* codeTable, unsigned maxCode, size_t nbSeq, const FSE_CTable* fseCTable, - const U32* additionalBits, + const U8* additionalBits, short const* defaultNorm, U32 defaultNormLog, U32 defaultMax, void* workspace, size_t wkspSize) { @@ -474,7 +479,7 @@ /* I think there is an optimization opportunity here. * Calling ZSTD_estimateSubBlockSize for every sequence can be wasteful * since it recalculates estimate from scratch. - * For example, it would recount literal distribution and symbol codes everytime. + * For example, it would recount literal distribution and symbol codes every time. */ cBlockSizeEstimate = ZSTD_estimateSubBlockSize(lp, litSize, ofCodePtr, llCodePtr, mlCodePtr, seqCount, &nextCBlock->entropy, entropyMetadata, @@ -538,7 +543,7 @@ repcodes_t rep; ZSTD_memcpy(&rep, prevCBlock->rep, sizeof(rep)); for (seq = sstart; seq < sp; ++seq) { - rep = ZSTD_updateRep(rep.rep, seq->offset - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); + ZSTD_updateRep(rep.rep, seq->offBase, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0); } ZSTD_memcpy(nextCBlock->rep, &rep, sizeof(rep)); }
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h index 176f9b1..8e494f0 100644 --- a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h +++ b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_cwksp.h b/Utilities/cmzstd/lib/compress/zstd_cwksp.h index 2656d26..cc7fb1c 100644 --- a/Utilities/cmzstd/lib/compress/zstd_cwksp.h +++ b/Utilities/cmzstd/lib/compress/zstd_cwksp.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,7 +14,9 @@ /*-************************************* * Dependencies ***************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "../common/zstd_internal.h" +#include "../common/portability_macros.h" #if defined (__cplusplus) extern "C" { @@ -44,8 +46,9 @@ ***************************************/ typedef enum { ZSTD_cwksp_alloc_objects, - ZSTD_cwksp_alloc_buffers, - ZSTD_cwksp_alloc_aligned + ZSTD_cwksp_alloc_aligned_init_once, + ZSTD_cwksp_alloc_aligned, + ZSTD_cwksp_alloc_buffers } ZSTD_cwksp_alloc_phase_e; /** @@ -98,8 +101,8 @@ * * Workspace Layout: * - * [ ... workspace ... ] - * [objects][tables ... ->] free space [<- ... aligned][<- ... buffers] + * [ ... workspace ... ] + * [objects][tables ->] free space [<- buffers][<- aligned][<- init once] * * The various objects that live in the workspace are divided into the * following categories, and are allocated separately: @@ -123,9 +126,18 @@ * uint32_t arrays, all of whose values are between 0 and (nextSrc - base). * Their sizes depend on the cparams. These tables are 64-byte aligned. * - * - Aligned: these buffers are used for various purposes that require 4 byte - * alignment, but don't require any initialization before they're used. These - * buffers are each aligned to 64 bytes. + * - Init once: these buffers require to be initialized at least once before + * use. They should be used when we want to skip memory initialization + * while not triggering memory checkers (like Valgrind) when reading from + * from this memory without writing to it first. + * These buffers should be used carefully as they might contain data + * from previous compressions. + * Buffers are aligned to 64 bytes. + * + * - Aligned: these buffers don't require any initialization before they're + * used. The user of the buffer should make sure they write into a buffer + * location before reading from it. + * Buffers are aligned to 64 bytes. * * - Buffers: these buffers are used for various purposes that don't require * any alignment or initialization before they're used. This means they can @@ -137,8 +149,9 @@ * correctly packed into the workspace buffer. That order is: * * 1. Objects - * 2. Buffers - * 3. Aligned/Tables + * 2. Init once / Tables + * 3. Aligned / Tables + * 4. Buffers / Tables * * Attempts to reserve objects of different types out of order will fail. */ @@ -150,6 +163,7 @@ void* tableEnd; void* tableValidEnd; void* allocStart; + void* initOnceStart; BYTE allocFailed; int workspaceOversizedDuration; @@ -162,6 +176,7 @@ ***************************************/ MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws); +MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws); MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) { (void)ws; @@ -171,6 +186,20 @@ assert(ws->tableEnd <= ws->allocStart); assert(ws->tableValidEnd <= ws->allocStart); assert(ws->allocStart <= ws->workspaceEnd); + assert(ws->initOnceStart <= ZSTD_cwksp_initialAllocStart(ws)); + assert(ws->workspace <= ws->initOnceStart); +#if ZSTD_MEMORY_SANITIZER + { + intptr_t const offset = __msan_test_shadow(ws->initOnceStart, + (U8*)ZSTD_cwksp_initialAllocStart(ws) - (U8*)ws->initOnceStart); +#if defined(ZSTD_MSAN_PRINT) + if(offset!=-1) { + __msan_print_shadow((U8*)ws->initOnceStart + offset - 8, 32); + } +#endif + assert(offset==-1); + }; +#endif } /** @@ -217,14 +246,10 @@ * for internal purposes (currently only alignment). */ MEM_STATIC size_t ZSTD_cwksp_slack_space_required(void) { - /* For alignment, the wksp will always allocate an additional n_1=[1, 64] bytes - * to align the beginning of tables section, as well as another n_2=[0, 63] bytes - * to align the beginning of the aligned secion. - * - * n_1 + n_2 == 64 bytes if the cwksp is freshly allocated, due to tables and - * aligneds being sized in multiples of 64 bytes. + /* For alignment, the wksp will always allocate an additional 2*ZSTD_CWKSP_ALIGNMENT_BYTES + * bytes to align the beginning of tables section and end of buffers; */ - size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES; + size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES * 2; return slackSpace; } @@ -237,18 +262,28 @@ size_t const alignBytesMask = alignBytes - 1; size_t const bytes = (alignBytes - ((size_t)ptr & (alignBytesMask))) & alignBytesMask; assert((alignBytes & alignBytesMask) == 0); - assert(bytes != ZSTD_CWKSP_ALIGNMENT_BYTES); + assert(bytes < alignBytes); return bytes; } /** + * Returns the initial value for allocStart which is used to determine the position from + * which we can allocate from the end of the workspace. + */ +MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws) { + return (void*)((size_t)ws->workspaceEnd & ~(ZSTD_CWKSP_ALIGNMENT_BYTES-1)); +} + +/** * Internal function. Do not use directly. - * Reserves the given number of bytes within the aligned/buffer segment of the wksp, which - * counts from the end of the wksp. (as opposed to the object/table segment) + * Reserves the given number of bytes within the aligned/buffer segment of the wksp, + * which counts from the end of the wksp (as opposed to the object/table segment). * * Returns a pointer to the beginning of that space. */ -MEM_STATIC void* ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, size_t const bytes) { +MEM_STATIC void* +ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, size_t const bytes) +{ void* const alloc = (BYTE*)ws->allocStart - bytes; void* const bottom = ws->tableEnd; DEBUGLOG(5, "cwksp: reserving %p %zd bytes, %zd bytes remaining", @@ -260,6 +295,8 @@ ws->allocFailed = 1; return NULL; } + /* the area is reserved from the end of wksp. + * If it overlaps with tableValidEnd, it voids guarantees on values' range */ if (alloc < ws->tableValidEnd) { ws->tableValidEnd = alloc; } @@ -269,39 +306,32 @@ /** * Moves the cwksp to the next phase, and does any necessary allocations. + * cwksp initialization must necessarily go through each phase in order. * Returns a 0 on success, or zstd error */ -MEM_STATIC size_t ZSTD_cwksp_internal_advance_phase( - ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase) { +MEM_STATIC size_t +ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase) +{ assert(phase >= ws->phase); if (phase > ws->phase) { - /* Going from allocating objects to allocating buffers */ - if (ws->phase < ZSTD_cwksp_alloc_buffers && - phase >= ZSTD_cwksp_alloc_buffers) { + /* Going from allocating objects to allocating initOnce / tables */ + if (ws->phase < ZSTD_cwksp_alloc_aligned_init_once && + phase >= ZSTD_cwksp_alloc_aligned_init_once) { ws->tableValidEnd = ws->objectEnd; - } + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); - /* Going from allocating buffers to allocating aligneds/tables */ - if (ws->phase < ZSTD_cwksp_alloc_aligned && - phase >= ZSTD_cwksp_alloc_aligned) { - { /* Align the start of the "aligned" to 64 bytes. Use [1, 64] bytes. */ - size_t const bytesToAlign = - ZSTD_CWKSP_ALIGNMENT_BYTES - ZSTD_cwksp_bytes_to_align_ptr(ws->allocStart, ZSTD_CWKSP_ALIGNMENT_BYTES); - DEBUGLOG(5, "reserving aligned alignment addtl space: %zu", bytesToAlign); - ZSTD_STATIC_ASSERT((ZSTD_CWKSP_ALIGNMENT_BYTES & (ZSTD_CWKSP_ALIGNMENT_BYTES - 1)) == 0); /* power of 2 */ - RETURN_ERROR_IF(!ZSTD_cwksp_reserve_internal_buffer_space(ws, bytesToAlign), - memory_allocation, "aligned phase - alignment initial allocation failed!"); - } { /* Align the start of the tables to 64 bytes. Use [0, 63] bytes */ - void* const alloc = ws->objectEnd; + void *const alloc = ws->objectEnd; size_t const bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, ZSTD_CWKSP_ALIGNMENT_BYTES); - void* const end = (BYTE*)alloc + bytesToAlign; + void *const objectEnd = (BYTE *) alloc + bytesToAlign; DEBUGLOG(5, "reserving table alignment addtl space: %zu", bytesToAlign); - RETURN_ERROR_IF(end > ws->workspaceEnd, memory_allocation, + RETURN_ERROR_IF(objectEnd > ws->workspaceEnd, memory_allocation, "table phase - alignment initial allocation failed!"); - ws->objectEnd = end; - ws->tableEnd = end; - ws->tableValidEnd = end; + ws->objectEnd = objectEnd; + ws->tableEnd = objectEnd; /* table area starts being empty */ + if (ws->tableValidEnd < ws->tableEnd) { + ws->tableValidEnd = ws->tableEnd; + } } } ws->phase = phase; @@ -313,15 +343,17 @@ /** * Returns whether this object/buffer/etc was allocated in this workspace. */ -MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) { - return (ptr != NULL) && (ws->workspace <= ptr) && (ptr <= ws->workspaceEnd); +MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) +{ + return (ptr != NULL) && (ws->workspace <= ptr) && (ptr < ws->workspaceEnd); } /** * Internal function. Do not use directly. */ -MEM_STATIC void* ZSTD_cwksp_reserve_internal( - ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase) { +MEM_STATIC void* +ZSTD_cwksp_reserve_internal(ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase) +{ void* alloc; if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase)) || bytes == 0) { return NULL; @@ -340,7 +372,9 @@ if (alloc) { alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) { - __asan_unpoison_memory_region(alloc, bytes); + /* We need to keep the redzone poisoned while unpoisoning the bytes that + * are actually allocated. */ + __asan_unpoison_memory_region(alloc, bytes - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE); } } #endif @@ -351,14 +385,46 @@ /** * Reserves and returns unaligned memory. */ -MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes) { +MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes) +{ return (BYTE*)ZSTD_cwksp_reserve_internal(ws, bytes, ZSTD_cwksp_alloc_buffers); } /** * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). + * This memory has been initialized at least once in the past. + * This doesn't mean it has been initialized this time, and it might contain data from previous + * operations. + * The main usage is for algorithms that might need read access into uninitialized memory. + * The algorithm must maintain safety under these conditions and must make sure it doesn't + * leak any of the past data (directly or in side channels). */ -MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) { +MEM_STATIC void* ZSTD_cwksp_reserve_aligned_init_once(ZSTD_cwksp* ws, size_t bytes) +{ + size_t const alignedBytes = ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES); + void* ptr = ZSTD_cwksp_reserve_internal(ws, alignedBytes, ZSTD_cwksp_alloc_aligned_init_once); + assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0); + if(ptr && ptr < ws->initOnceStart) { + /* We assume the memory following the current allocation is either: + * 1. Not usable as initOnce memory (end of workspace) + * 2. Another initOnce buffer that has been allocated before (and so was previously memset) + * 3. An ASAN redzone, in which case we don't want to write on it + * For these reasons it should be fine to not explicitly zero every byte up to ws->initOnceStart. + * Note that we assume here that MSAN and ASAN cannot run in the same time. */ + ZSTD_memset(ptr, 0, MIN((size_t)((U8*)ws->initOnceStart - (U8*)ptr), alignedBytes)); + ws->initOnceStart = ptr; + } +#if ZSTD_MEMORY_SANITIZER + assert(__msan_test_shadow(ptr, bytes) == -1); +#endif + return ptr; +} + +/** + * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). + */ +MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) +{ void* ptr = ZSTD_cwksp_reserve_internal(ws, ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES), ZSTD_cwksp_alloc_aligned); assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0); @@ -370,14 +436,19 @@ * their values remain constrained, allowing us to re-use them without * memset()-ing them. */ -MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) { - const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned; +MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) +{ + const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned_init_once; void* alloc; void* end; void* top; - if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) { - return NULL; + /* We can only start allocating tables after we are done reserving space for objects at the + * start of the workspace */ + if(ws->phase < phase) { + if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) { + return NULL; + } } alloc = ws->tableEnd; end = (BYTE *)alloc + bytes; @@ -408,9 +479,11 @@ /** * Aligned on sizeof(void*). + * Note : should happen only once, at workspace first initialization */ -MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes) { - size_t roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*)); +MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes) +{ + size_t const roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*)); void* alloc = ws->objectEnd; void* end = (BYTE*)alloc + roundedBytes; @@ -419,15 +492,15 @@ end = (BYTE *)end + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE; #endif - DEBUGLOG(5, + DEBUGLOG(4, "cwksp: reserving %p object %zd bytes (rounded to %zd), %zd bytes remaining", alloc, bytes, roundedBytes, ZSTD_cwksp_available_space(ws) - roundedBytes); - assert(((size_t)alloc & (sizeof(void*)-1)) == 0); - assert((bytes & (sizeof(void*)-1)) == 0); + assert((size_t)alloc % ZSTD_ALIGNOF(void*) == 0); + assert(bytes % ZSTD_ALIGNOF(void*) == 0); ZSTD_cwksp_assert_internal_consistency(ws); /* we must be in the first phase, no advance is possible */ if (ws->phase != ZSTD_cwksp_alloc_objects || end > ws->workspaceEnd) { - DEBUGLOG(4, "cwksp: object alloc failed!"); + DEBUGLOG(3, "cwksp: object alloc failed!"); ws->allocFailed = 1; return NULL; } @@ -438,7 +511,7 @@ #if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on * either size. */ - alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; + alloc = (BYTE*)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) { __asan_unpoison_memory_region(alloc, bytes); } @@ -447,17 +520,26 @@ return alloc; } -MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) { +MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) +{ DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_dirty"); #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the table re-use logic is sound, and that we don't * access table space that we haven't cleaned, we re-"poison" the table - * space every time we mark it dirty. */ + * space every time we mark it dirty. + * Since tableValidEnd space and initOnce space may overlap we don't poison + * the initOnce portion as it break its promise. This means that this poisoning + * check isn't always applied fully. */ { size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd; assert(__msan_test_shadow(ws->objectEnd, size) == -1); - __msan_poison(ws->objectEnd, size); + if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) { + __msan_poison(ws->objectEnd, size); + } else { + assert(ws->initOnceStart >= ws->objectEnd); + __msan_poison(ws->objectEnd, (BYTE*)ws->initOnceStart - (BYTE*)ws->objectEnd); + } } #endif @@ -485,7 +567,7 @@ assert(ws->tableValidEnd >= ws->objectEnd); assert(ws->tableValidEnd <= ws->allocStart); if (ws->tableValidEnd < ws->tableEnd) { - ZSTD_memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd); + ZSTD_memset(ws->tableValidEnd, 0, (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd)); } ZSTD_cwksp_mark_tables_clean(ws); } @@ -522,11 +604,14 @@ #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the context re-use logic is sound, and that we don't * access stuff that this compression hasn't initialized, we re-"poison" - * the workspace (or at least the non-static, non-table parts of it) - * every time we start a new compression. */ + * the workspace except for the areas in which we expect memory re-use + * without initialization (objects, valid tables area and init once + * memory). */ { - size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->tableValidEnd; - __msan_poison(ws->tableValidEnd, size); + if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) { + size_t size = (BYTE*)ws->initOnceStart - (BYTE*)ws->tableValidEnd; + __msan_poison(ws->tableValidEnd, size); + } } #endif @@ -542,10 +627,10 @@ #endif ws->tableEnd = ws->objectEnd; - ws->allocStart = ws->workspaceEnd; + ws->allocStart = ZSTD_cwksp_initialAllocStart(ws); ws->allocFailed = 0; - if (ws->phase > ZSTD_cwksp_alloc_buffers) { - ws->phase = ZSTD_cwksp_alloc_buffers; + if (ws->phase > ZSTD_cwksp_alloc_aligned_init_once) { + ws->phase = ZSTD_cwksp_alloc_aligned_init_once; } ZSTD_cwksp_assert_internal_consistency(ws); } @@ -562,6 +647,7 @@ ws->workspaceEnd = (BYTE*)start + size; ws->objectEnd = ws->workspace; ws->tableValidEnd = ws->objectEnd; + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); ws->phase = ZSTD_cwksp_alloc_objects; ws->isStatic = isStatic; ZSTD_cwksp_clear(ws); @@ -614,17 +700,11 @@ * Returns if the estimated space needed for a wksp is within an acceptable limit of the * actual amount of space used. */ -MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp* const ws, - size_t const estimatedSpace, int resizedWorkspace) { - if (resizedWorkspace) { - /* Resized/newly allocated wksp should have exact bounds */ - return ZSTD_cwksp_used(ws) == estimatedSpace; - } else { - /* Due to alignment, when reusing a workspace, we can actually consume 63 fewer or more bytes - * than estimatedSpace. See the comments in zstd_cwksp.h for details. - */ - return (ZSTD_cwksp_used(ws) >= estimatedSpace - 63) && (ZSTD_cwksp_used(ws) <= estimatedSpace + 63); - } +MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp *const ws, size_t const estimatedSpace) { + /* We have an alignment space between objects and tables between tables and buffers, so we can have up to twice + * the alignment bytes difference between estimation and actual usage */ + return (estimatedSpace - ZSTD_cwksp_slack_space_required()) <= ZSTD_cwksp_used(ws) && + ZSTD_cwksp_used(ws) <= estimatedSpace; }
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.c b/Utilities/cmzstd/lib/compress/zstd_double_fast.c index d0d3a78..0ad88ff 100644 --- a/Utilities/cmzstd/lib/compress/zstd_double_fast.c +++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,8 +11,43 @@ #include "zstd_compress_internal.h" #include "zstd_double_fast.h" +static void ZSTD_fillDoubleHashTableForCDict(ZSTD_matchState_t* ms, + void const* end, ZSTD_dictTableLoadMethod_e dtlm) +{ + const ZSTD_compressionParameters* const cParams = &ms->cParams; + U32* const hashLarge = ms->hashTable; + U32 const hBitsL = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; + U32 const mls = cParams->minMatch; + U32* const hashSmall = ms->chainTable; + U32 const hBitsS = cParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS; + const BYTE* const base = ms->window.base; + const BYTE* ip = base + ms->nextToUpdate; + const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; + const U32 fastHashFillStep = 3; -void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, + /* Always insert every fastHashFillStep position into the hash tables. + * Insert the other positions into the large hash table if their entry + * is empty. + */ + for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) { + U32 const curr = (U32)(ip - base); + U32 i; + for (i = 0; i < fastHashFillStep; ++i) { + size_t const smHashAndTag = ZSTD_hashPtr(ip + i, hBitsS, mls); + size_t const lgHashAndTag = ZSTD_hashPtr(ip + i, hBitsL, 8); + if (i == 0) { + ZSTD_writeTaggedIndex(hashSmall, smHashAndTag, curr + i); + } + if (i == 0 || hashLarge[lgHashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) { + ZSTD_writeTaggedIndex(hashLarge, lgHashAndTag, curr + i); + } + /* Only load extra positions for ZSTD_dtlm_full */ + if (dtlm == ZSTD_dtlm_fast) + break; + } } +} + +static void ZSTD_fillDoubleHashTableForCCtx(ZSTD_matchState_t* ms, void const* end, ZSTD_dictTableLoadMethod_e dtlm) { const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -43,15 +78,26 @@ /* Only load extra positions for ZSTD_dtlm_full */ if (dtlm == ZSTD_dtlm_fast) break; - } } + } } +} + +void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, + const void* const end, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp) +{ + if (tfp == ZSTD_tfp_forCDict) { + ZSTD_fillDoubleHashTableForCDict(ms, end, dtlm); + } else { + ZSTD_fillDoubleHashTableForCCtx(ms, end, dtlm); + } } FORCE_INLINE_TEMPLATE -size_t ZSTD_compressBlock_doubleFast_generic( +size_t ZSTD_compressBlock_doubleFast_noDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - void const* src, size_t srcSize, - U32 const mls /* template */, ZSTD_dictMode_e const dictMode) + void const* src, size_t srcSize, U32 const mls /* template */) { ZSTD_compressionParameters const* cParams = &ms->cParams; U32* const hashLong = ms->hashTable; @@ -60,7 +106,6 @@ const U32 hBitsS = cParams->chainLog; const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); /* presumes that, if there is a dictionary, it must be using Attach mode */ @@ -69,188 +114,161 @@ const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; - U32 offsetSaved = 0; + U32 offsetSaved1 = 0, offsetSaved2 = 0; - const ZSTD_matchState_t* const dms = ms->dictMatchState; - const ZSTD_compressionParameters* const dictCParams = - dictMode == ZSTD_dictMatchState ? - &dms->cParams : NULL; - const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ? - dms->hashTable : NULL; - const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? - dms->chainTable : NULL; - const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ? - dms->window.dictLimit : 0; - const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? - dms->window.base : NULL; - const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ? - dictBase + dictStartIndex : NULL; - const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? - dms->window.nextSrc : NULL; - const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - prefixLowestIndex - (U32)(dictEnd - dictBase) : - 0; - const U32 dictHBitsL = dictMode == ZSTD_dictMatchState ? - dictCParams->hashLog : hBitsL; - const U32 dictHBitsS = dictMode == ZSTD_dictMatchState ? - dictCParams->chainLog : hBitsS; - const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictStart)); + size_t mLength; + U32 offset; + U32 curr; - DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_generic"); + /* how many positions to search before increasing step size */ + const size_t kStepIncr = 1 << kSearchStrength; + /* the position at which to increment the step size if no match is found */ + const BYTE* nextStep; + size_t step; /* the current step size */ - assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); + size_t hl0; /* the long hash at ip */ + size_t hl1; /* the long hash at ip1 */ - /* if a dictionary is attached, it must be within window range */ - if (dictMode == ZSTD_dictMatchState) { - assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex); - } + U32 idxl0; /* the long match index for ip */ + U32 idxl1; /* the long match index for ip1 */ + + const BYTE* matchl0; /* the long match for ip */ + const BYTE* matchs0; /* the short match for ip */ + const BYTE* matchl1; /* the long match for ip1 */ + + const BYTE* ip = istart; /* the current position */ + const BYTE* ip1; /* the next position */ + + DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_noDict_generic"); /* init */ - ip += (dictAndPrefixLength == 0); - if (dictMode == ZSTD_noDict) { - U32 const curr = (U32)(ip - base); - U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog); - U32 const maxRep = curr - windowLow; - if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; - if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; - } - if (dictMode == ZSTD_dictMatchState) { - /* dictMatchState repCode checks don't currently handle repCode == 0 - * disabling. */ - assert(offset_1 <= dictAndPrefixLength); - assert(offset_2 <= dictAndPrefixLength); + ip += ((ip - prefixLowest) == 0); + { + U32 const current = (U32)(ip - base); + U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog); + U32 const maxRep = current - windowLow; + if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0; + if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0; } - /* Main Search Loop */ - while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ - size_t mLength; - U32 offset; - size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); - size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); - size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8); - size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls); - U32 const curr = (U32)(ip-base); - U32 const matchIndexL = hashLong[h2]; - U32 matchIndexS = hashSmall[h]; - const BYTE* matchLong = base + matchIndexL; - const BYTE* match = base + matchIndexS; - const U32 repIndex = curr + 1 - offset_1; - const BYTE* repMatch = (dictMode == ZSTD_dictMatchState - && repIndex < prefixLowestIndex) ? - dictBase + (repIndex - dictIndexDelta) : - base + repIndex; - hashLong[h2] = hashSmall[h] = curr; /* update hash tables */ + /* Outer Loop: one iteration per match found and stored */ + while (1) { + step = 1; + nextStep = ip + kStepIncr; + ip1 = ip + step; - /* check dictMatchState repcode */ - if (dictMode == ZSTD_dictMatchState - && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; - ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); - goto _match_stored; + if (ip1 > ilimit) { + goto _cleanup; } - /* check noDict repcode */ - if ( dictMode == ZSTD_noDict - && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { - mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; - ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); - goto _match_stored; - } + hl0 = ZSTD_hashPtr(ip, hBitsL, 8); + idxl0 = hashLong[hl0]; + matchl0 = base + idxl0; - if (matchIndexL > prefixLowestIndex) { - /* check prefix long match */ - if (MEM_read64(matchLong) == MEM_read64(ip)) { - mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; - offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - goto _match_found; + /* Inner Loop: one iteration per search / position */ + do { + const size_t hs0 = ZSTD_hashPtr(ip, hBitsS, mls); + const U32 idxs0 = hashSmall[hs0]; + curr = (U32)(ip-base); + matchs0 = base + idxs0; + + hashLong[hl0] = hashSmall[hs0] = curr; /* update hash tables */ + + /* check noDict repcode */ + if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; + ip++; + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); + goto _match_stored; } - } else if (dictMode == ZSTD_dictMatchState) { - /* check dictMatchState long match */ - U32 const dictMatchIndexL = dictHashLong[dictHL]; - const BYTE* dictMatchL = dictBase + dictMatchIndexL; - assert(dictMatchL < dictEnd); - if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; - offset = (U32)(curr - dictMatchIndexL - dictIndexDelta); - while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ - goto _match_found; - } } + hl1 = ZSTD_hashPtr(ip1, hBitsL, 8); - if (matchIndexS > prefixLowestIndex) { - /* check prefix short match */ - if (MEM_read32(match) == MEM_read32(ip)) { - goto _search_next_long; + if (idxl0 > prefixLowestIndex) { + /* check prefix long match */ + if (MEM_read64(matchl0) == MEM_read64(ip)) { + mLength = ZSTD_count(ip+8, matchl0+8, iend) + 8; + offset = (U32)(ip-matchl0); + while (((ip>anchor) & (matchl0>prefixLowest)) && (ip[-1] == matchl0[-1])) { ip--; matchl0--; mLength++; } /* catch up */ + goto _match_found; + } } - } else if (dictMode == ZSTD_dictMatchState) { - /* check dictMatchState short match */ - U32 const dictMatchIndexS = dictHashSmall[dictHS]; - match = dictBase + dictMatchIndexS; - matchIndexS = dictMatchIndexS + dictIndexDelta; - if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) { - goto _search_next_long; - } } + idxl1 = hashLong[hl1]; + matchl1 = base + idxl1; - ip += ((ip-anchor) >> kSearchStrength) + 1; -#if defined(__aarch64__) - PREFETCH_L1(ip+256); -#endif - continue; + if (idxs0 > prefixLowestIndex) { + /* check prefix short match */ + if (MEM_read32(matchs0) == MEM_read32(ip)) { + goto _search_next_long; + } + } + + if (ip1 >= nextStep) { + PREFETCH_L1(ip1 + 64); + PREFETCH_L1(ip1 + 128); + step++; + nextStep += kStepIncr; + } + ip = ip1; + ip1 += step; + + hl0 = hl1; + idxl0 = idxl1; + matchl0 = matchl1; + #if defined(__aarch64__) + PREFETCH_L1(ip+256); + #endif + } while (ip1 <= ilimit); + +_cleanup: + /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0), + * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */ + offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2; + + /* save reps for next block */ + rep[0] = offset_1 ? offset_1 : offsetSaved1; + rep[1] = offset_2 ? offset_2 : offsetSaved2; + + /* Return the last literals size */ + return (size_t)(iend - anchor); _search_next_long: - { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8); - U32 const matchIndexL3 = hashLong[hl3]; - const BYTE* matchL3 = base + matchIndexL3; - hashLong[hl3] = curr + 1; - - /* check prefix long +1 match */ - if (matchIndexL3 > prefixLowestIndex) { - if (MEM_read64(matchL3) == MEM_read64(ip+1)) { - mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; - ip++; - offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ - goto _match_found; - } - } else if (dictMode == ZSTD_dictMatchState) { - /* check dict long +1 match */ - U32 const dictMatchIndexL3 = dictHashLong[dictHLNext]; - const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; - assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; - ip++; - offset = (U32)(curr + 1 - dictMatchIndexL3 - dictIndexDelta); - while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ - goto _match_found; - } } } + /* check prefix long +1 match */ + if (idxl1 > prefixLowestIndex) { + if (MEM_read64(matchl1) == MEM_read64(ip1)) { + ip = ip1; + mLength = ZSTD_count(ip+8, matchl1+8, iend) + 8; + offset = (U32)(ip-matchl1); + while (((ip>anchor) & (matchl1>prefixLowest)) && (ip[-1] == matchl1[-1])) { ip--; matchl1--; mLength++; } /* catch up */ + goto _match_found; + } + } /* if no long +1 match, explore the short match we found */ - if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) { - mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4; - offset = (U32)(curr - matchIndexS); - while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - offset = (U32)(ip - match); - while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - } + mLength = ZSTD_count(ip+4, matchs0+4, iend) + 4; + offset = (U32)(ip - matchs0); + while (((ip>anchor) & (matchs0>prefixLowest)) && (ip[-1] == matchs0[-1])) { ip--; matchs0--; mLength++; } /* catch up */ /* fall-through */ -_match_found: +_match_found: /* requires ip, offset, mLength */ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + if (step < 4) { + /* It is unsafe to write this value back to the hashtable when ip1 is + * greater than or equal to the new ip we will have after we're done + * processing this match. Rather than perform that test directly + * (ip1 >= ip + mLength), which costs speed in practice, we do a simpler + * more predictable test. The minmatch even if we take a short match is + * 4 bytes, so as long as step, the distance between ip and ip1 + * (initially) is less than 4, we know ip1 < new ip. */ + hashLong[hl1] = (U32)(ip1 - base); + } + + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); _match_stored: /* match found */ @@ -268,53 +286,268 @@ } /* check immediate repcode */ - if (dictMode == ZSTD_dictMatchState) { - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); - U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < prefixLowestIndex ? - dictBase + repIndex2 - dictIndexDelta : - base + repIndex2; - if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4; - U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); - hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; - hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; - ip += repLength2; - anchor = ip; - continue; - } - break; - } } + while ( (ip <= ilimit) + && ( (offset_2>0) + & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { + /* store sequence */ + size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; + U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, rLength); + ip += rLength; + anchor = ip; + continue; /* faster when present ... (?) */ + } + } + } +} - if (dictMode == ZSTD_noDict) { - while ( (ip <= ilimit) - && ( (offset_2>0) - & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { - /* store sequence */ - size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ - hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); - hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - continue; /* faster when present ... (?) */ + +FORCE_INLINE_TEMPLATE +size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + void const* src, size_t srcSize, + U32 const mls /* template */) +{ + ZSTD_compressionParameters const* cParams = &ms->cParams; + U32* const hashLong = ms->hashTable; + const U32 hBitsL = cParams->hashLog; + U32* const hashSmall = ms->chainTable; + const U32 hBitsS = cParams->chainLog; + const BYTE* const base = ms->window.base; + const BYTE* const istart = (const BYTE*)src; + const BYTE* ip = istart; + const BYTE* anchor = istart; + const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); + /* presumes that, if there is a dictionary, it must be using Attach mode */ + const U32 prefixLowestIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); + const BYTE* const prefixLowest = base + prefixLowestIndex; + const BYTE* const iend = istart + srcSize; + const BYTE* const ilimit = iend - HASH_READ_SIZE; + U32 offset_1=rep[0], offset_2=rep[1]; + + const ZSTD_matchState_t* const dms = ms->dictMatchState; + const ZSTD_compressionParameters* const dictCParams = &dms->cParams; + const U32* const dictHashLong = dms->hashTable; + const U32* const dictHashSmall = dms->chainTable; + const U32 dictStartIndex = dms->window.dictLimit; + const BYTE* const dictBase = dms->window.base; + const BYTE* const dictStart = dictBase + dictStartIndex; + const BYTE* const dictEnd = dms->window.nextSrc; + const U32 dictIndexDelta = prefixLowestIndex - (U32)(dictEnd - dictBase); + const U32 dictHBitsL = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; + const U32 dictHBitsS = dictCParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS; + const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictStart)); + + DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_dictMatchState_generic"); + + /* if a dictionary is attached, it must be within window range */ + assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex); + + if (ms->prefetchCDictTables) { + size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32); + size_t const chainTableBytes = (((size_t)1) << dictCParams->chainLog) * sizeof(U32); + PREFETCH_AREA(dictHashLong, hashTableBytes) + PREFETCH_AREA(dictHashSmall, chainTableBytes) + } + + /* init */ + ip += (dictAndPrefixLength == 0); + + /* dictMatchState repCode checks don't currently handle repCode == 0 + * disabling. */ + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); + + /* Main Search Loop */ + while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ + size_t mLength; + U32 offset; + size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); + size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); + size_t const dictHashAndTagL = ZSTD_hashPtr(ip, dictHBitsL, 8); + size_t const dictHashAndTagS = ZSTD_hashPtr(ip, dictHBitsS, mls); + U32 const dictMatchIndexAndTagL = dictHashLong[dictHashAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS]; + U32 const dictMatchIndexAndTagS = dictHashSmall[dictHashAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS]; + int const dictTagsMatchL = ZSTD_comparePackedTags(dictMatchIndexAndTagL, dictHashAndTagL); + int const dictTagsMatchS = ZSTD_comparePackedTags(dictMatchIndexAndTagS, dictHashAndTagS); + U32 const curr = (U32)(ip-base); + U32 const matchIndexL = hashLong[h2]; + U32 matchIndexS = hashSmall[h]; + const BYTE* matchLong = base + matchIndexL; + const BYTE* match = base + matchIndexS; + const U32 repIndex = curr + 1 - offset_1; + const BYTE* repMatch = (repIndex < prefixLowestIndex) ? + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; + hashLong[h2] = hashSmall[h] = curr; /* update hash tables */ + + /* check repcode */ + if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; + ip++; + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); + goto _match_stored; + } + + if (matchIndexL > prefixLowestIndex) { + /* check prefix long match */ + if (MEM_read64(matchLong) == MEM_read64(ip)) { + mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; + offset = (U32)(ip-matchLong); + while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + goto _match_found; + } + } else if (dictTagsMatchL) { + /* check dictMatchState long match */ + U32 const dictMatchIndexL = dictMatchIndexAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + assert(dictMatchL < dictEnd); + + if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; + offset = (U32)(curr - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + goto _match_found; + } } + + if (matchIndexS > prefixLowestIndex) { + /* check prefix short match */ + if (MEM_read32(match) == MEM_read32(ip)) { + goto _search_next_long; + } + } else if (dictTagsMatchS) { + /* check dictMatchState short match */ + U32 const dictMatchIndexS = dictMatchIndexAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS; + match = dictBase + dictMatchIndexS; + matchIndexS = dictMatchIndexS + dictIndexDelta; + + if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) { + goto _search_next_long; + } } + + ip += ((ip-anchor) >> kSearchStrength) + 1; +#if defined(__aarch64__) + PREFETCH_L1(ip+256); +#endif + continue; + +_search_next_long: + { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + size_t const dictHashAndTagL3 = ZSTD_hashPtr(ip+1, dictHBitsL, 8); + U32 const matchIndexL3 = hashLong[hl3]; + U32 const dictMatchIndexAndTagL3 = dictHashLong[dictHashAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS]; + int const dictTagsMatchL3 = ZSTD_comparePackedTags(dictMatchIndexAndTagL3, dictHashAndTagL3); + const BYTE* matchL3 = base + matchIndexL3; + hashLong[hl3] = curr + 1; + + /* check prefix long +1 match */ + if (matchIndexL3 > prefixLowestIndex) { + if (MEM_read64(matchL3) == MEM_read64(ip+1)) { + mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; + ip++; + offset = (U32)(ip-matchL3); + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + goto _match_found; + } + } else if (dictTagsMatchL3) { + /* check dict long +1 match */ + U32 const dictMatchIndexL3 = dictMatchIndexAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; + ip++; + offset = (U32)(curr + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + goto _match_found; } } } + + /* if no long +1 match, explore the short match we found */ + if (matchIndexS < prefixLowestIndex) { + mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4; + offset = (U32)(curr - matchIndexS); + while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count(ip+4, match+4, iend) + 4; + offset = (U32)(ip - match); + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } + +_match_found: + offset_2 = offset_1; + offset_1 = offset; + + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); + +_match_stored: + /* match found */ + ip += mLength; + anchor = ip; + + if (ip <= ilimit) { + /* Complementary insertion */ + /* done after iLimit test, as candidates could be > iend-8 */ + { U32 const indexToInsert = curr+2; + hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert; + hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base); + hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert; + hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base); + } + + /* check immediate repcode */ + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + U32 const repIndex2 = current2 - offset_2; + const BYTE* repMatch2 = repIndex2 < prefixLowestIndex ? + dictBase + repIndex2 - dictIndexDelta : + base + repIndex2; + if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; + } + } } /* while (ip < ilimit) */ /* save reps for next block */ - rep[0] = offset_1 ? offset_1 : offsetSaved; - rep[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); } +#define ZSTD_GEN_DFAST_FN(dictMode, mls) \ + static size_t ZSTD_compressBlock_doubleFast_##dictMode##_##mls( \ + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], \ + void const* src, size_t srcSize) \ + { \ + return ZSTD_compressBlock_doubleFast_##dictMode##_generic(ms, seqStore, rep, src, srcSize, mls); \ + } + +ZSTD_GEN_DFAST_FN(noDict, 4) +ZSTD_GEN_DFAST_FN(noDict, 5) +ZSTD_GEN_DFAST_FN(noDict, 6) +ZSTD_GEN_DFAST_FN(noDict, 7) + +ZSTD_GEN_DFAST_FN(dictMatchState, 4) +ZSTD_GEN_DFAST_FN(dictMatchState, 5) +ZSTD_GEN_DFAST_FN(dictMatchState, 6) +ZSTD_GEN_DFAST_FN(dictMatchState, 7) + size_t ZSTD_compressBlock_doubleFast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], @@ -325,13 +558,13 @@ { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_noDict); + return ZSTD_compressBlock_doubleFast_noDict_4(ms, seqStore, rep, src, srcSize); case 5 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_noDict); + return ZSTD_compressBlock_doubleFast_noDict_5(ms, seqStore, rep, src, srcSize); case 6 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_noDict); + return ZSTD_compressBlock_doubleFast_noDict_6(ms, seqStore, rep, src, srcSize); case 7 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_noDict); + return ZSTD_compressBlock_doubleFast_noDict_7(ms, seqStore, rep, src, srcSize); } } @@ -345,13 +578,13 @@ { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_dictMatchState); + return ZSTD_compressBlock_doubleFast_dictMatchState_4(ms, seqStore, rep, src, srcSize); case 5 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_dictMatchState); + return ZSTD_compressBlock_doubleFast_dictMatchState_5(ms, seqStore, rep, src, srcSize); case 6 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_dictMatchState); + return ZSTD_compressBlock_doubleFast_dictMatchState_6(ms, seqStore, rep, src, srcSize); case 7 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_dictMatchState); + return ZSTD_compressBlock_doubleFast_dictMatchState_7(ms, seqStore, rep, src, srcSize); } } @@ -387,7 +620,7 @@ /* if extDict is invalidated due to maxDistance, switch to "regular" variant */ if (prefixStartIndex == dictStartIndex) - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, mls, ZSTD_noDict); + return ZSTD_compressBlock_doubleFast(ms, seqStore, rep, src, srcSize); /* Search Loop */ while (ip < ilimit) { /* < instead of <=, because (ip+1) */ @@ -409,12 +642,12 @@ hashSmall[hSmall] = hashLong[hLong] = curr; /* update hash table */ if ((((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex doesn't overlap dict + prefix */ - & (offset_1 < curr+1 - dictStartIndex)) /* note: we are searching at curr+1 */ + & (offset_1 <= curr+1 - dictStartIndex)) /* note: we are searching at curr+1 */ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); } else { if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) { const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend; @@ -425,7 +658,7 @@ while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) { size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8); @@ -450,7 +683,7 @@ } offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); } else { ip += ((ip-anchor) >> kSearchStrength) + 1; @@ -477,12 +710,12 @@ U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) /* intentional overflow : ensure repIndex2 doesn't overlap dict + prefix */ - & (offset_2 < current2 - dictStartIndex)) + & (offset_2 <= current2 - dictStartIndex)) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; ip += repLength2; @@ -500,6 +733,10 @@ return (size_t)(iend - anchor); } +ZSTD_GEN_DFAST_FN(extDict, 4) +ZSTD_GEN_DFAST_FN(extDict, 5) +ZSTD_GEN_DFAST_FN(extDict, 6) +ZSTD_GEN_DFAST_FN(extDict, 7) size_t ZSTD_compressBlock_doubleFast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], @@ -510,12 +747,12 @@ { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 4); + return ZSTD_compressBlock_doubleFast_extDict_4(ms, seqStore, rep, src, srcSize); case 5 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 5); + return ZSTD_compressBlock_doubleFast_extDict_5(ms, seqStore, rep, src, srcSize); case 6 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 6); + return ZSTD_compressBlock_doubleFast_extDict_6(ms, seqStore, rep, src, srcSize); case 7 : - return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 7); + return ZSTD_compressBlock_doubleFast_extDict_7(ms, seqStore, rep, src, srcSize); } }
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.h b/Utilities/cmzstd/lib/compress/zstd_double_fast.h index e16b7b0..6f0047c 100644 --- a/Utilities/cmzstd/lib/compress/zstd_double_fast.h +++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -19,7 +19,8 @@ #include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, - void const* end, ZSTD_dictTableLoadMethod_e dtlm); + void const* end, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp); size_t ZSTD_compressBlock_doubleFast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize);
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.c b/Utilities/cmzstd/lib/compress/zstd_fast.c index 4edc04d..5f2c6a2 100644 --- a/Utilities/cmzstd/lib/compress/zstd_fast.c +++ b/Utilities/cmzstd/lib/compress/zstd_fast.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,8 +11,42 @@ #include "zstd_compress_internal.h" /* ZSTD_hashPtr, ZSTD_count, ZSTD_storeSeq */ #include "zstd_fast.h" +static void ZSTD_fillHashTableForCDict(ZSTD_matchState_t* ms, + const void* const end, + ZSTD_dictTableLoadMethod_e dtlm) +{ + const ZSTD_compressionParameters* const cParams = &ms->cParams; + U32* const hashTable = ms->hashTable; + U32 const hBits = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; + U32 const mls = cParams->minMatch; + const BYTE* const base = ms->window.base; + const BYTE* ip = base + ms->nextToUpdate; + const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; + const U32 fastHashFillStep = 3; -void ZSTD_fillHashTable(ZSTD_matchState_t* ms, + /* Currently, we always use ZSTD_dtlm_full for filling CDict tables. + * Feel free to remove this assert if there's a good reason! */ + assert(dtlm == ZSTD_dtlm_full); + + /* Always insert every fastHashFillStep position into the hash table. + * Insert the other positions if their hash entry is empty. + */ + for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) { + U32 const curr = (U32)(ip - base); + { size_t const hashAndTag = ZSTD_hashPtr(ip, hBits, mls); + ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr); } + + if (dtlm == ZSTD_dtlm_fast) continue; + /* Only load extra positions for ZSTD_dtlm_full */ + { U32 p; + for (p = 1; p < fastHashFillStep; ++p) { + size_t const hashAndTag = ZSTD_hashPtr(ip + p, hBits, mls); + if (hashTable[hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) { /* not yet filled */ + ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr + p); + } } } } +} + +static void ZSTD_fillHashTableForCCtx(ZSTD_matchState_t* ms, const void* const end, ZSTD_dictTableLoadMethod_e dtlm) { @@ -25,6 +59,10 @@ const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const U32 fastHashFillStep = 3; + /* Currently, we always use ZSTD_dtlm_fast for filling CCtx tables. + * Feel free to remove this assert if there's a good reason! */ + assert(dtlm == ZSTD_dtlm_fast); + /* Always insert every fastHashFillStep position into the hash table. * Insert the other positions if their hash entry is empty. */ @@ -42,146 +80,344 @@ } } } } } +void ZSTD_fillHashTable(ZSTD_matchState_t* ms, + const void* const end, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp) +{ + if (tfp == ZSTD_tfp_forCDict) { + ZSTD_fillHashTableForCDict(ms, end, dtlm); + } else { + ZSTD_fillHashTableForCCtx(ms, end, dtlm); + } +} + +/** + * If you squint hard enough (and ignore repcodes), the search operation at any + * given position is broken into 4 stages: + * + * 1. Hash (map position to hash value via input read) + * 2. Lookup (map hash val to index via hashtable read) + * 3. Load (map index to value at that position via input read) + * 4. Compare + * + * Each of these steps involves a memory read at an address which is computed + * from the previous step. This means these steps must be sequenced and their + * latencies are cumulative. + * + * Rather than do 1->2->3->4 sequentially for a single position before moving + * onto the next, this implementation interleaves these operations across the + * next few positions: + * + * R = Repcode Read & Compare + * H = Hash + * T = Table Lookup + * M = Match Read & Compare + * + * Pos | Time --> + * ----+------------------- + * N | ... M + * N+1 | ... TM + * N+2 | R H T M + * N+3 | H TM + * N+4 | R H T M + * N+5 | H ... + * N+6 | R ... + * + * This is very much analogous to the pipelining of execution in a CPU. And just + * like a CPU, we have to dump the pipeline when we find a match (i.e., take a + * branch). + * + * When this happens, we throw away our current state, and do the following prep + * to re-enter the loop: + * + * Pos | Time --> + * ----+------------------- + * N | H T + * N+1 | H + * + * This is also the work we do at the beginning to enter the loop initially. + */ FORCE_INLINE_TEMPLATE size_t -ZSTD_compressBlock_fast_generic( +ZSTD_compressBlock_fast_noDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, - U32 const mls) + U32 const mls, U32 const hasStep) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hlog = cParams->hashLog; /* support stepSize of 0 */ - size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1; + size_t const stepSize = hasStep ? (cParams->targetLength + !(cParams->targetLength) + 1) : 2; const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; - /* We check ip0 (ip + 0) and ip1 (ip + 1) each loop */ - const BYTE* ip0 = istart; - const BYTE* ip1; - const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); const U32 prefixStartIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; - U32 offset_1=rep[0], offset_2=rep[1]; - U32 offsetSaved = 0; - /* init */ + const BYTE* anchor = istart; + const BYTE* ip0 = istart; + const BYTE* ip1; + const BYTE* ip2; + const BYTE* ip3; + U32 current0; + + U32 rep_offset1 = rep[0]; + U32 rep_offset2 = rep[1]; + U32 offsetSaved1 = 0, offsetSaved2 = 0; + + size_t hash0; /* hash for ip0 */ + size_t hash1; /* hash for ip1 */ + U32 idx; /* match idx for ip0 */ + U32 mval; /* src value at match idx */ + + U32 offcode; + const BYTE* match0; + size_t mLength; + + /* ip0 and ip1 are always adjacent. The targetLength skipping and + * uncompressibility acceleration is applied to every other position, + * matching the behavior of #1562. step therefore represents the gap + * between pairs of positions, from ip0 to ip2 or ip1 to ip3. */ + size_t step; + const BYTE* nextStep; + const size_t kStepIncr = (1 << (kSearchStrength - 1)); + DEBUGLOG(5, "ZSTD_compressBlock_fast_generic"); ip0 += (ip0 == prefixStart); - ip1 = ip0 + 1; { U32 const curr = (U32)(ip0 - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog); U32 const maxRep = curr - windowLow; - if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; - if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; + if (rep_offset2 > maxRep) offsetSaved2 = rep_offset2, rep_offset2 = 0; + if (rep_offset1 > maxRep) offsetSaved1 = rep_offset1, rep_offset1 = 0; } - /* Main Search Loop */ -#ifdef __INTEL_COMPILER - /* From intel 'The vector pragma indicates that the loop should be - * vectorized if it is legal to do so'. Can be used together with - * #pragma ivdep (but have opted to exclude that because intel - * warns against using it).*/ - #pragma vector always -#endif - while (ip1 < ilimit) { /* < instead of <=, because check at ip0+2 */ - size_t mLength; - BYTE const* ip2 = ip0 + 2; - size_t const h0 = ZSTD_hashPtr(ip0, hlog, mls); - U32 const val0 = MEM_read32(ip0); - size_t const h1 = ZSTD_hashPtr(ip1, hlog, mls); - U32 const val1 = MEM_read32(ip1); - U32 const current0 = (U32)(ip0-base); - U32 const current1 = (U32)(ip1-base); - U32 const matchIndex0 = hashTable[h0]; - U32 const matchIndex1 = hashTable[h1]; - BYTE const* repMatch = ip2 - offset_1; - const BYTE* match0 = base + matchIndex0; - const BYTE* match1 = base + matchIndex1; - U32 offcode; + /* start each op */ +_start: /* Requires: ip0 */ -#if defined(__aarch64__) - PREFETCH_L1(ip0+256); -#endif + step = stepSize; + nextStep = ip0 + kStepIncr; - hashTable[h0] = current0; /* update hash table */ - hashTable[h1] = current1; /* update hash table */ + /* calculate positions, ip0 - anchor == 0, so we skip step calc */ + ip1 = ip0 + 1; + ip2 = ip0 + step; + ip3 = ip2 + 1; - assert(ip0 + 1 == ip1); + if (ip3 >= ilimit) { + goto _cleanup; + } - if ((offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip2))) { - mLength = (ip2[-1] == repMatch[-1]) ? 1 : 0; - ip0 = ip2 - mLength; - match0 = repMatch - mLength; + hash0 = ZSTD_hashPtr(ip0, hlog, mls); + hash1 = ZSTD_hashPtr(ip1, hlog, mls); + + idx = hashTable[hash0]; + + do { + /* load repcode match for ip[2]*/ + const U32 rval = MEM_read32(ip2 - rep_offset1); + + /* write back hash table entry */ + current0 = (U32)(ip0 - base); + hashTable[hash0] = current0; + + /* check repcode at ip[2] */ + if ((MEM_read32(ip2) == rval) & (rep_offset1 > 0)) { + ip0 = ip2; + match0 = ip0 - rep_offset1; + mLength = ip0[-1] == match0[-1]; + ip0 -= mLength; + match0 -= mLength; + offcode = REPCODE1_TO_OFFBASE; mLength += 4; - offcode = 0; + + /* First write next hash table entry; we've already calculated it. + * This write is known to be safe because the ip1 is before the + * repcode (ip2). */ + hashTable[hash1] = (U32)(ip1 - base); + goto _match; } - if ((matchIndex0 > prefixStartIndex) && MEM_read32(match0) == val0) { - /* found a regular match */ + + /* load match for ip[0] */ + if (idx >= prefixStartIndex) { + mval = MEM_read32(base + idx); + } else { + mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */ + } + + /* check match at ip[0] */ + if (MEM_read32(ip0) == mval) { + /* found a match! */ + + /* First write next hash table entry; we've already calculated it. + * This write is known to be safe because the ip1 == ip0 + 1, so + * we know we will resume searching after ip1 */ + hashTable[hash1] = (U32)(ip1 - base); + goto _offset; } - if ((matchIndex1 > prefixStartIndex) && MEM_read32(match1) == val1) { - /* found a regular match after one literal */ - ip0 = ip1; - match0 = match1; + + /* lookup ip[1] */ + idx = hashTable[hash1]; + + /* hash ip[2] */ + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + + /* advance to next positions */ + ip0 = ip1; + ip1 = ip2; + ip2 = ip3; + + /* write back hash table entry */ + current0 = (U32)(ip0 - base); + hashTable[hash0] = current0; + + /* load match for ip[0] */ + if (idx >= prefixStartIndex) { + mval = MEM_read32(base + idx); + } else { + mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */ + } + + /* check match at ip[0] */ + if (MEM_read32(ip0) == mval) { + /* found a match! */ + + /* first write next hash table entry; we've already calculated it */ + if (step <= 4) { + /* We need to avoid writing an index into the hash table >= the + * position at which we will pick up our searching after we've + * taken this match. + * + * The minimum possible match has length 4, so the earliest ip0 + * can be after we take this match will be the current ip0 + 4. + * ip1 is ip0 + step - 1. If ip1 is >= ip0 + 4, we can't safely + * write this position. + */ + hashTable[hash1] = (U32)(ip1 - base); + } + goto _offset; } - { size_t const step = ((size_t)(ip0-anchor) >> (kSearchStrength - 1)) + stepSize; - assert(step >= 2); - ip0 += step; - ip1 += step; - continue; + + /* lookup ip[1] */ + idx = hashTable[hash1]; + + /* hash ip[2] */ + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + + /* advance to next positions */ + ip0 = ip1; + ip1 = ip2; + ip2 = ip0 + step; + ip3 = ip1 + step; + + /* calculate step */ + if (ip2 >= nextStep) { + step++; + PREFETCH_L1(ip1 + 64); + PREFETCH_L1(ip1 + 128); + nextStep += kStepIncr; } -_offset: /* Requires: ip0, match0 */ - /* Compute the offset code */ - offset_2 = offset_1; - offset_1 = (U32)(ip0-match0); - offcode = offset_1 + ZSTD_REP_MOVE; - mLength = 4; - /* Count the backwards match length */ - while (((ip0>anchor) & (match0>prefixStart)) - && (ip0[-1] == match0[-1])) { ip0--; match0--; mLength++; } /* catch up */ + } while (ip3 < ilimit); -_match: /* Requires: ip0, match0, offcode */ - /* Count the forward length */ - mLength += ZSTD_count(ip0+mLength, match0+mLength, iend); - ZSTD_storeSeq(seqStore, (size_t)(ip0-anchor), anchor, iend, offcode, mLength-MINMATCH); - /* match found */ - ip0 += mLength; - anchor = ip0; +_cleanup: + /* Note that there are probably still a couple positions we could search. + * However, it seems to be a meaningful performance hit to try to search + * them. So let's not. */ - if (ip0 <= ilimit) { - /* Fill Table */ - assert(base+current0+2 > istart); /* check base overflow */ - hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */ - hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); - - if (offset_2 > 0) { /* offset_2==0 means offset_2 is invalidated */ - while ( (ip0 <= ilimit) && (MEM_read32(ip0) == MEM_read32(ip0 - offset_2)) ) { - /* store sequence */ - size_t const rLength = ZSTD_count(ip0+4, ip0+4-offset_2, iend) + 4; - { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ - hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); - ip0 += rLength; - ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, 0 /*offCode*/, rLength-MINMATCH); - anchor = ip0; - continue; /* faster when present (confirmed on gcc-8) ... (?) */ - } } } - ip1 = ip0 + 1; - } + /* When the repcodes are outside of the prefix, we set them to zero before the loop. + * When the offsets are still zero, we need to restore them after the block to have a correct + * repcode history. If only one offset was invalid, it is easy. The tricky case is when both + * offsets were invalid. We need to figure out which offset to refill with. + * - If both offsets are zero they are in the same order. + * - If both offsets are non-zero, we won't restore the offsets from `offsetSaved[12]`. + * - If only one is zero, we need to decide which offset to restore. + * - If rep_offset1 is non-zero, then rep_offset2 must be offsetSaved1. + * - It is impossible for rep_offset2 to be non-zero. + * + * So if rep_offset1 started invalid (offsetSaved1 != 0) and became valid (rep_offset1 != 0), then + * set rep[0] = rep_offset1 and rep[1] = offsetSaved1. + */ + offsetSaved2 = ((offsetSaved1 != 0) && (rep_offset1 != 0)) ? offsetSaved1 : offsetSaved2; /* save reps for next block */ - rep[0] = offset_1 ? offset_1 : offsetSaved; - rep[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = rep_offset1 ? rep_offset1 : offsetSaved1; + rep[1] = rep_offset2 ? rep_offset2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); + +_offset: /* Requires: ip0, idx */ + + /* Compute the offset code. */ + match0 = base + idx; + rep_offset2 = rep_offset1; + rep_offset1 = (U32)(ip0-match0); + offcode = OFFSET_TO_OFFBASE(rep_offset1); + mLength = 4; + + /* Count the backwards match length. */ + while (((ip0>anchor) & (match0>prefixStart)) && (ip0[-1] == match0[-1])) { + ip0--; + match0--; + mLength++; + } + +_match: /* Requires: ip0, match0, offcode */ + + /* Count the forward length. */ + mLength += ZSTD_count(ip0 + mLength, match0 + mLength, iend); + + ZSTD_storeSeq(seqStore, (size_t)(ip0 - anchor), anchor, iend, offcode, mLength); + + ip0 += mLength; + anchor = ip0; + + /* Fill table and check for immediate repcode. */ + if (ip0 <= ilimit) { + /* Fill Table */ + assert(base+current0+2 > istart); /* check base overflow */ + hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */ + hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); + + if (rep_offset2 > 0) { /* rep_offset2==0 means rep_offset2 is invalidated */ + while ( (ip0 <= ilimit) && (MEM_read32(ip0) == MEM_read32(ip0 - rep_offset2)) ) { + /* store sequence */ + size_t const rLength = ZSTD_count(ip0+4, ip0+4-rep_offset2, iend) + 4; + { U32 const tmpOff = rep_offset2; rep_offset2 = rep_offset1; rep_offset1 = tmpOff; } /* swap rep_offset2 <=> rep_offset1 */ + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); + ip0 += rLength; + ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, REPCODE1_TO_OFFBASE, rLength); + anchor = ip0; + continue; /* faster when present (confirmed on gcc-8) ... (?) */ + } } } + + goto _start; } +#define ZSTD_GEN_FAST_FN(dictMode, mls, step) \ + static size_t ZSTD_compressBlock_fast_##dictMode##_##mls##_##step( \ + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], \ + void const* src, size_t srcSize) \ + { \ + return ZSTD_compressBlock_fast_##dictMode##_generic(ms, seqStore, rep, src, srcSize, mls, step); \ + } + +ZSTD_GEN_FAST_FN(noDict, 4, 1) +ZSTD_GEN_FAST_FN(noDict, 5, 1) +ZSTD_GEN_FAST_FN(noDict, 6, 1) +ZSTD_GEN_FAST_FN(noDict, 7, 1) + +ZSTD_GEN_FAST_FN(noDict, 4, 0) +ZSTD_GEN_FAST_FN(noDict, 5, 0) +ZSTD_GEN_FAST_FN(noDict, 6, 0) +ZSTD_GEN_FAST_FN(noDict, 7, 0) size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], @@ -189,24 +425,40 @@ { U32 const mls = ms->cParams.minMatch; assert(ms->dictMatchState == NULL); - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7); + if (ms->cParams.targetLength > 1) { + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_noDict_4_1(ms, seqStore, rep, src, srcSize); + case 5 : + return ZSTD_compressBlock_fast_noDict_5_1(ms, seqStore, rep, src, srcSize); + case 6 : + return ZSTD_compressBlock_fast_noDict_6_1(ms, seqStore, rep, src, srcSize); + case 7 : + return ZSTD_compressBlock_fast_noDict_7_1(ms, seqStore, rep, src, srcSize); + } + } else { + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_noDict_4_0(ms, seqStore, rep, src, srcSize); + case 5 : + return ZSTD_compressBlock_fast_noDict_5_0(ms, seqStore, rep, src, srcSize); + case 6 : + return ZSTD_compressBlock_fast_noDict_6_0(ms, seqStore, rep, src, srcSize); + case 7 : + return ZSTD_compressBlock_fast_noDict_7_0(ms, seqStore, rep, src, srcSize); + } + } } FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_dictMatchState_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - void const* src, size_t srcSize, U32 const mls) + void const* src, size_t srcSize, U32 const mls, U32 const hasStep) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; @@ -215,14 +467,14 @@ U32 const stepSize = cParams->targetLength + !(cParams->targetLength); const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; + const BYTE* ip0 = istart; + const BYTE* ip1 = ip0 + stepSize; /* we assert below that stepSize >= 1 */ const BYTE* anchor = istart; const U32 prefixStartIndex = ms->window.dictLimit; const BYTE* const prefixStart = base + prefixStartIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; - U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; const ZSTD_compressionParameters* const dictCParams = &dms->cParams ; @@ -232,125 +484,182 @@ const BYTE* const dictStart = dictBase + dictStartIndex; const BYTE* const dictEnd = dms->window.nextSrc; const U32 dictIndexDelta = prefixStartIndex - (U32)(dictEnd - dictBase); - const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart); - const U32 dictHLog = dictCParams->hashLog; + const U32 dictAndPrefixLength = (U32)(istart - prefixStart + dictEnd - dictStart); + const U32 dictHBits = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS; /* if a dictionary is still attached, it necessarily means that * it is within window size. So we just check it. */ const U32 maxDistance = 1U << cParams->windowLog; - const U32 endIndex = (U32)((size_t)(ip - base) + srcSize); + const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); assert(endIndex - prefixStartIndex <= maxDistance); (void)maxDistance; (void)endIndex; /* these variables are not used when assert() is disabled */ + (void)hasStep; /* not currently specialized on whether it's accelerated */ + /* ensure there will be no underflow * when translating a dict index into a local index */ assert(prefixStartIndex >= (U32)(dictEnd - dictBase)); + if (ms->prefetchCDictTables) { + size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32); + PREFETCH_AREA(dictHashTable, hashTableBytes) + } + /* init */ DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic"); - ip += (dictAndPrefixLength == 0); + ip0 += (dictAndPrefixLength == 0); /* dictMatchState repCode checks don't currently handle repCode == 0 * disabling. */ assert(offset_1 <= dictAndPrefixLength); assert(offset_2 <= dictAndPrefixLength); - /* Main Search Loop */ - while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ + /* Outer search loop */ + assert(stepSize >= 1); + while (ip1 <= ilimit) { /* repcode check at (ip0 + 1) is safe because ip0 < ip1 */ size_t mLength; - size_t const h = ZSTD_hashPtr(ip, hlog, mls); - U32 const curr = (U32)(ip-base); - U32 const matchIndex = hashTable[h]; - const BYTE* match = base + matchIndex; - const U32 repIndex = curr + 1 - offset_1; - const BYTE* repMatch = (repIndex < prefixStartIndex) ? - dictBase + (repIndex - dictIndexDelta) : - base + repIndex; - hashTable[h] = curr; /* update hash table */ + size_t hash0 = ZSTD_hashPtr(ip0, hlog, mls); - if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; - mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4; - ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH); - } else if ( (matchIndex <= prefixStartIndex) ) { - size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls); - U32 const dictMatchIndex = dictHashTable[dictHash]; - const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= dictStartIndex || - MEM_read32(dictMatch) != MEM_read32(ip)) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } else { - /* found a dict match */ - U32 const offset = (U32)(curr-dictMatchIndex-dictIndexDelta); - mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4; - while (((ip>anchor) & (dictMatch>dictStart)) - && (ip[-1] == dictMatch[-1])) { - ip--; dictMatch--; mLength++; + size_t const dictHashAndTag0 = ZSTD_hashPtr(ip0, dictHBits, mls); + U32 dictMatchIndexAndTag = dictHashTable[dictHashAndTag0 >> ZSTD_SHORT_CACHE_TAG_BITS]; + int dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag0); + + U32 matchIndex = hashTable[hash0]; + U32 curr = (U32)(ip0 - base); + size_t step = stepSize; + const size_t kStepIncr = 1 << kSearchStrength; + const BYTE* nextStep = ip0 + kStepIncr; + + /* Inner search loop */ + while (1) { + const BYTE* match = base + matchIndex; + const U32 repIndex = curr + 1 - offset_1; + const BYTE* repMatch = (repIndex < prefixStartIndex) ? + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; + const size_t hash1 = ZSTD_hashPtr(ip1, hlog, mls); + size_t const dictHashAndTag1 = ZSTD_hashPtr(ip1, dictHBits, mls); + hashTable[hash0] = curr; /* update hash table */ + + if (((U32) ((prefixStartIndex - 1) - repIndex) >= + 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */ + && (MEM_read32(repMatch) == MEM_read32(ip0 + 1))) { + const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip0 + 1 + 4, repMatch + 4, iend, repMatchEnd, prefixStart) + 4; + ip0++; + ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength); + break; + } + + if (dictTagsMatch) { + /* Found a possible dict match */ + const U32 dictMatchIndex = dictMatchIndexAndTag >> ZSTD_SHORT_CACHE_TAG_BITS; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex > dictStartIndex && + MEM_read32(dictMatch) == MEM_read32(ip0)) { + /* To replicate extDict parse behavior, we only use dict matches when the normal matchIndex is invalid */ + if (matchIndex <= prefixStartIndex) { + U32 const offset = (U32) (curr - dictMatchIndex - dictIndexDelta); + mLength = ZSTD_count_2segments(ip0 + 4, dictMatch + 4, iend, dictEnd, prefixStart) + 4; + while (((ip0 > anchor) & (dictMatch > dictStart)) + && (ip0[-1] == dictMatch[-1])) { + ip0--; + dictMatch--; + mLength++; + } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); + break; + } + } + } + + if (matchIndex > prefixStartIndex && MEM_read32(match) == MEM_read32(ip0)) { + /* found a regular match */ + U32 const offset = (U32) (ip0 - match); + mLength = ZSTD_count(ip0 + 4, match + 4, iend) + 4; + while (((ip0 > anchor) & (match > prefixStart)) + && (ip0[-1] == match[-1])) { + ip0--; + match--; + mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength); + break; } - } else if (MEM_read32(match) != MEM_read32(ip)) { - /* it's not a match, and we're not going to check the dictionary */ - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } else { - /* found a regular match */ - U32 const offset = (U32)(ip-match); - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>prefixStart)) - && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } + + /* Prepare for next iteration */ + dictMatchIndexAndTag = dictHashTable[dictHashAndTag1 >> ZSTD_SHORT_CACHE_TAG_BITS]; + dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag1); + matchIndex = hashTable[hash1]; + + if (ip1 >= nextStep) { + step++; + nextStep += kStepIncr; + } + ip0 = ip1; + ip1 = ip1 + step; + if (ip1 > ilimit) goto _cleanup; + + curr = (U32)(ip0 - base); + hash0 = hash1; + } /* end inner search loop */ /* match found */ - ip += mLength; - anchor = ip; + assert(mLength); + ip0 += mLength; + anchor = ip0; - if (ip <= ilimit) { + if (ip0 <= ilimit) { /* Fill Table */ assert(base+curr+2 > istart); /* check base overflow */ hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2; /* here because curr+2 could be > iend-8 */ - hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); + hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); /* check immediate repcode */ - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); + while (ip0 <= ilimit) { + U32 const current2 = (U32)(ip0-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + && (MEM_read32(repMatch2) == MEM_read32(ip0))) { const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; + size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = current2; + ip0 += repLength2; + anchor = ip0; continue; } break; } } + + /* Prepare for next iteration */ + assert(ip0 == anchor); + ip1 = ip0 + stepSize; } +_cleanup: /* save reps for next block */ - rep[0] = offset_1 ? offset_1 : offsetSaved; - rep[1] = offset_2 ? offset_2 : offsetSaved; + rep[0] = offset_1; + rep[1] = offset_2; /* Return the last literals size */ return (size_t)(iend - anchor); } + +ZSTD_GEN_FAST_FN(dictMatchState, 4, 0) +ZSTD_GEN_FAST_FN(dictMatchState, 5, 0) +ZSTD_GEN_FAST_FN(dictMatchState, 6, 0) +ZSTD_GEN_FAST_FN(dictMatchState, 7, 0) + size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) @@ -361,30 +670,29 @@ { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 4); + return ZSTD_compressBlock_fast_dictMatchState_4_0(ms, seqStore, rep, src, srcSize); case 5 : - return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 5); + return ZSTD_compressBlock_fast_dictMatchState_5_0(ms, seqStore, rep, src, srcSize); case 6 : - return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 6); + return ZSTD_compressBlock_fast_dictMatchState_6_0(ms, seqStore, rep, src, srcSize); case 7 : - return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 7); + return ZSTD_compressBlock_fast_dictMatchState_7_0(ms, seqStore, rep, src, srcSize); } } static size_t ZSTD_compressBlock_fast_extDict_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - void const* src, size_t srcSize, U32 const mls) + void const* src, size_t srcSize, U32 const mls, U32 const hasStep) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32* const hashTable = ms->hashTable; U32 const hlog = cParams->hashLog; /* support stepSize of 0 */ - U32 const stepSize = cParams->targetLength + !(cParams->targetLength); + size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1; const BYTE* const base = ms->window.base; const BYTE* const dictBase = ms->window.dictBase; const BYTE* const istart = (const BYTE*)src; - const BYTE* ip = istart; const BYTE* anchor = istart; const U32 endIndex = (U32)((size_t)(istart - base) + srcSize); const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog); @@ -397,100 +705,256 @@ const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - 8; U32 offset_1=rep[0], offset_2=rep[1]; + U32 offsetSaved1 = 0, offsetSaved2 = 0; + + const BYTE* ip0 = istart; + const BYTE* ip1; + const BYTE* ip2; + const BYTE* ip3; + U32 current0; + + + size_t hash0; /* hash for ip0 */ + size_t hash1; /* hash for ip1 */ + U32 idx; /* match idx for ip0 */ + const BYTE* idxBase; /* base pointer for idx */ + + U32 offcode; + const BYTE* match0; + size_t mLength; + const BYTE* matchEnd = 0; /* initialize to avoid warning, assert != 0 later */ + + size_t step; + const BYTE* nextStep; + const size_t kStepIncr = (1 << (kSearchStrength - 1)); + + (void)hasStep; /* not currently specialized on whether it's accelerated */ DEBUGLOG(5, "ZSTD_compressBlock_fast_extDict_generic (offset_1=%u)", offset_1); /* switch to "regular" variant if extDict is invalidated due to maxDistance */ if (prefixStartIndex == dictStartIndex) - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, mls); + return ZSTD_compressBlock_fast(ms, seqStore, rep, src, srcSize); - /* Search Loop */ - while (ip < ilimit) { /* < instead of <=, because (ip+1) */ - const size_t h = ZSTD_hashPtr(ip, hlog, mls); - const U32 matchIndex = hashTable[h]; - const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base; - const BYTE* match = matchBase + matchIndex; - const U32 curr = (U32)(ip-base); - const U32 repIndex = curr + 1 - offset_1; - const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; - const BYTE* const repMatch = repBase + repIndex; - hashTable[h] = curr; /* update hash table */ - DEBUGLOG(7, "offset_1 = %u , curr = %u", offset_1, curr); + { U32 const curr = (U32)(ip0 - base); + U32 const maxRep = curr - dictStartIndex; + if (offset_2 >= maxRep) offsetSaved2 = offset_2, offset_2 = 0; + if (offset_1 >= maxRep) offsetSaved1 = offset_1, offset_1 = 0; + } - if ( ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ - & (offset_1 < curr+1 - dictStartIndex) ) /* note: we are searching at curr+1 */ - && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; - size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4; - ip++; - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - } else { - if ( (matchIndex < dictStartIndex) || - (MEM_read32(match) != MEM_read32(ip)) ) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; + /* start each op */ +_start: /* Requires: ip0 */ + + step = stepSize; + nextStep = ip0 + kStepIncr; + + /* calculate positions, ip0 - anchor == 0, so we skip step calc */ + ip1 = ip0 + 1; + ip2 = ip0 + step; + ip3 = ip2 + 1; + + if (ip3 >= ilimit) { + goto _cleanup; + } + + hash0 = ZSTD_hashPtr(ip0, hlog, mls); + hash1 = ZSTD_hashPtr(ip1, hlog, mls); + + idx = hashTable[hash0]; + idxBase = idx < prefixStartIndex ? dictBase : base; + + do { + { /* load repcode match for ip[2] */ + U32 const current2 = (U32)(ip2 - base); + U32 const repIndex = current2 - offset_1; + const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base; + U32 rval; + if ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */ + & (offset_1 > 0) ) { + rval = MEM_read32(repBase + repIndex); + } else { + rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */ } - { const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; - const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; - U32 const offset = curr - matchIndex; - size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4; - while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; offset_1 = offset; /* update offset history */ - ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - ip += mLength; - anchor = ip; + + /* write back hash table entry */ + current0 = (U32)(ip0 - base); + hashTable[hash0] = current0; + + /* check repcode at ip[2] */ + if (MEM_read32(ip2) == rval) { + ip0 = ip2; + match0 = repBase + repIndex; + matchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + assert((match0 != prefixStart) & (match0 != dictStart)); + mLength = ip0[-1] == match0[-1]; + ip0 -= mLength; + match0 -= mLength; + offcode = REPCODE1_TO_OFFBASE; + mLength += 4; + goto _match; } } - if (ip <= ilimit) { - /* Fill Table */ - hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2; - hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); - /* check immediate repcode */ - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); - U32 const repIndex2 = current2 - offset_2; - const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; - if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 < curr - dictStartIndex)) /* intentional overflow */ - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; - { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, 0 /*offcode*/, repLength2-MINMATCH); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; - continue; - } - break; - } } } + { /* load match for ip[0] */ + U32 const mval = idx >= dictStartIndex ? + MEM_read32(idxBase + idx) : + MEM_read32(ip0) ^ 1; /* guaranteed not to match */ + + /* check match at ip[0] */ + if (MEM_read32(ip0) == mval) { + /* found a match! */ + goto _offset; + } } + + /* lookup ip[1] */ + idx = hashTable[hash1]; + idxBase = idx < prefixStartIndex ? dictBase : base; + + /* hash ip[2] */ + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + + /* advance to next positions */ + ip0 = ip1; + ip1 = ip2; + ip2 = ip3; + + /* write back hash table entry */ + current0 = (U32)(ip0 - base); + hashTable[hash0] = current0; + + { /* load match for ip[0] */ + U32 const mval = idx >= dictStartIndex ? + MEM_read32(idxBase + idx) : + MEM_read32(ip0) ^ 1; /* guaranteed not to match */ + + /* check match at ip[0] */ + if (MEM_read32(ip0) == mval) { + /* found a match! */ + goto _offset; + } } + + /* lookup ip[1] */ + idx = hashTable[hash1]; + idxBase = idx < prefixStartIndex ? dictBase : base; + + /* hash ip[2] */ + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + + /* advance to next positions */ + ip0 = ip1; + ip1 = ip2; + ip2 = ip0 + step; + ip3 = ip1 + step; + + /* calculate step */ + if (ip2 >= nextStep) { + step++; + PREFETCH_L1(ip1 + 64); + PREFETCH_L1(ip1 + 128); + nextStep += kStepIncr; + } + } while (ip3 < ilimit); + +_cleanup: + /* Note that there are probably still a couple positions we could search. + * However, it seems to be a meaningful performance hit to try to search + * them. So let's not. */ + + /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0), + * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */ + offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2; /* save reps for next block */ - rep[0] = offset_1; - rep[1] = offset_2; + rep[0] = offset_1 ? offset_1 : offsetSaved1; + rep[1] = offset_2 ? offset_2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); + +_offset: /* Requires: ip0, idx, idxBase */ + + /* Compute the offset code. */ + { U32 const offset = current0 - idx; + const BYTE* const lowMatchPtr = idx < prefixStartIndex ? dictStart : prefixStart; + matchEnd = idx < prefixStartIndex ? dictEnd : iend; + match0 = idxBase + idx; + offset_2 = offset_1; + offset_1 = offset; + offcode = OFFSET_TO_OFFBASE(offset); + mLength = 4; + + /* Count the backwards match length. */ + while (((ip0>anchor) & (match0>lowMatchPtr)) && (ip0[-1] == match0[-1])) { + ip0--; + match0--; + mLength++; + } } + +_match: /* Requires: ip0, match0, offcode, matchEnd */ + + /* Count the forward length. */ + assert(matchEnd != 0); + mLength += ZSTD_count_2segments(ip0 + mLength, match0 + mLength, iend, matchEnd, prefixStart); + + ZSTD_storeSeq(seqStore, (size_t)(ip0 - anchor), anchor, iend, offcode, mLength); + + ip0 += mLength; + anchor = ip0; + + /* write next hash table entry */ + if (ip1 < ip0) { + hashTable[hash1] = (U32)(ip1 - base); + } + + /* Fill table and check for immediate repcode. */ + if (ip0 <= ilimit) { + /* Fill Table */ + assert(base+current0+2 > istart); /* check base overflow */ + hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */ + hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base); + + while (ip0 <= ilimit) { + U32 const repIndex2 = (U32)(ip0-base) - offset_2; + const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2; + if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 > 0)) /* intentional underflow */ + && (MEM_read32(repMatch2) == MEM_read32(ip0)) ) { + const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4; + { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, REPCODE1_TO_OFFBASE, repLength2); + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base); + ip0 += repLength2; + anchor = ip0; + continue; + } + break; + } } + + goto _start; } +ZSTD_GEN_FAST_FN(extDict, 4, 0) +ZSTD_GEN_FAST_FN(extDict, 5, 0) +ZSTD_GEN_FAST_FN(extDict, 6, 0) +ZSTD_GEN_FAST_FN(extDict, 7, 0) size_t ZSTD_compressBlock_fast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) { U32 const mls = ms->cParams.minMatch; + assert(ms->dictMatchState == NULL); switch(mls) { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 4); + return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize); case 5 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 5); + return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize); case 6 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 6); + return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize); case 7 : - return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 7); + return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize); } }
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.h b/Utilities/cmzstd/lib/compress/zstd_fast.h index 0d4a0c1..9e4236b 100644 --- a/Utilities/cmzstd/lib/compress/zstd_fast.h +++ b/Utilities/cmzstd/lib/compress/zstd_fast.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -19,7 +19,8 @@ #include "zstd_compress_internal.h" void ZSTD_fillHashTable(ZSTD_matchState_t* ms, - void const* end, ZSTD_dictTableLoadMethod_e dtlm); + void const* end, ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp); size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize);
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.c b/Utilities/cmzstd/lib/compress/zstd_lazy.c index 6ce897d..5ba88e8 100644 --- a/Utilities/cmzstd/lib/compress/zstd_lazy.c +++ b/Utilities/cmzstd/lib/compress/zstd_lazy.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -10,6 +10,9 @@ #include "zstd_compress_internal.h" #include "zstd_lazy.h" +#include "../common/bits.h" /* ZSTD_countTrailingZeros64 */ + +#define kLazySkippingStep 8 /*-************************************* @@ -61,7 +64,7 @@ * assumption : curr >= btlow == (curr - btmask) * doesn't fail */ static void -ZSTD_insertDUBT1(ZSTD_matchState_t* ms, +ZSTD_insertDUBT1(const ZSTD_matchState_t* ms, U32 curr, const BYTE* inputEnd, U32 nbCompares, U32 btLow, const ZSTD_dictMode_e dictMode) @@ -93,7 +96,7 @@ assert(curr >= btLow); assert(ip < iend); /* condition for ZSTD_count */ - while (nbCompares-- && (matchIndex > windowLow)) { + for (; nbCompares && (matchIndex > windowLow); --nbCompares) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(matchIndex < curr); @@ -151,7 +154,7 @@ static size_t ZSTD_DUBT_findBetterDictMatch ( - ZSTD_matchState_t* ms, + const ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, size_t* offsetPtr, size_t bestLength, @@ -185,7 +188,7 @@ (void)dictMode; assert(dictMode == ZSTD_dictMatchState); - while (nbCompares-- && (dictMatchIndex > dictLowLimit)) { + for (; nbCompares && (dictMatchIndex > dictLowLimit); --nbCompares) { U32* const nextPtr = dictBt + 2*(dictMatchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match = dictBase + dictMatchIndex; @@ -197,8 +200,8 @@ U32 matchIndex = dictMatchIndex + dictIndexDelta; if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) { DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)", - curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, ZSTD_REP_MOVE + curr - matchIndex, dictMatchIndex, matchIndex); - bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + curr - matchIndex; + curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, OFFSET_TO_OFFBASE(curr - matchIndex), dictMatchIndex, matchIndex); + bestLength = matchLength, *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex); } if (ip+matchLength == iend) { /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */ break; /* drop, to guarantee consistency (miss a little bit of compression) */ @@ -218,7 +221,7 @@ } if (bestLength >= MINMATCH) { - U32 const mIndex = curr - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; + U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offsetPtr); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)", curr, (U32)bestLength, (U32)*offsetPtr, mIndex); } @@ -230,7 +233,7 @@ static size_t ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, - size_t* offsetPtr, + size_t* offBasePtr, U32 const mls, const ZSTD_dictMode_e dictMode) { @@ -309,7 +312,7 @@ matchIndex = hashTable[h]; hashTable[h] = curr; /* Update Hash Table */ - while (nbCompares-- && (matchIndex > windowLow)) { + for (; nbCompares && (matchIndex > windowLow); --nbCompares) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match; @@ -327,8 +330,8 @@ if (matchLength > bestLength) { if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; - if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) - bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + curr - matchIndex; + if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr - matchIndex + 1) - ZSTD_highbit32((U32)*offBasePtr)) ) + bestLength = matchLength, *offBasePtr = OFFSET_TO_OFFBASE(curr - matchIndex); if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */ if (dictMode == ZSTD_dictMatchState) { nbCompares = 0; /* in addition to avoiding checking any @@ -357,19 +360,20 @@ *smallerPtr = *largerPtr = 0; + assert(nbCompares <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */ if (dictMode == ZSTD_dictMatchState && nbCompares) { bestLength = ZSTD_DUBT_findBetterDictMatch( ms, ip, iend, - offsetPtr, bestLength, nbCompares, + offBasePtr, bestLength, nbCompares, mls, dictMode); } assert(matchEndIdx > curr+8); /* ensure nextToUpdate is increased */ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ if (bestLength >= MINMATCH) { - U32 const mIndex = curr - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex; + U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offBasePtr); (void)mIndex; DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)", - curr, (U32)bestLength, (U32)*offsetPtr, mIndex); + curr, (U32)bestLength, (U32)*offBasePtr, mIndex); } return bestLength; } @@ -380,62 +384,14 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_BtFindBestMatch( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iLimit, - size_t* offsetPtr, + size_t* offBasePtr, const U32 mls /* template */, const ZSTD_dictMode_e dictMode) { DEBUGLOG(7, "ZSTD_BtFindBestMatch"); if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ ZSTD_updateDUBT(ms, ip, iLimit, mls); - return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offsetPtr, mls, dictMode); -} - - -static size_t -ZSTD_BtFindBestMatch_selectMLS ( ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict); - case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict); - case 7 : - case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict); - } -} - - -static size_t ZSTD_BtFindBestMatch_dictMatchState_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState); - case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState); - case 7 : - case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState); - } -} - - -static size_t ZSTD_BtFindBestMatch_extDict_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict); - case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict); - case 7 : - case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict); - } + return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offBasePtr, mls, dictMode); } /*********************************** @@ -450,7 +406,7 @@ U32* const chainTable = ms->chainTable; U32 const chainSize = 1 << ms->cParams.chainLog; U32 idx = ms->nextToUpdate; - U32 const minChain = chainSize < target ? target - chainSize : idx; + U32 const minChain = chainSize < target - idx ? target - chainSize : idx; U32 const bucketSize = 1 << ZSTD_LAZY_DDSS_BUCKET_LOG; U32 const cacheSize = bucketSize - 1; U32 const chainAttempts = (1 << ms->cParams.searchLog) - cacheSize; @@ -464,7 +420,7 @@ U32 const hashLog = ms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG; U32* const tmpHashTable = hashTable; U32* const tmpChainTable = hashTable + ((size_t)1 << hashLog); - U32 const tmpChainSize = ((1 << ZSTD_LAZY_DDSS_BUCKET_LOG) - 1) << hashLog; + U32 const tmpChainSize = (U32)((1 << ZSTD_LAZY_DDSS_BUCKET_LOG) - 1) << hashLog; U32 const tmpMinChain = tmpChainSize < target ? target - tmpChainSize : idx; U32 hashIdx; @@ -608,7 +564,7 @@ /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = curr - (matchIndex + ddsIndexDelta) + ZSTD_REP_MOVE; + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta)); if (ip+currentMl == iLimit) { /* best possible, avoids read overflow on next attempt */ return ml; @@ -645,7 +601,7 @@ /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = curr - (matchIndex + ddsIndexDelta) + ZSTD_REP_MOVE; + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta)); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } } @@ -664,7 +620,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( ZSTD_matchState_t* ms, const ZSTD_compressionParameters* const cParams, - const BYTE* ip, U32 const mls) + const BYTE* ip, U32 const mls, U32 const lazySkipping) { U32* const hashTable = ms->hashTable; const U32 hashLog = cParams->hashLog; @@ -679,6 +635,9 @@ NEXT_IN_CHAIN(idx, chainMask) = hashTable[h]; hashTable[h] = idx; idx++; + /* Stop inserting every position when in the lazy skipping mode. */ + if (lazySkipping) + break; } ms->nextToUpdate = target; @@ -687,12 +646,12 @@ U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip) { const ZSTD_compressionParameters* const cParams = &ms->cParams; - return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch); + return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch, /* lazySkipping*/ 0); } /* inlining is important to hardwire a hot branch (template emulation) */ FORCE_INLINE_TEMPLATE -size_t ZSTD_HcFindBestMatch_generic ( +size_t ZSTD_HcFindBestMatch( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, @@ -731,14 +690,15 @@ } /* HC4 match finder */ - matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls); + matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls, ms->lazySkipping); for ( ; (matchIndex>=lowLimit) & (nbAttempts>0) ; nbAttempts--) { size_t currentMl=0; if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) { const BYTE* const match = base + matchIndex; assert(matchIndex >= dictLimit); /* ensures this is true if dictMode != ZSTD_extDict */ - if (match[ml] == ip[ml]) /* potentially better */ + /* read 4B starting from (match + ml + 1 - sizeof(U32)) */ + if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3)) /* potentially better */ currentMl = ZSTD_count(ip, match, iLimit); } else { const BYTE* const match = dictBase + matchIndex; @@ -750,7 +710,7 @@ /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = curr - matchIndex + ZSTD_REP_MOVE; + *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } @@ -758,6 +718,7 @@ matchIndex = NEXT_IN_CHAIN(matchIndex, chainMask); } + assert(nbAttempts <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */ if (dictMode == ZSTD_dedicatedDictSearch) { ml = ZSTD_dedicatedDictSearch_lazy_search(offsetPtr, ml, nbAttempts, dms, ip, iLimit, prefixStart, curr, dictLimit, ddsIdx); @@ -784,7 +745,8 @@ /* save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = curr - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE; + assert(curr > matchIndex + dmsIndexDelta); + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta)); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } @@ -797,321 +759,34 @@ return ml; } - -FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict); - case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict); - case 7 : - case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict); - } -} - - -static size_t ZSTD_HcFindBestMatch_dictMatchState_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState); - case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState); - case 7 : - case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState); - } -} - - -static size_t ZSTD_HcFindBestMatch_dedicatedDictSearch_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dedicatedDictSearch); - case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dedicatedDictSearch); - case 7 : - case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dedicatedDictSearch); - } -} - - -FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict); - case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict); - case 7 : - case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict); - } -} - /* ********************************* * (SIMD) Row-based matchfinder ***********************************/ /* Constants for row-based hash */ -#define ZSTD_ROW_HASH_TAG_OFFSET 1 /* byte offset of hashes in the match state's tagTable from the beginning of a row */ -#define ZSTD_ROW_HASH_TAG_BITS 8 /* nb bits to use for the tag */ #define ZSTD_ROW_HASH_TAG_MASK ((1u << ZSTD_ROW_HASH_TAG_BITS) - 1) +#define ZSTD_ROW_HASH_MAX_ENTRIES 64 /* absolute maximum number of entries per row, for all configurations */ #define ZSTD_ROW_HASH_CACHE_MASK (ZSTD_ROW_HASH_CACHE_SIZE - 1) -typedef U32 ZSTD_VecMask; /* Clarifies when we are interacting with a U32 representing a mask of matches */ - -#if !defined(ZSTD_NO_INTRINSICS) && defined(__SSE2__) /* SIMD SSE version */ - -#include <emmintrin.h> -typedef __m128i ZSTD_Vec128; - -/* Returns a 128-bit container with 128-bits from src */ -static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) { - return _mm_loadu_si128((ZSTD_Vec128 const*)src); -} - -/* Returns a ZSTD_Vec128 with the byte "val" packed 16 times */ -static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) { - return _mm_set1_epi8((char)val); -} - -/* Do byte-by-byte comparison result of x and y. Then collapse 128-bit resultant mask - * into a 32-bit mask that is the MSB of each byte. - * */ -static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) { - return (ZSTD_VecMask)_mm_movemask_epi8(_mm_cmpeq_epi8(x, y)); -} - -typedef struct { - __m128i fst; - __m128i snd; -} ZSTD_Vec256; - -static ZSTD_Vec256 ZSTD_Vec256_read(const void* const ptr) { - ZSTD_Vec256 v; - v.fst = ZSTD_Vec128_read(ptr); - v.snd = ZSTD_Vec128_read((ZSTD_Vec128 const*)ptr + 1); - return v; -} - -static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) { - ZSTD_Vec256 v; - v.fst = ZSTD_Vec128_set8(val); - v.snd = ZSTD_Vec128_set8(val); - return v; -} - -static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) { - ZSTD_VecMask fstMask; - ZSTD_VecMask sndMask; - fstMask = ZSTD_Vec128_cmpMask8(x.fst, y.fst); - sndMask = ZSTD_Vec128_cmpMask8(x.snd, y.snd); - return fstMask | (sndMask << 16); -} - -#elif !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) /* SIMD ARM NEON Version */ - -#include <arm_neon.h> -typedef uint8x16_t ZSTD_Vec128; - -static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) { - return vld1q_u8((const BYTE* const)src); -} - -static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) { - return vdupq_n_u8(val); -} - -/* Mimics '_mm_movemask_epi8()' from SSE */ -static U32 ZSTD_vmovmaskq_u8(ZSTD_Vec128 val) { - /* Shift out everything but the MSB bits in each byte */ - uint16x8_t highBits = vreinterpretq_u16_u8(vshrq_n_u8(val, 7)); - /* Merge the even lanes together with vsra (right shift and add) */ - uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(highBits, highBits, 7)); - uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); - uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); - /* Extract the low 8 bits from each lane, merge */ - return vgetq_lane_u8(paired64, 0) | ((U32)vgetq_lane_u8(paired64, 8) << 8); -} - -static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) { - return (ZSTD_VecMask)ZSTD_vmovmaskq_u8(vceqq_u8(x, y)); -} - -typedef struct { - uint8x16_t fst; - uint8x16_t snd; -} ZSTD_Vec256; - -static ZSTD_Vec256 ZSTD_Vec256_read(const void* const ptr) { - ZSTD_Vec256 v; - v.fst = ZSTD_Vec128_read(ptr); - v.snd = ZSTD_Vec128_read((ZSTD_Vec128 const*)ptr + 1); - return v; -} - -static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) { - ZSTD_Vec256 v; - v.fst = ZSTD_Vec128_set8(val); - v.snd = ZSTD_Vec128_set8(val); - return v; -} - -static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) { - ZSTD_VecMask fstMask; - ZSTD_VecMask sndMask; - fstMask = ZSTD_Vec128_cmpMask8(x.fst, y.fst); - sndMask = ZSTD_Vec128_cmpMask8(x.snd, y.snd); - return fstMask | (sndMask << 16); -} - -#else /* Scalar fallback version */ - -#define VEC128_NB_SIZE_T (16 / sizeof(size_t)) -typedef struct { - size_t vec[VEC128_NB_SIZE_T]; -} ZSTD_Vec128; - -static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) { - ZSTD_Vec128 ret; - ZSTD_memcpy(ret.vec, src, VEC128_NB_SIZE_T*sizeof(size_t)); - return ret; -} - -static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) { - ZSTD_Vec128 ret = { {0} }; - int startBit = sizeof(size_t) * 8 - 8; - for (;startBit >= 0; startBit -= 8) { - unsigned j = 0; - for (;j < VEC128_NB_SIZE_T; ++j) { - ret.vec[j] |= ((size_t)val << startBit); - } - } - return ret; -} - -/* Compare x to y, byte by byte, generating a "matches" bitfield */ -static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) { - ZSTD_VecMask res = 0; - unsigned i = 0; - unsigned l = 0; - for (; i < VEC128_NB_SIZE_T; ++i) { - const size_t cmp1 = x.vec[i]; - const size_t cmp2 = y.vec[i]; - unsigned j = 0; - for (; j < sizeof(size_t); ++j, ++l) { - if (((cmp1 >> j*8) & 0xFF) == ((cmp2 >> j*8) & 0xFF)) { - res |= ((U32)1 << (j+i*sizeof(size_t))); - } - } - } - return res; -} - -#define VEC256_NB_SIZE_T 2*VEC128_NB_SIZE_T -typedef struct { - size_t vec[VEC256_NB_SIZE_T]; -} ZSTD_Vec256; - -static ZSTD_Vec256 ZSTD_Vec256_read(const void* const src) { - ZSTD_Vec256 ret; - ZSTD_memcpy(ret.vec, src, VEC256_NB_SIZE_T*sizeof(size_t)); - return ret; -} - -static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) { - ZSTD_Vec256 ret = { {0} }; - int startBit = sizeof(size_t) * 8 - 8; - for (;startBit >= 0; startBit -= 8) { - unsigned j = 0; - for (;j < VEC256_NB_SIZE_T; ++j) { - ret.vec[j] |= ((size_t)val << startBit); - } - } - return ret; -} - -/* Compare x to y, byte by byte, generating a "matches" bitfield */ -static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) { - ZSTD_VecMask res = 0; - unsigned i = 0; - unsigned l = 0; - for (; i < VEC256_NB_SIZE_T; ++i) { - const size_t cmp1 = x.vec[i]; - const size_t cmp2 = y.vec[i]; - unsigned j = 0; - for (; j < sizeof(size_t); ++j, ++l) { - if (((cmp1 >> j*8) & 0xFF) == ((cmp2 >> j*8) & 0xFF)) { - res |= ((U32)1 << (j+i*sizeof(size_t))); - } - } - } - return res; -} - -#endif /* !defined(ZSTD_NO_INTRINSICS) && defined(__SSE2__) */ +typedef U64 ZSTD_VecMask; /* Clarifies when we are interacting with a U64 representing a mask of matches */ /* ZSTD_VecMask_next(): * Starting from the LSB, returns the idx of the next non-zero bit. * Basically counting the nb of trailing zeroes. */ -static U32 ZSTD_VecMask_next(ZSTD_VecMask val) { -# if defined(_MSC_VER) /* Visual */ - unsigned long r=0; - return _BitScanForward(&r, val) ? (U32)r : 0; -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (U32)__builtin_ctz(val); -# else - /* Software ctz version: http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup */ - static const U32 multiplyDeBruijnBitPosition[32] = - { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - return multiplyDeBruijnBitPosition[((U32)((val & -(int)val) * 0x077CB531U)) >> 27]; - -# endif -} - -/* ZSTD_VecMask_rotateRight(): - * Rotates a bitfield to the right by "rotation" bits. - * If the rotation is greater than totalBits, the returned mask is 0. - */ -FORCE_INLINE_TEMPLATE ZSTD_VecMask -ZSTD_VecMask_rotateRight(ZSTD_VecMask mask, U32 const rotation, U32 const totalBits) { - if (rotation == 0) - return mask; - switch (totalBits) { - default: - assert(0); - case 16: - return (mask >> rotation) | (U16)(mask << (16 - rotation)); - case 32: - return (mask >> rotation) | (U32)(mask << (32 - rotation)); - } +MEM_STATIC U32 ZSTD_VecMask_next(ZSTD_VecMask val) { + return ZSTD_countTrailingZeros64(val); } /* ZSTD_row_nextIndex(): * Returns the next index to insert at within a tagTable row, and updates the "head" - * value to reflect the update. Essentially cycles backwards from [0, {entries per row}) + * value to reflect the update. Essentially cycles backwards from [1, {entries per row}) */ FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextIndex(BYTE* const tagRow, U32 const rowMask) { - U32 const next = (*tagRow - 1) & rowMask; - *tagRow = (BYTE)next; - return next; + U32 next = (*tagRow-1) & rowMask; + next += (next == 0) ? rowMask : 0; /* skip first position */ + *tagRow = (BYTE)next; + return next; } /* ZSTD_isAligned(): @@ -1125,33 +800,37 @@ /* ZSTD_row_prefetch(): * Performs prefetching for the hashTable and tagTable at a given row. */ -FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, U16 const* tagTable, U32 const relRow, U32 const rowLog) { +FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, BYTE const* tagTable, U32 const relRow, U32 const rowLog) { PREFETCH_L1(hashTable + relRow); - if (rowLog == 5) { + if (rowLog >= 5) { PREFETCH_L1(hashTable + relRow + 16); + /* Note: prefetching more of the hash table does not appear to be beneficial for 128-entry rows */ } PREFETCH_L1(tagTable + relRow); - assert(rowLog == 4 || rowLog == 5); + if (rowLog == 6) { + PREFETCH_L1(tagTable + relRow + 32); + } + assert(rowLog == 4 || rowLog == 5 || rowLog == 6); assert(ZSTD_isAligned(hashTable + relRow, 64)); /* prefetched hash row always 64-byte aligned */ - assert(ZSTD_isAligned(tagTable + relRow, (size_t)1 << rowLog)); /* prefetched tagRow sits on a multiple of 32 or 64 bytes */ + assert(ZSTD_isAligned(tagTable + relRow, (size_t)1 << rowLog)); /* prefetched tagRow sits on correct multiple of bytes (32,64,128) */ } /* ZSTD_row_fillHashCache(): * Fill up the hash cache starting at idx, prefetching up to ZSTD_ROW_HASH_CACHE_SIZE entries, * but not beyond iLimit. */ -static void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const BYTE* base, +FORCE_INLINE_TEMPLATE void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const BYTE* base, U32 const rowLog, U32 const mls, U32 idx, const BYTE* const iLimit) { U32 const* const hashTable = ms->hashTable; - U16 const* const tagTable = ms->tagTable; + BYTE const* const tagTable = ms->tagTable; U32 const hashLog = ms->rowHashLog; U32 const maxElemsToPrefetch = (base + idx) > iLimit ? 0 : (U32)(iLimit - (base + idx) + 1); U32 const lim = idx + MIN(ZSTD_ROW_HASH_CACHE_SIZE, maxElemsToPrefetch); for (; idx < lim; ++idx) { - U32 const hash = (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const hash = (U32)ZSTD_hashPtrSalted(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt); U32 const row = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); ms->hashCache[idx & ZSTD_ROW_HASH_CACHE_MASK] = hash; @@ -1167,11 +846,12 @@ * base + idx + ZSTD_ROW_HASH_CACHE_SIZE. Also prefetches the appropriate rows from hashTable and tagTable. */ FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextCachedHash(U32* cache, U32 const* hashTable, - U16 const* tagTable, BYTE const* base, + BYTE const* tagTable, BYTE const* base, U32 idx, U32 const hashLog, - U32 const rowLog, U32 const mls) + U32 const rowLog, U32 const mls, + U64 const hashSalt) { - U32 const newHash = (U32)ZSTD_hashPtr(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const newHash = (U32)ZSTD_hashPtrSalted(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt); U32 const row = (newHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); { U32 const hash = cache[idx & ZSTD_ROW_HASH_CACHE_MASK]; @@ -1180,35 +860,64 @@ } } +/* ZSTD_row_update_internalImpl(): + * Updates the hash table with positions starting from updateStartIdx until updateEndIdx. + */ +FORCE_INLINE_TEMPLATE void ZSTD_row_update_internalImpl(ZSTD_matchState_t* ms, + U32 updateStartIdx, U32 const updateEndIdx, + U32 const mls, U32 const rowLog, + U32 const rowMask, U32 const useCache) +{ + U32* const hashTable = ms->hashTable; + BYTE* const tagTable = ms->tagTable; + U32 const hashLog = ms->rowHashLog; + const BYTE* const base = ms->window.base; + + DEBUGLOG(6, "ZSTD_row_update_internalImpl(): updateStartIdx=%u, updateEndIdx=%u", updateStartIdx, updateEndIdx); + for (; updateStartIdx < updateEndIdx; ++updateStartIdx) { + U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, updateStartIdx, hashLog, rowLog, mls, ms->hashSalt) + : (U32)ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt); + U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; + U32* const row = hashTable + relRow; + BYTE* tagRow = tagTable + relRow; + U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); + + assert(hash == ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt)); + tagRow[pos] = hash & ZSTD_ROW_HASH_TAG_MASK; + row[pos] = updateStartIdx; + } +} + /* ZSTD_row_update_internal(): - * Inserts the byte at ip into the appropriate position in the hash table. - * Determines the relative row, and the position within the {16, 32} entry row to insert at. + * Inserts the byte at ip into the appropriate position in the hash table, and updates ms->nextToUpdate. + * Skips sections of long matches as is necessary. */ FORCE_INLINE_TEMPLATE void ZSTD_row_update_internal(ZSTD_matchState_t* ms, const BYTE* ip, U32 const mls, U32 const rowLog, U32 const rowMask, U32 const useCache) { - U32* const hashTable = ms->hashTable; - U16* const tagTable = ms->tagTable; - U32 const hashLog = ms->rowHashLog; + U32 idx = ms->nextToUpdate; const BYTE* const base = ms->window.base; const U32 target = (U32)(ip - base); - U32 idx = ms->nextToUpdate; + const U32 kSkipThreshold = 384; + const U32 kMaxMatchStartPositionsToUpdate = 96; + const U32 kMaxMatchEndPositionsToUpdate = 32; - DEBUGLOG(6, "ZSTD_row_update_internal(): nextToUpdate=%u, current=%u", idx, target); - for (; idx < target; ++idx) { - U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, idx, hashLog, rowLog, mls) - : (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); - U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; - U32* const row = hashTable + relRow; - BYTE* tagRow = (BYTE*)(tagTable + relRow); /* Though tagTable is laid out as a table of U16, each tag is only 1 byte. - Explicit cast allows us to get exact desired position within each row */ - U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); - - assert(hash == ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls)); - ((BYTE*)tagRow)[pos + ZSTD_ROW_HASH_TAG_OFFSET] = hash & ZSTD_ROW_HASH_TAG_MASK; - row[pos] = idx; + if (useCache) { + /* Only skip positions when using hash cache, i.e. + * if we are loading a dict, don't skip anything. + * If we decide to skip, then we only update a set number + * of positions at the beginning and end of the match. + */ + if (UNLIKELY(target - idx > kSkipThreshold)) { + U32 const bound = idx + kMaxMatchStartPositionsToUpdate; + ZSTD_row_update_internalImpl(ms, idx, bound, mls, rowLog, rowMask, useCache); + idx = target - kMaxMatchEndPositionsToUpdate; + ZSTD_row_fillHashCache(ms, base, rowLog, mls, idx, ip+1); + } } + assert(target >= idx); + ZSTD_row_update_internalImpl(ms, idx, target, mls, rowLog, rowMask, useCache); ms->nextToUpdate = target; } @@ -1217,34 +926,178 @@ * processing. */ void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip) { - const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5; + const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6); const U32 rowMask = (1u << rowLog) - 1; const U32 mls = MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */); DEBUGLOG(5, "ZSTD_row_update(), rowLog=%u", rowLog); - ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* dont use cache */); + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* don't use cache */); } -/* Returns a ZSTD_VecMask (U32) that has the nth bit set to 1 if the newly-computed "tag" matches - * the hash at the nth position in a row of the tagTable. +/* Returns the mask width of bits group of which will be set to 1. Given not all + * architectures have easy movemask instruction, this helps to iterate over + * groups of bits easier and faster. */ -FORCE_INLINE_TEMPLATE -ZSTD_VecMask ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head, const U32 rowEntries) { - ZSTD_VecMask matches = 0; - if (rowEntries == 16) { - ZSTD_Vec128 hashes = ZSTD_Vec128_read(tagRow + ZSTD_ROW_HASH_TAG_OFFSET); - ZSTD_Vec128 expandedTags = ZSTD_Vec128_set8(tag); - matches = ZSTD_Vec128_cmpMask8(hashes, expandedTags); - } else if (rowEntries == 32) { - ZSTD_Vec256 hashes = ZSTD_Vec256_read(tagRow + ZSTD_ROW_HASH_TAG_OFFSET); - ZSTD_Vec256 expandedTags = ZSTD_Vec256_set8(tag); - matches = ZSTD_Vec256_cmpMask8(hashes, expandedTags); - } else { - assert(0); +FORCE_INLINE_TEMPLATE U32 +ZSTD_row_matchMaskGroupWidth(const U32 rowEntries) +{ + assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); + assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES); + (void)rowEntries; +#if defined(ZSTD_ARCH_ARM_NEON) + /* NEON path only works for little endian */ + if (!MEM_isLittleEndian()) { + return 1; } - /* Each row is a circular buffer beginning at the value of "head". So we must rotate the "matches" bitfield - to match up with the actual layout of the entries within the hashTable */ - return ZSTD_VecMask_rotateRight(matches, head, rowEntries); + if (rowEntries == 16) { + return 4; + } + if (rowEntries == 32) { + return 2; + } + if (rowEntries == 64) { + return 1; + } +#endif + return 1; +} + +#if defined(ZSTD_ARCH_X86_SSE2) +FORCE_INLINE_TEMPLATE ZSTD_VecMask +ZSTD_row_getSSEMask(int nbChunks, const BYTE* const src, const BYTE tag, const U32 head) +{ + const __m128i comparisonMask = _mm_set1_epi8((char)tag); + int matches[4] = {0}; + int i; + assert(nbChunks == 1 || nbChunks == 2 || nbChunks == 4); + for (i=0; i<nbChunks; i++) { + const __m128i chunk = _mm_loadu_si128((const __m128i*)(const void*)(src + 16*i)); + const __m128i equalMask = _mm_cmpeq_epi8(chunk, comparisonMask); + matches[i] = _mm_movemask_epi8(equalMask); + } + if (nbChunks == 1) return ZSTD_rotateRight_U16((U16)matches[0], head); + if (nbChunks == 2) return ZSTD_rotateRight_U32((U32)matches[1] << 16 | (U32)matches[0], head); + assert(nbChunks == 4); + return ZSTD_rotateRight_U64((U64)matches[3] << 48 | (U64)matches[2] << 32 | (U64)matches[1] << 16 | (U64)matches[0], head); +} +#endif + +#if defined(ZSTD_ARCH_ARM_NEON) +FORCE_INLINE_TEMPLATE ZSTD_VecMask +ZSTD_row_getNEONMask(const U32 rowEntries, const BYTE* const src, const BYTE tag, const U32 headGrouped) +{ + assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); + if (rowEntries == 16) { + /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits. + * After that groups of 4 bits represent the equalMask. We lower + * all bits except the highest in these groups by doing AND with + * 0x88 = 0b10001000. + */ + const uint8x16_t chunk = vld1q_u8(src); + const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag))); + const uint8x8_t res = vshrn_n_u16(equalMask, 4); + const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0); + return ZSTD_rotateRight_U64(matches, headGrouped) & 0x8888888888888888ull; + } else if (rowEntries == 32) { + /* Same idea as with rowEntries == 16 but doing AND with + * 0x55 = 0b01010101. + */ + const uint16x8x2_t chunk = vld2q_u16((const uint16_t*)(const void*)src); + const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]); + const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]); + const uint8x16_t dup = vdupq_n_u8(tag); + const uint8x8_t t0 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk0, dup)), 6); + const uint8x8_t t1 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk1, dup)), 6); + const uint8x8_t res = vsli_n_u8(t0, t1, 4); + const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0) ; + return ZSTD_rotateRight_U64(matches, headGrouped) & 0x5555555555555555ull; + } else { /* rowEntries == 64 */ + const uint8x16x4_t chunk = vld4q_u8(src); + const uint8x16_t dup = vdupq_n_u8(tag); + const uint8x16_t cmp0 = vceqq_u8(chunk.val[0], dup); + const uint8x16_t cmp1 = vceqq_u8(chunk.val[1], dup); + const uint8x16_t cmp2 = vceqq_u8(chunk.val[2], dup); + const uint8x16_t cmp3 = vceqq_u8(chunk.val[3], dup); + + const uint8x16_t t0 = vsriq_n_u8(cmp1, cmp0, 1); + const uint8x16_t t1 = vsriq_n_u8(cmp3, cmp2, 1); + const uint8x16_t t2 = vsriq_n_u8(t1, t0, 2); + const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4); + const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4); + const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0); + return ZSTD_rotateRight_U64(matches, headGrouped); + } +} +#endif + +/* Returns a ZSTD_VecMask (U64) that has the nth group (determined by + * ZSTD_row_matchMaskGroupWidth) of bits set to 1 if the newly-computed "tag" + * matches the hash at the nth position in a row of the tagTable. + * Each row is a circular buffer beginning at the value of "headGrouped". So we + * must rotate the "matches" bitfield to match up with the actual layout of the + * entries within the hashTable */ +FORCE_INLINE_TEMPLATE ZSTD_VecMask +ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 headGrouped, const U32 rowEntries) +{ + const BYTE* const src = tagRow; + assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); + assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES); + assert(ZSTD_row_matchMaskGroupWidth(rowEntries) * rowEntries <= sizeof(ZSTD_VecMask) * 8); + +#if defined(ZSTD_ARCH_X86_SSE2) + + return ZSTD_row_getSSEMask(rowEntries / 16, src, tag, headGrouped); + +#else /* SW or NEON-LE */ + +# if defined(ZSTD_ARCH_ARM_NEON) + /* This NEON path only works for little endian - otherwise use SWAR below */ + if (MEM_isLittleEndian()) { + return ZSTD_row_getNEONMask(rowEntries, src, tag, headGrouped); + } +# endif /* ZSTD_ARCH_ARM_NEON */ + /* SWAR */ + { const int chunkSize = sizeof(size_t); + const size_t shiftAmount = ((chunkSize * 8) - chunkSize); + const size_t xFF = ~((size_t)0); + const size_t x01 = xFF / 0xFF; + const size_t x80 = x01 << 7; + const size_t splatChar = tag * x01; + ZSTD_VecMask matches = 0; + int i = rowEntries - chunkSize; + assert((sizeof(size_t) == 4) || (sizeof(size_t) == 8)); + if (MEM_isLittleEndian()) { /* runtime check so have two loops */ + const size_t extractMagic = (xFF / 0x7F) >> chunkSize; + do { + size_t chunk = MEM_readST(&src[i]); + chunk ^= splatChar; + chunk = (((chunk | x80) - x01) | chunk) & x80; + matches <<= chunkSize; + matches |= (chunk * extractMagic) >> shiftAmount; + i -= chunkSize; + } while (i >= 0); + } else { /* big endian: reverse bits during extraction */ + const size_t msb = xFF ^ (xFF >> 1); + const size_t extractMagic = (msb / 0x1FF) | msb; + do { + size_t chunk = MEM_readST(&src[i]); + chunk ^= splatChar; + chunk = (((chunk | x80) - x01) | chunk) & x80; + matches <<= chunkSize; + matches |= ((chunk >> 7) * extractMagic) >> shiftAmount; + i -= chunkSize; + } while (i >= 0); + } + matches = ~matches; + if (rowEntries == 16) { + return ZSTD_rotateRight_U16((U16)matches, headGrouped); + } else if (rowEntries == 32) { + return ZSTD_rotateRight_U32((U32)matches, headGrouped); + } else { + return ZSTD_rotateRight_U64((U64)matches, headGrouped); + } + } +#endif } /* The high-level approach of the SIMD row based match finder is as follows: @@ -1263,7 +1116,7 @@ * - Pick the longest match. */ FORCE_INLINE_TEMPLATE -size_t ZSTD_RowFindBestMatch_generic ( +size_t ZSTD_RowFindBestMatch( ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iLimit, size_t* offsetPtr, @@ -1271,7 +1124,7 @@ const U32 rowLog) { U32* const hashTable = ms->hashTable; - U16* const tagTable = ms->tagTable; + BYTE* const tagTable = ms->tagTable; U32* const hashCache = ms->hashCache; const U32 hashLog = ms->rowHashLog; const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -1289,16 +1142,21 @@ const U32 rowEntries = (1U << rowLog); const U32 rowMask = rowEntries - 1; const U32 cappedSearchLog = MIN(cParams->searchLog, rowLog); /* nb of searches is capped at nb entries per row */ + const U32 groupWidth = ZSTD_row_matchMaskGroupWidth(rowEntries); + const U64 hashSalt = ms->hashSalt; U32 nbAttempts = 1U << cappedSearchLog; size_t ml=4-1; + U32 hash; /* DMS/DDS variables that may be referenced laster */ const ZSTD_matchState_t* const dms = ms->dictMatchState; - size_t ddsIdx; - U32 ddsExtraAttempts; /* cctx hash tables are limited in searches, but allow extra searches into DDS */ - U32 dmsTag; - U32* dmsRow; - BYTE* dmsTagRow; + + /* Initialize the following variables to satisfy static analyzer */ + size_t ddsIdx = 0; + U32 ddsExtraAttempts = 0; /* cctx hash tables are limited in searches, but allow extra searches into DDS */ + U32 dmsTag = 0; + U32* dmsRow = NULL; + BYTE* dmsTagRow = NULL; if (dictMode == ZSTD_dedicatedDictSearch) { const U32 ddsHashLog = dms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG; @@ -1312,7 +1170,7 @@ if (dictMode == ZSTD_dictMatchState) { /* Prefetch DMS rows */ U32* const dmsHashTable = dms->hashTable; - U16* const dmsTagTable = dms->tagTable; + BYTE* const dmsTagTable = dms->tagTable; U32 const dmsHash = (U32)ZSTD_hashPtr(ip, dms->rowHashLog + ZSTD_ROW_HASH_TAG_BITS, mls); U32 const dmsRelRow = (dmsHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; dmsTag = dmsHash & ZSTD_ROW_HASH_TAG_MASK; @@ -1322,23 +1180,34 @@ } /* Update the hashTable and tagTable up to (but not including) ip */ - ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */); + if (!ms->lazySkipping) { + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */); + hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls, hashSalt); + } else { + /* Stop inserting every position when in the lazy skipping mode. + * The hash cache is also not kept up to date in this mode. + */ + hash = (U32)ZSTD_hashPtrSalted(ip, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt); + ms->nextToUpdate = curr; + } + ms->hashSaltEntropy += hash; /* collect salt entropy */ + { /* Get the hash for ip, compute the appropriate row */ - U32 const hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls); U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; U32 const tag = hash & ZSTD_ROW_HASH_TAG_MASK; U32* const row = hashTable + relRow; BYTE* tagRow = (BYTE*)(tagTable + relRow); - U32 const head = *tagRow & rowMask; - U32 matchBuffer[32 /* maximum nb entries per row */]; + U32 const headGrouped = (*tagRow & rowMask) * groupWidth; + U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES]; size_t numMatches = 0; size_t currMatch = 0; - ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, head, rowEntries); + ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, headGrouped, rowEntries); /* Cycle through the matches and prefetch */ - for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) { - U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask; + for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) { + U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask; U32 const matchIndex = row[matchPos]; + if(matchPos == 0) continue; assert(numMatches < rowEntries); if (matchIndex < lowLimit) break; @@ -1348,13 +1217,14 @@ PREFETCH_L1(dictBase + matchIndex); } matchBuffer[numMatches++] = matchIndex; + --nbAttempts; } /* Speed opt: insert current byte into hashtable too. This allows us to avoid one iteration of the loop in ZSTD_row_update_internal() at the next search. */ { U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); - tagRow[pos + ZSTD_ROW_HASH_TAG_OFFSET] = (BYTE)tag; + tagRow[pos] = (BYTE)tag; row[pos] = ms->nextToUpdate++; } @@ -1368,7 +1238,8 @@ if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) { const BYTE* const match = base + matchIndex; assert(matchIndex >= dictLimit); /* ensures this is true if dictMode != ZSTD_extDict */ - if (match[ml] == ip[ml]) /* potentially better */ + /* read 4B starting from (match + ml + 1 - sizeof(U32)) */ + if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3)) /* potentially better */ currentMl = ZSTD_count(ip, match, iLimit); } else { const BYTE* const match = dictBase + matchIndex; @@ -1380,12 +1251,13 @@ /* Save best solution */ if (currentMl > ml) { ml = currentMl; - *offsetPtr = curr - matchIndex + ZSTD_REP_MOVE; + *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex); if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */ } } } + assert(nbAttempts <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */ if (dictMode == ZSTD_dedicatedDictSearch) { ml = ZSTD_dedicatedDictSearch_lazy_search(offsetPtr, ml, nbAttempts + ddsExtraAttempts, dms, ip, iLimit, prefixStart, curr, dictLimit, ddsIdx); @@ -1397,19 +1269,21 @@ const U32 dmsSize = (U32)(dmsEnd - dmsBase); const U32 dmsIndexDelta = dictLimit - dmsSize; - { U32 const head = *dmsTagRow & rowMask; - U32 matchBuffer[32 /* maximum nb row entries */]; + { U32 const headGrouped = (*dmsTagRow & rowMask) * groupWidth; + U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES]; size_t numMatches = 0; size_t currMatch = 0; - ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries); + ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, headGrouped, rowEntries); - for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) { - U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask; + for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) { + U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask; U32 const matchIndex = dmsRow[matchPos]; + if(matchPos == 0) continue; if (matchIndex < dmsLowestIndex) break; PREFETCH_L1(dmsBase + matchIndex); matchBuffer[numMatches++] = matchIndex; + --nbAttempts; } /* Return the longest match */ @@ -1427,7 +1301,8 @@ if (currentMl > ml) { ml = currentMl; - *offsetPtr = curr - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE; + assert(curr > matchIndex + dmsIndexDelta); + *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta)); if (ip+currentMl == iLimit) break; } } @@ -1436,83 +1311,183 @@ return ml; } -/* Inlining is important to hardwire a hot branch (template emulation) */ -FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_selectMLS ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - const ZSTD_dictMode_e dictMode, size_t* offsetPtr, const U32 rowLog) -{ - switch(ms->cParams.minMatch) - { - default : /* includes case 3 */ - case 4 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, dictMode, rowLog); - case 5 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, dictMode, rowLog); - case 7 : - case 6 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, dictMode, rowLog); - } -} -FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_selectRowLog ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5); - switch(cappedSearchLog) - { - default : - case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_noDict, offsetPtr, 4); - case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_noDict, offsetPtr, 5); - } -} +/** + * Generate search functions templated on (dictMode, mls, rowLog). + * These functions are outlined for code size & compilation time. + * ZSTD_searchMax() dispatches to the correct implementation function. + * + * TODO: The start of the search function involves loading and calculating a + * bunch of constants from the ZSTD_matchState_t. These computations could be + * done in an initialization function, and saved somewhere in the match state. + * Then we could pass a pointer to the saved state instead of the match state, + * and avoid duplicate computations. + * + * TODO: Move the match re-winding into searchMax. This improves compression + * ratio, and unlocks further simplifications with the next TODO. + * + * TODO: Try moving the repcode search into searchMax. After the re-winding + * and repcode search are in searchMax, there is no more logic in the match + * finder loop that requires knowledge about the dictMode. So we should be + * able to avoid force inlining it, and we can join the extDict loop with + * the single segment loop. It should go in searchMax instead of its own + * function to avoid having multiple virtual function calls per search. + */ -FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_dictMatchState_selectRowLog( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5); - switch(cappedSearchLog) - { - default : - case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dictMatchState, offsetPtr, 4); - case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dictMatchState, offsetPtr, 5); - } -} +#define ZSTD_BT_SEARCH_FN(dictMode, mls) ZSTD_BtFindBestMatch_##dictMode##_##mls +#define ZSTD_HC_SEARCH_FN(dictMode, mls) ZSTD_HcFindBestMatch_##dictMode##_##mls +#define ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog) ZSTD_RowFindBestMatch_##dictMode##_##mls##_##rowLog -FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_dedicatedDictSearch_selectRowLog( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5); - switch(cappedSearchLog) - { - default : - case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dedicatedDictSearch, offsetPtr, 4); - case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dedicatedDictSearch, offsetPtr, 5); - } -} +#define ZSTD_SEARCH_FN_ATTRS FORCE_NOINLINE -FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_extDict_selectRowLog ( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* const iLimit, - size_t* offsetPtr) -{ - const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5); - switch(cappedSearchLog) - { - default : - case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_extDict, offsetPtr, 4); - case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_extDict, offsetPtr, 5); - } -} +#define GEN_ZSTD_BT_SEARCH_FN(dictMode, mls) \ + ZSTD_SEARCH_FN_ATTRS size_t ZSTD_BT_SEARCH_FN(dictMode, mls)( \ + ZSTD_matchState_t* ms, \ + const BYTE* ip, const BYTE* const iLimit, \ + size_t* offBasePtr) \ + { \ + assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \ + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, mls, ZSTD_##dictMode); \ + } \ +#define GEN_ZSTD_HC_SEARCH_FN(dictMode, mls) \ + ZSTD_SEARCH_FN_ATTRS size_t ZSTD_HC_SEARCH_FN(dictMode, mls)( \ + ZSTD_matchState_t* ms, \ + const BYTE* ip, const BYTE* const iLimit, \ + size_t* offsetPtr) \ + { \ + assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \ + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode); \ + } \ + +#define GEN_ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog) \ + ZSTD_SEARCH_FN_ATTRS size_t ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)( \ + ZSTD_matchState_t* ms, \ + const BYTE* ip, const BYTE* const iLimit, \ + size_t* offsetPtr) \ + { \ + assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \ + assert(MAX(4, MIN(6, ms->cParams.searchLog)) == rowLog); \ + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode, rowLog); \ + } \ + +#define ZSTD_FOR_EACH_ROWLOG(X, dictMode, mls) \ + X(dictMode, mls, 4) \ + X(dictMode, mls, 5) \ + X(dictMode, mls, 6) + +#define ZSTD_FOR_EACH_MLS_ROWLOG(X, dictMode) \ + ZSTD_FOR_EACH_ROWLOG(X, dictMode, 4) \ + ZSTD_FOR_EACH_ROWLOG(X, dictMode, 5) \ + ZSTD_FOR_EACH_ROWLOG(X, dictMode, 6) + +#define ZSTD_FOR_EACH_MLS(X, dictMode) \ + X(dictMode, 4) \ + X(dictMode, 5) \ + X(dictMode, 6) + +#define ZSTD_FOR_EACH_DICT_MODE(X, ...) \ + X(__VA_ARGS__, noDict) \ + X(__VA_ARGS__, extDict) \ + X(__VA_ARGS__, dictMatchState) \ + X(__VA_ARGS__, dedicatedDictSearch) + +/* Generate row search fns for each combination of (dictMode, mls, rowLog) */ +ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS_ROWLOG, GEN_ZSTD_ROW_SEARCH_FN) +/* Generate binary Tree search fns for each combination of (dictMode, mls) */ +ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_BT_SEARCH_FN) +/* Generate hash chain search fns for each combination of (dictMode, mls) */ +ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_HC_SEARCH_FN) + +typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e; + +#define GEN_ZSTD_CALL_BT_SEARCH_FN(dictMode, mls) \ + case mls: \ + return ZSTD_BT_SEARCH_FN(dictMode, mls)(ms, ip, iend, offsetPtr); +#define GEN_ZSTD_CALL_HC_SEARCH_FN(dictMode, mls) \ + case mls: \ + return ZSTD_HC_SEARCH_FN(dictMode, mls)(ms, ip, iend, offsetPtr); +#define GEN_ZSTD_CALL_ROW_SEARCH_FN(dictMode, mls, rowLog) \ + case rowLog: \ + return ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)(ms, ip, iend, offsetPtr); + +#define ZSTD_SWITCH_MLS(X, dictMode) \ + switch (mls) { \ + ZSTD_FOR_EACH_MLS(X, dictMode) \ + } + +#define ZSTD_SWITCH_ROWLOG(dictMode, mls) \ + case mls: \ + switch (rowLog) { \ + ZSTD_FOR_EACH_ROWLOG(GEN_ZSTD_CALL_ROW_SEARCH_FN, dictMode, mls) \ + } \ + ZSTD_UNREACHABLE; \ + break; + +#define ZSTD_SWITCH_SEARCH_METHOD(dictMode) \ + switch (searchMethod) { \ + case search_hashChain: \ + ZSTD_SWITCH_MLS(GEN_ZSTD_CALL_HC_SEARCH_FN, dictMode) \ + break; \ + case search_binaryTree: \ + ZSTD_SWITCH_MLS(GEN_ZSTD_CALL_BT_SEARCH_FN, dictMode) \ + break; \ + case search_rowHash: \ + ZSTD_SWITCH_MLS(ZSTD_SWITCH_ROWLOG, dictMode) \ + break; \ + } \ + ZSTD_UNREACHABLE; + +/** + * Searches for the longest match at @p ip. + * Dispatches to the correct implementation function based on the + * (searchMethod, dictMode, mls, rowLog). We use switch statements + * here instead of using an indirect function call through a function + * pointer because after Spectre and Meltdown mitigations, indirect + * function calls can be very costly, especially in the kernel. + * + * NOTE: dictMode and searchMethod should be templated, so those switch + * statements should be optimized out. Only the mls & rowLog switches + * should be left. + * + * @param ms The match state. + * @param ip The position to search at. + * @param iend The end of the input data. + * @param[out] offsetPtr Stores the match offset into this pointer. + * @param mls The minimum search length, in the range [4, 6]. + * @param rowLog The row log (if applicable), in the range [4, 6]. + * @param searchMethod The search method to use (templated). + * @param dictMode The dictMode (templated). + * + * @returns The length of the longest match found, or < mls if no match is found. + * If a match is found its offset is stored in @p offsetPtr. + */ +FORCE_INLINE_TEMPLATE size_t ZSTD_searchMax( + ZSTD_matchState_t* ms, + const BYTE* ip, + const BYTE* iend, + size_t* offsetPtr, + U32 const mls, + U32 const rowLog, + searchMethod_e const searchMethod, + ZSTD_dictMode_e const dictMode) +{ + if (dictMode == ZSTD_noDict) { + ZSTD_SWITCH_SEARCH_METHOD(noDict) + } else if (dictMode == ZSTD_extDict) { + ZSTD_SWITCH_SEARCH_METHOD(extDict) + } else if (dictMode == ZSTD_dictMatchState) { + ZSTD_SWITCH_SEARCH_METHOD(dictMatchState) + } else if (dictMode == ZSTD_dedicatedDictSearch) { + ZSTD_SWITCH_SEARCH_METHOD(dedicatedDictSearch) + } + ZSTD_UNREACHABLE; + return 0; +} /* ******************************* * Common parser - lazy strategy *********************************/ -typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e; FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_lazy_generic( @@ -1526,47 +1501,15 @@ const BYTE* ip = istart; const BYTE* anchor = istart; const BYTE* const iend = istart + srcSize; - const BYTE* const ilimit = searchMethod == search_rowHash ? iend - 8 - ZSTD_ROW_HASH_CACHE_SIZE : iend - 8; + const BYTE* const ilimit = (searchMethod == search_rowHash) ? iend - 8 - ZSTD_ROW_HASH_CACHE_SIZE : iend - 8; const BYTE* const base = ms->window.base; const U32 prefixLowestIndex = ms->window.dictLimit; const BYTE* const prefixLowest = base + prefixLowestIndex; - const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5; + const U32 mls = BOUNDED(4, ms->cParams.minMatch, 6); + const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6); - typedef size_t (*searchMax_f)( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); - - /** - * This table is indexed first by the four ZSTD_dictMode_e values, and then - * by the two searchMethod_e values. NULLs are placed for configurations - * that should never occur (extDict modes go to the other implementation - * below and there is no DDSS for binary tree search yet). - */ - const searchMax_f searchFuncs[4][3] = { - { - ZSTD_HcFindBestMatch_selectMLS, - ZSTD_BtFindBestMatch_selectMLS, - ZSTD_RowFindBestMatch_selectRowLog - }, - { - NULL, - NULL, - NULL - }, - { - ZSTD_HcFindBestMatch_dictMatchState_selectMLS, - ZSTD_BtFindBestMatch_dictMatchState_selectMLS, - ZSTD_RowFindBestMatch_dictMatchState_selectRowLog - }, - { - ZSTD_HcFindBestMatch_dedicatedDictSearch_selectMLS, - NULL, - ZSTD_RowFindBestMatch_dedicatedDictSearch_selectRowLog - } - }; - - searchMax_f const searchMax = searchFuncs[dictMode][(int)searchMethod]; - U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0; + U32 offset_1 = rep[0], offset_2 = rep[1]; + U32 offsetSaved1 = 0, offsetSaved2 = 0; const int isDMS = dictMode == ZSTD_dictMatchState; const int isDDS = dictMode == ZSTD_dedicatedDictSearch; @@ -1581,16 +1524,14 @@ 0; const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictLowest)); - assert(searchMax != NULL); - DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u) (searchFunc=%u)", (U32)dictMode, (U32)searchMethod); ip += (dictAndPrefixLength == 0); if (dictMode == ZSTD_noDict) { U32 const curr = (U32)(ip - base); U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, ms->cParams.windowLog); U32 const maxRep = curr - windowLow; - if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0; - if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0; + if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0; + if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0; } if (isDxS) { /* dictMatchState repCode checks don't currently handle repCode == 0 @@ -1599,10 +1540,11 @@ assert(offset_2 <= dictAndPrefixLength); } + /* Reset the lazy skipping state */ + ms->lazySkipping = 0; + if (searchMethod == search_rowHash) { - ZSTD_row_fillHashCache(ms, base, rowLog, - MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */), - ms->nextToUpdate, ilimit); + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); } /* Match Loop */ @@ -1614,8 +1556,9 @@ #endif while (ip < ilimit) { size_t matchLength=0; - size_t offset=0; + size_t offBase = REPCODE1_TO_OFFBASE; const BYTE* start=ip+1; + DEBUGLOG(7, "search baseline (depth 0)"); /* check repCode */ if (isDxS) { @@ -1638,28 +1581,38 @@ } /* first search (depth 0) */ - { size_t offsetFound = 999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offsetFound); + { size_t offbaseFound = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &offbaseFound, mls, rowLog, searchMethod, dictMode); if (ml2 > matchLength) - matchLength = ml2, start = ip, offset=offsetFound; + matchLength = ml2, start = ip, offBase = offbaseFound; } if (matchLength < 4) { - ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ + size_t const step = ((size_t)(ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */; + ip += step; + /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time. + * In this mode we stop inserting every position into our tables, and only insert + * positions that we search, which is one in step positions. + * The exact cutoff is flexible, I've just chosen a number that is reasonably high, + * so we minimize the compression ratio loss in "normal" scenarios. This mode gets + * triggered once we've gone 2KB without finding any matches. + */ + ms->lazySkipping = step > kLazySkippingStep; continue; } /* let's try to find a better solution */ if (depth>=1) while (ip<ilimit) { + DEBUGLOG(7, "search depth 1"); ip ++; if ( (dictMode == ZSTD_noDict) - && (offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { + && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4; int const gain2 = (int)(mlRep * 3); - int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); + int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offset = 0, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } if (isDxS) { const U32 repIndex = (U32)(ip - base) - offset_1; @@ -1671,30 +1624,31 @@ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; int const gain2 = (int)(mlRep * 3); - int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); + int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offset = 0, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } } - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); + { size_t ofbCandidate=999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, dictMode); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offset = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; /* search a better one */ } } /* let's find an even better one */ if ((depth==2) && (ip<ilimit)) { + DEBUGLOG(7, "search depth 2"); ip ++; if ( (dictMode == ZSTD_noDict) - && (offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { + && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) { size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4; int const gain2 = (int)(mlRep * 4); - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offset = 0, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } if (isDxS) { const U32 repIndex = (U32)(ip - base) - offset_1; @@ -1706,48 +1660,54 @@ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4; int const gain2 = (int)(mlRep * 4); - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1); if ((mlRep >= 4) && (gain2 > gain1)) - matchLength = mlRep, offset = 0, start = ip; + matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip; } } - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); + { size_t ofbCandidate=999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, dictMode); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offset = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; } } } break; /* nothing found : store previous solution */ } /* NOTE: - * start[-offset+ZSTD_REP_MOVE-1] is undefined behavior. - * (-offset+ZSTD_REP_MOVE-1) is unsigned, and is added to start, which - * overflows the pointer, which is undefined behavior. + * Pay attention that `start[-value]` can lead to strange undefined behavior + * notably if `value` is unsigned, resulting in a large positive `-value`. */ /* catch up */ - if (offset) { + if (OFFBASE_IS_OFFSET(offBase)) { if (dictMode == ZSTD_noDict) { - while ( ((start > anchor) & (start - (offset-ZSTD_REP_MOVE) > prefixLowest)) - && (start[-1] == (start-(offset-ZSTD_REP_MOVE))[-1]) ) /* only search for offset within prefix */ + while ( ((start > anchor) & (start - OFFBASE_TO_OFFSET(offBase) > prefixLowest)) + && (start[-1] == (start-OFFBASE_TO_OFFSET(offBase))[-1]) ) /* only search for offset within prefix */ { start--; matchLength++; } } if (isDxS) { - U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE)); + U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase)); const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex; const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest; while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ } - offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE); + offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase); } /* store sequence */ _storeSequence: - { size_t const litLength = start - anchor; - ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH); + { size_t const litLength = (size_t)(start - anchor); + ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength); anchor = ip = start + matchLength; } + if (ms->lazySkipping) { + /* We've found a match, disable lazy skipping mode, and refill the hash cache. */ + if (searchMethod == search_rowHash) { + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); + } + ms->lazySkipping = 0; + } /* check immediate repcode */ if (isDxS) { @@ -1761,8 +1721,8 @@ && (MEM_read32(repMatch) == MEM_read32(ip)) ) { const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4; - offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); + offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength); ip += matchLength; anchor = ip; continue; @@ -1776,16 +1736,20 @@ && (MEM_read32(ip) == MEM_read32(ip - offset_2)) ) { /* store sequence */ matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); + offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap repcodes */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ } } } - /* Save reps for next block */ - rep[0] = offset_1 ? offset_1 : savedOffset; - rep[1] = offset_2 ? offset_2 : savedOffset; + /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0), + * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */ + offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2; + + /* save reps for next block */ + rep[0] = offset_1 ? offset_1 : offsetSaved1; + rep[1] = offset_2 ? offset_2 : offsetSaved2; /* Return the last literals size */ return (size_t)(iend - anchor); @@ -1954,27 +1918,20 @@ const BYTE* const dictEnd = dictBase + dictLimit; const BYTE* const dictStart = dictBase + ms->window.lowLimit; const U32 windowLog = ms->cParams.windowLog; - const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5; + const U32 mls = BOUNDED(4, ms->cParams.minMatch, 6); + const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6); - typedef size_t (*searchMax_f)( - ZSTD_matchState_t* ms, - const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr); - const searchMax_f searchFuncs[3] = { - ZSTD_HcFindBestMatch_extDict_selectMLS, - ZSTD_BtFindBestMatch_extDict_selectMLS, - ZSTD_RowFindBestMatch_extDict_selectRowLog - }; - searchMax_f searchMax = searchFuncs[(int)searchMethod]; U32 offset_1 = rep[0], offset_2 = rep[1]; DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic (searchFunc=%u)", (U32)searchMethod); + /* Reset the lazy skipping state */ + ms->lazySkipping = 0; + /* init */ ip += (ip == prefixStart); if (searchMethod == search_rowHash) { - ZSTD_row_fillHashCache(ms, base, rowLog, - MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */), - ms->nextToUpdate, ilimit); + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); } /* Match Loop */ @@ -1986,7 +1943,7 @@ #endif while (ip < ilimit) { size_t matchLength=0; - size_t offset=0; + size_t offBase = REPCODE1_TO_OFFBASE; const BYTE* start=ip+1; U32 curr = (U32)(ip-base); @@ -1996,7 +1953,7 @@ const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow */ - & (offset_1 < curr+1 - windowLow) ) /* note: we are searching at curr+1 */ + & (offset_1 <= curr+1 - windowLow) ) /* note: we are searching at curr+1 */ if (MEM_read32(ip+1) == MEM_read32(repMatch)) { /* repcode detected we should take it */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; @@ -2005,14 +1962,23 @@ } } /* first search (depth 0) */ - { size_t offsetFound = 999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offsetFound); + { size_t ofbCandidate = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict); if (ml2 > matchLength) - matchLength = ml2, start = ip, offset=offsetFound; + matchLength = ml2, start = ip, offBase = ofbCandidate; } - if (matchLength < 4) { - ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ + if (matchLength < 4) { + size_t const step = ((size_t)(ip-anchor) >> kSearchStrength); + ip += step + 1; /* jump faster over incompressible sections */ + /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time. + * In this mode we stop inserting every position into our tables, and only insert + * positions that we search, which is one in step positions. + * The exact cutoff is flexible, I've just chosen a number that is reasonably high, + * so we minimize the compression ratio loss in "normal" scenarios. This mode gets + * triggered once we've gone 2KB without finding any matches. + */ + ms->lazySkipping = step > kLazySkippingStep; continue; } @@ -2022,30 +1988,30 @@ ip ++; curr++; /* check repCode */ - if (offset) { + if (offBase) { const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog); const U32 repIndex = (U32)(curr - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments */ - & (offset_1 < curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */ + & (offset_1 <= curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; int const gain2 = (int)(repLength * 3); - int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1); + int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1); if ((repLength >= 4) && (gain2 > gain1)) - matchLength = repLength, offset = 0, start = ip; + matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip; } } /* search match, depth 1 */ - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4); + { size_t ofbCandidate = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offset = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; /* search a better one */ } } @@ -2054,50 +2020,57 @@ ip ++; curr++; /* check repCode */ - if (offset) { + if (offBase) { const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog); const U32 repIndex = (U32)(curr - offset_1); const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments */ - & (offset_1 < curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */ + & (offset_1 <= curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; int const gain2 = (int)(repLength * 4); - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1); + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1); if ((repLength >= 4) && (gain2 > gain1)) - matchLength = repLength, offset = 0, start = ip; + matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip; } } /* search match, depth 2 */ - { size_t offset2=999999999; - size_t const ml2 = searchMax(ms, ip, iend, &offset2); - int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */ - int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7); + { size_t ofbCandidate = 999999999; + size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict); + int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */ + int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7); if ((ml2 >= 4) && (gain2 > gain1)) { - matchLength = ml2, offset = offset2, start = ip; + matchLength = ml2, offBase = ofbCandidate, start = ip; continue; } } } break; /* nothing found : store previous solution */ } /* catch up */ - if (offset) { - U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE)); + if (OFFBASE_IS_OFFSET(offBase)) { + U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase)); const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex; const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart; while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */ - offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE); + offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase); } /* store sequence */ _storeSequence: - { size_t const litLength = start - anchor; - ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH); + { size_t const litLength = (size_t)(start - anchor); + ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength); anchor = ip = start + matchLength; } + if (ms->lazySkipping) { + /* We've found a match, disable lazy skipping mode, and refill the hash cache. */ + if (searchMethod == search_rowHash) { + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); + } + ms->lazySkipping = 0; + } /* check immediate repcode */ while (ip <= ilimit) { @@ -2107,13 +2080,13 @@ const BYTE* const repBase = repIndex < dictLimit ? dictBase : base; const BYTE* const repMatch = repBase + repIndex; if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments */ - & (offset_2 < repCurrent - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */ + & (offset_2 <= repCurrent - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */ if (MEM_read32(ip) == MEM_read32(repMatch)) { /* repcode detected we should take it */ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend; matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4; - offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */ - ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH); + offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap offset history */ + ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength); ip += matchLength; anchor = ip; continue; /* faster when present ... (?) */ @@ -2179,7 +2152,6 @@ size_t ZSTD_compressBlock_lazy2_extDict_row( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize) - { return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2); }
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.h b/Utilities/cmzstd/lib/compress/zstd_lazy.h index 150f7b3..3bde673 100644 --- a/Utilities/cmzstd/lib/compress/zstd_lazy.h +++ b/Utilities/cmzstd/lib/compress/zstd_lazy.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -25,6 +25,8 @@ */ #define ZSTD_LAZY_DDSS_BUCKET_LOG 2 +#define ZSTD_ROW_HASH_TAG_BITS 8 /* nb bits to use for the tag */ + U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip); void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip); @@ -116,7 +118,7 @@ size_t ZSTD_compressBlock_btlazy2_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize); - + #if defined (__cplusplus) }
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.c b/Utilities/cmzstd/lib/compress/zstd_ldm.c index fa4ebea..3d74ff1 100644 --- a/Utilities/cmzstd/lib/compress/zstd_ldm.c +++ b/Utilities/cmzstd/lib/compress/zstd_ldm.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -159,12 +159,12 @@ size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog); size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize) + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t)); - return params.enableLdm ? totalSize : 0; + return params.enableLdm == ZSTD_ps_enable ? totalSize : 0; } size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize) { - return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0; + return params.enableLdm == ZSTD_ps_enable ? (maxChunkSize / params.minMatchLength) : 0; } /** ZSTD_ldm_getBucket() : @@ -242,11 +242,11 @@ switch(ms->cParams.strategy) { case ZSTD_fast: - ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast); + ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx); break; case ZSTD_dfast: - ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast); + ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx); break; case ZSTD_greedy: @@ -478,7 +478,7 @@ */ if (anchor > ip + hashed) { ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength); - /* Continue the outter loop at anchor (ip + hashed == anchor). */ + /* Continue the outer loop at anchor (ip + hashed == anchor). */ ip = anchor - hashed; break; } @@ -549,7 +549,7 @@ * the window through early invalidation. * TODO: * Test the chunk size. * * Try invalidation after the sequence generation and test the - * the offset against maxDist directly. + * offset against maxDist directly. * * NOTE: Because of dictionaries + sequence splitting we MUST make sure * that any offset used is valid at the END of the sequence, since it may @@ -579,7 +579,9 @@ return 0; } -void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) { +void +ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) +{ while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) { rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos; if (srcSize <= seq->litLength) { @@ -657,7 +659,7 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + ZSTD_paramSwitch_e useRowMatchFinder, void const* src, size_t srcSize) { const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -709,8 +711,8 @@ rep[0] = sequence.offset; /* Store the sequence */ ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend, - sequence.offset + ZSTD_REP_MOVE, - sequence.matchLength - MINMATCH); + OFFSET_TO_OFFBASE(sequence.offset), + sequence.matchLength); ip += sequence.matchLength; } }
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.h b/Utilities/cmzstd/lib/compress/zstd_ldm.h index 393466f..f147021 100644 --- a/Utilities/cmzstd/lib/compress/zstd_ldm.h +++ b/Utilities/cmzstd/lib/compress/zstd_ldm.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -66,7 +66,7 @@ */ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_useRowMatchFinderMode_e useRowMatchFinder, + ZSTD_paramSwitch_e useRowMatchFinder, void const* src, size_t srcSize); /**
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h b/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h index e5c24d8..ef34bc5 100644 --- a/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h +++ b/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -11,7 +11,10 @@ #ifndef ZSTD_LDM_GEARTAB_H #define ZSTD_LDM_GEARTAB_H -static U64 ZSTD_ldm_gearTab[256] = { +#include "../common/compiler.h" /* UNUSED_ATTR */ +#include "../common/mem.h" /* U64 */ + +static UNUSED_ATTR const U64 ZSTD_ldm_gearTab[256] = { 0xf5b8f72c5f77775c, 0x84935f266b7ac412, 0xb647ada9ca730ccc, 0xb065bb4b114fb1de, 0x34584e7e8c3a9fd0, 0x4e97e17c6ae26b05, 0x3a03d743bc99a604, 0xcecd042422c4044f, 0x76de76c58524259e,
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.c b/Utilities/cmzstd/lib/compress/zstd_opt.c index 402a7e5..f02a760 100644 --- a/Utilities/cmzstd/lib/compress/zstd_opt.c +++ b/Utilities/cmzstd/lib/compress/zstd_opt.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,40 +14,47 @@ #define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats */ -#define ZSTD_FREQ_DIV 4 /* log factor when using previous stats to init next stats */ #define ZSTD_MAX_PRICE (1<<30) -#define ZSTD_PREDEF_THRESHOLD 1024 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */ +#define ZSTD_PREDEF_THRESHOLD 8 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */ /*-************************************* * Price functions for optimal parser ***************************************/ -#if 0 /* approximation at bit level */ +#if 0 /* approximation at bit level (for tests) */ # define BITCOST_ACCURACY 0 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat) ((void)opt, ZSTD_bitWeight(stat)) -#elif 0 /* fractional bit accuracy */ +# define WEIGHT(stat, opt) ((void)(opt), ZSTD_bitWeight(stat)) +#elif 0 /* fractional bit accuracy (for tests) */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat)) +# define WEIGHT(stat,opt) ((void)(opt), ZSTD_fracWeight(stat)) #else /* opt==approx, ultra==accurate */ # define BITCOST_ACCURACY 8 # define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY) -# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) +# define WEIGHT(stat,opt) ((opt) ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat)) #endif +/* ZSTD_bitWeight() : + * provide estimated "cost" of a stat in full bits only */ MEM_STATIC U32 ZSTD_bitWeight(U32 stat) { return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER); } +/* ZSTD_fracWeight() : + * provide fractional-bit "cost" of a stat, + * using linear interpolation approximation */ MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat) { U32 const stat = rawStat + 1; U32 const hb = ZSTD_highbit32(stat); U32 const BWeight = hb * BITCOST_MULTIPLIER; + /* Fweight was meant for "Fractional weight" + * but it's effectively a value between 1 and 2 + * using fixed point arithmetic */ U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb; U32 const weight = BWeight + FWeight; assert(hb + BITCOST_ACCURACY < 31); @@ -58,7 +65,7 @@ /* debugging function, * @return price in bytes as fractional value * for debug messages only */ -MEM_STATIC double ZSTD_fCost(U32 price) +MEM_STATIC double ZSTD_fCost(int price) { return (double)price / (BITCOST_MULTIPLIER*8); } @@ -66,7 +73,7 @@ static int ZSTD_compressedLiterals(optState_t const* const optPtr) { - return optPtr->literalCompressionMode != ZSTD_lcm_uncompressed; + return optPtr->literalCompressionMode != ZSTD_ps_disable; } static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel) @@ -79,25 +86,52 @@ } -/* ZSTD_downscaleStat() : - * reduce all elements in table by a factor 2^(ZSTD_FREQ_DIV+malus) - * return the resulting sum of elements */ -static U32 ZSTD_downscaleStat(unsigned* table, U32 lastEltIndex, int malus) +static U32 sum_u32(const unsigned table[], size_t nbElts) +{ + size_t n; + U32 total = 0; + for (n=0; n<nbElts; n++) { + total += table[n]; + } + return total; +} + +typedef enum { base_0possible=0, base_1guaranteed=1 } base_directive_e; + +static U32 +ZSTD_downscaleStats(unsigned* table, U32 lastEltIndex, U32 shift, base_directive_e base1) { U32 s, sum=0; - DEBUGLOG(5, "ZSTD_downscaleStat (nbElts=%u)", (unsigned)lastEltIndex+1); - assert(ZSTD_FREQ_DIV+malus > 0 && ZSTD_FREQ_DIV+malus < 31); + DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)", + (unsigned)lastEltIndex+1, (unsigned)shift ); + assert(shift < 30); for (s=0; s<lastEltIndex+1; s++) { - table[s] = 1 + (table[s] >> (ZSTD_FREQ_DIV+malus)); - sum += table[s]; + unsigned const base = base1 ? 1 : (table[s]>0); + unsigned const newStat = base + (table[s] >> shift); + sum += newStat; + table[s] = newStat; } return sum; } +/* ZSTD_scaleStats() : + * reduce all elt frequencies in table if sum too large + * return the resulting sum of elements */ +static U32 ZSTD_scaleStats(unsigned* table, U32 lastEltIndex, U32 logTarget) +{ + U32 const prevsum = sum_u32(table, lastEltIndex+1); + U32 const factor = prevsum >> logTarget; + DEBUGLOG(5, "ZSTD_scaleStats (nbElts=%u, target=%u)", (unsigned)lastEltIndex+1, (unsigned)logTarget); + assert(logTarget < 30); + if (factor <= 1) return prevsum; + return ZSTD_downscaleStats(table, lastEltIndex, ZSTD_highbit32(factor), base_1guaranteed); +} + /* ZSTD_rescaleFreqs() : * if first block (detected by optPtr->litLengthSum == 0) : init statistics * take hints from dictionary if there is one - * or init from zero, using src for literals stats, or flat 1 for match symbols + * and init from zero if there is none, + * using src for literals stats, and baseline stats for sequence symbols * otherwise downscale existing stats, to be used as seed for next block. */ static void @@ -109,24 +143,28 @@ DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize); optPtr->priceType = zop_dynamic; - if (optPtr->litLengthSum == 0) { /* first block : init */ - if (srcSize <= ZSTD_PREDEF_THRESHOLD) { /* heuristic */ - DEBUGLOG(5, "(srcSize <= ZSTD_PREDEF_THRESHOLD) => zop_predef"); + if (optPtr->litLengthSum == 0) { /* no literals stats collected -> first block assumed -> init */ + + /* heuristic: use pre-defined stats for too small inputs */ + if (srcSize <= ZSTD_PREDEF_THRESHOLD) { + DEBUGLOG(5, "srcSize <= %i : use predefined stats", ZSTD_PREDEF_THRESHOLD); optPtr->priceType = zop_predef; } assert(optPtr->symbolCosts != NULL); if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) { - /* huffman table presumed generated by dictionary */ + + /* huffman stats covering the full value set : table presumed generated by dictionary */ optPtr->priceType = zop_dynamic; if (compressedLiterals) { + /* generate literals statistics from huffman table */ unsigned lit; assert(optPtr->litFreq != NULL); optPtr->litSum = 0; for (lit=0; lit<=MaxLit; lit++) { U32 const scaleLog = 11; /* scale to 2K */ - U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit); + U32 const bitCost = HUF_getNbBitsFromCTable(optPtr->symbolCosts->huf.CTable, lit); assert(bitCost <= scaleLog); optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->litSum += optPtr->litFreq[lit]; @@ -168,20 +206,26 @@ optPtr->offCodeSum += optPtr->offCodeFreq[of]; } } - } else { /* not a dictionary */ + } else { /* first block, no dictionary */ assert(optPtr->litFreq != NULL); if (compressedLiterals) { + /* base initial cost of literals on direct frequency within src */ unsigned lit = MaxLit; HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */ - optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); + optPtr->litSum = ZSTD_downscaleStats(optPtr->litFreq, MaxLit, 8, base_0possible); } - { unsigned ll; - for (ll=0; ll<=MaxLL; ll++) - optPtr->litLengthFreq[ll] = 1; + { unsigned const baseLLfreqs[MaxLL+1] = { + 4, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1 + }; + ZSTD_memcpy(optPtr->litLengthFreq, baseLLfreqs, sizeof(baseLLfreqs)); + optPtr->litLengthSum = sum_u32(baseLLfreqs, MaxLL+1); } - optPtr->litLengthSum = MaxLL+1; { unsigned ml; for (ml=0; ml<=MaxML; ml++) @@ -189,21 +233,25 @@ } optPtr->matchLengthSum = MaxML+1; - { unsigned of; - for (of=0; of<=MaxOff; of++) - optPtr->offCodeFreq[of] = 1; + { unsigned const baseOFCfreqs[MaxOff+1] = { + 6, 2, 1, 1, 2, 3, 4, 4, + 4, 3, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + ZSTD_memcpy(optPtr->offCodeFreq, baseOFCfreqs, sizeof(baseOFCfreqs)); + optPtr->offCodeSum = sum_u32(baseOFCfreqs, MaxOff+1); } - optPtr->offCodeSum = MaxOff+1; } - } else { /* new block : re-use previous statistics, scaled down */ + } else { /* new block : scale down accumulated statistics */ if (compressedLiterals) - optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1); - optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0); - optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0); - optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0); + optPtr->litSum = ZSTD_scaleStats(optPtr->litFreq, MaxLit, 12); + optPtr->litLengthSum = ZSTD_scaleStats(optPtr->litLengthFreq, MaxLL, 11); + optPtr->matchLengthSum = ZSTD_scaleStats(optPtr->matchLengthFreq, MaxML, 11); + optPtr->offCodeSum = ZSTD_scaleStats(optPtr->offCodeFreq, MaxOff, 11); } ZSTD_setBasePrices(optPtr, optLevel); @@ -225,11 +273,14 @@ return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */ /* dynamic statistics */ - { U32 price = litLength * optPtr->litSumBasePrice; + { U32 price = optPtr->litSumBasePrice * litLength; + U32 const litPriceMax = optPtr->litSumBasePrice - BITCOST_MULTIPLIER; U32 u; + assert(optPtr->litSumBasePrice >= BITCOST_MULTIPLIER); for (u=0; u < litLength; u++) { - assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */ - price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel); + U32 litPrice = WEIGHT(optPtr->litFreq[literals[u]], optLevel); + if (UNLIKELY(litPrice > litPriceMax)) litPrice = litPriceMax; + price -= litPrice; } return price; } @@ -239,7 +290,17 @@ * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr, int optLevel) { - if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel); + assert(litLength <= ZSTD_BLOCKSIZE_MAX); + if (optPtr->priceType == zop_predef) + return WEIGHT(litLength, optLevel); + + /* ZSTD_LLcode() can't compute litLength price for sizes >= ZSTD_BLOCKSIZE_MAX + * because it isn't representable in the zstd format. + * So instead just pretend it would cost 1 bit more than ZSTD_BLOCKSIZE_MAX - 1. + * In such a case, the block would be all literals. + */ + if (litLength == ZSTD_BLOCKSIZE_MAX) + return BITCOST_MULTIPLIER + ZSTD_litLengthPrice(ZSTD_BLOCKSIZE_MAX - 1, optPtr, optLevel); /* dynamic statistics */ { U32 const llCode = ZSTD_LLcode(litLength); @@ -250,22 +311,25 @@ } /* ZSTD_getMatchPrice() : - * Provides the cost of the match part (offset + matchLength) of a sequence + * Provides the cost of the match part (offset + matchLength) of a sequence. * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. - * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ + * @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq() + * @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) + */ FORCE_INLINE_TEMPLATE U32 -ZSTD_getMatchPrice(U32 const offset, +ZSTD_getMatchPrice(U32 const offBase, U32 const matchLength, const optState_t* const optPtr, int const optLevel) { U32 price; - U32 const offCode = ZSTD_highbit32(offset+1); + U32 const offCode = ZSTD_highbit32(offBase); U32 const mlBase = matchLength - MINMATCH; assert(matchLength >= MINMATCH); - if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */ - return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER); + if (optPtr->priceType == zop_predef) /* fixed scheme, does not use statistics */ + return WEIGHT(mlBase, optLevel) + + ((16 + offCode) * BITCOST_MULTIPLIER); /* emulated offset cost */ /* dynamic statistics */ price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel)); @@ -284,10 +348,10 @@ } /* ZSTD_updateStats() : - * assumption : literals + litLengtn <= iend */ + * assumption : literals + litLength <= iend */ static void ZSTD_updateStats(optState_t* const optPtr, U32 litLength, const BYTE* literals, - U32 offsetCode, U32 matchLength) + U32 offBase, U32 matchLength) { /* literals */ if (ZSTD_compressedLiterals(optPtr)) { @@ -303,8 +367,8 @@ optPtr->litLengthSum++; } - /* match offset code (0-2=>repCode; 3+=>offset+2) */ - { U32 const offCode = ZSTD_highbit32(offsetCode+1); + /* offset code : follows storeSeq() numeric representation */ + { U32 const offCode = ZSTD_highbit32(offBase); assert(offCode <= MaxOff); optPtr->offCodeFreq[offCode]++; optPtr->offCodeSum++; @@ -338,7 +402,7 @@ /* Update hashTable3 up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ -static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, +static U32 ZSTD_insertAndFindFirstIndexHash3 (const ZSTD_matchState_t* ms, U32* nextToUpdate3, const BYTE* const ip) { @@ -364,11 +428,13 @@ * Binary Tree search ***************************************/ /** ZSTD_insertBt1() : add one or multiple positions to tree. - * ip : assumed <= iend-8 . + * @param ip assumed <= iend-8 . + * @param target The target of ZSTD_updateTree_internal() - we are filling to this position * @return : nb of positions added */ static U32 ZSTD_insertBt1( - ZSTD_matchState_t* ms, + const ZSTD_matchState_t* ms, const BYTE* const ip, const BYTE* const iend, + U32 const target, U32 const mls, const int extDict) { const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -391,7 +457,10 @@ U32* smallerPtr = bt + 2*(curr&btMask); U32* largerPtr = smallerPtr + 1; U32 dummy32; /* to be nullified at the end */ - U32 const windowLow = ms->window.lowLimit; + /* windowLow is based on target because + * we only need positions that will be in the window at the end of the tree update. + */ + U32 const windowLow = ZSTD_getLowestMatchIndex(ms, target, cParams->windowLog); U32 matchEndIdx = curr+8+1; size_t bestLength = 8; U32 nbCompares = 1U << cParams->searchLog; @@ -404,11 +473,12 @@ DEBUGLOG(8, "ZSTD_insertBt1 (%u)", curr); + assert(curr <= target); assert(ip <= iend-8); /* required for h calculation */ hashTable[h] = curr; /* Update Hash Table */ assert(windowLow > 0); - while (nbCompares-- && (matchIndex >= windowLow)) { + for (; nbCompares && (matchIndex >= windowLow); --nbCompares) { U32* const nextPtr = bt + 2*(matchIndex & btMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ assert(matchIndex < curr); @@ -492,7 +562,7 @@ idx, target, dictMode); while(idx < target) { - U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict); + U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, target, mls, dictMode == ZSTD_extDict); assert(idx < (U32)(idx + forward)); idx += forward; } @@ -505,16 +575,17 @@ ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_noDict); } -FORCE_INLINE_TEMPLATE -U32 ZSTD_insertBtAndGetAllMatches ( - ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */ - ZSTD_matchState_t* ms, - U32* nextToUpdate3, - const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode, - const U32 rep[ZSTD_REP_NUM], - U32 const ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */ - const U32 lengthToBeat, - U32 const mls /* template */) +FORCE_INLINE_TEMPLATE U32 +ZSTD_insertBtAndGetAllMatches ( + ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */ + ZSTD_matchState_t* ms, + U32* nextToUpdate3, + const BYTE* const ip, const BYTE* const iLimit, + const ZSTD_dictMode_e dictMode, + const U32 rep[ZSTD_REP_NUM], + const U32 ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */ + const U32 lengthToBeat, + const U32 mls /* template */) { const ZSTD_compressionParameters* const cParams = &ms->cParams; U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); @@ -597,7 +668,7 @@ DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u", repCode, ll0, repOffset, repLen); bestLength = repLen; - matches[mnum].off = repCode - ll0; + matches[mnum].off = REPCODE_TO_OFFBASE(repCode - ll0 + 1); /* expect value between 1 and 3 */ matches[mnum].len = (U32)repLen; mnum++; if ( (repLen > sufficient_len) @@ -626,7 +697,7 @@ bestLength = mlen; assert(curr > matchIndex3); assert(mnum==0); /* no prior solution */ - matches[0].off = (curr - matchIndex3) + ZSTD_REP_MOVE; + matches[0].off = OFFSET_TO_OFFBASE(curr - matchIndex3); matches[0].len = (U32)mlen; mnum = 1; if ( (mlen > sufficient_len) | @@ -635,11 +706,11 @@ return 1; } } } /* no dictMatchState lookup: dicts don't have a populated HC3 table */ - } + } /* if (mls == 3) */ hashTable[h] = curr; /* Update Hash Table */ - while (nbCompares-- && (matchIndex >= matchLow)) { + for (; nbCompares && (matchIndex >= matchLow); --nbCompares) { U32* const nextPtr = bt + 2*(matchIndex & btMask); const BYTE* match; size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ @@ -659,21 +730,20 @@ } if (matchLength > bestLength) { - DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)", - (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE); + DEBUGLOG(8, "found match of length %u at distance %u (offBase=%u)", + (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex)); assert(matchEndIdx > matchIndex); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; - matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE; + matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex); matches[mnum].len = (U32)matchLength; mnum++; if ( (matchLength > ZSTD_OPT_NUM) | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) { if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */ break; /* drop, to preserve bt consistency (miss a little bit of compression) */ - } - } + } } if (match[matchLength] < ip[matchLength]) { /* match smaller than current */ @@ -692,12 +762,13 @@ *smallerPtr = *largerPtr = 0; + assert(nbCompares <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */ if (dictMode == ZSTD_dictMatchState && nbCompares) { size_t const dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls); U32 dictMatchIndex = dms->hashTable[dmsH]; const U32* const dmsBt = dms->chainTable; commonLengthSmaller = commonLengthLarger = 0; - while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) { + for (; nbCompares && (dictMatchIndex > dmsLowLimit); --nbCompares) { const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & dmsBtMask); size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ const BYTE* match = dmsBase + dictMatchIndex; @@ -707,19 +778,18 @@ if (matchLength > bestLength) { matchIndex = dictMatchIndex + dmsIndexDelta; - DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)", - (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE); + DEBUGLOG(8, "found dms match of length %u at distance %u (offBase=%u)", + (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex)); if (matchLength > matchEndIdx - matchIndex) matchEndIdx = matchIndex + (U32)matchLength; bestLength = matchLength; - matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE; + matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex); matches[mnum].len = (U32)matchLength; mnum++; if ( (matchLength > ZSTD_OPT_NUM) | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) { break; /* drop, to guarantee consistency (miss a little bit of compression) */ - } - } + } } if (dictMatchIndex <= dmsBtLow) { break; } /* beyond tree size, stop the search */ if (match[matchLength] < ip[matchLength]) { @@ -729,39 +799,91 @@ /* match is larger than current */ commonLengthLarger = matchLength; dictMatchIndex = nextPtr[0]; - } - } - } + } } } /* if (dictMode == ZSTD_dictMatchState) */ assert(matchEndIdx > curr+8); ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */ return mnum; } +typedef U32 (*ZSTD_getAllMatchesFn)( + ZSTD_match_t*, + ZSTD_matchState_t*, + U32*, + const BYTE*, + const BYTE*, + const U32 rep[ZSTD_REP_NUM], + U32 const ll0, + U32 const lengthToBeat); -FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches ( - ZSTD_match_t* matches, /* store result (match found, increasing size) in this table */ - ZSTD_matchState_t* ms, - U32* nextToUpdate3, - const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode, - const U32 rep[ZSTD_REP_NUM], - U32 const ll0, - U32 const lengthToBeat) +FORCE_INLINE_TEMPLATE U32 ZSTD_btGetAllMatches_internal( + ZSTD_match_t* matches, + ZSTD_matchState_t* ms, + U32* nextToUpdate3, + const BYTE* ip, + const BYTE* const iHighLimit, + const U32 rep[ZSTD_REP_NUM], + U32 const ll0, + U32 const lengthToBeat, + const ZSTD_dictMode_e dictMode, + const U32 mls) { - const ZSTD_compressionParameters* const cParams = &ms->cParams; - U32 const matchLengthSearch = cParams->minMatch; - DEBUGLOG(8, "ZSTD_BtGetAllMatches"); - if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */ - ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode); - switch(matchLengthSearch) - { - case 3 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 3); - default : - case 4 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 4); - case 5 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 5); - case 7 : - case 6 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 6); + assert(BOUNDED(3, ms->cParams.minMatch, 6) == mls); + DEBUGLOG(8, "ZSTD_BtGetAllMatches(dictMode=%d, mls=%u)", (int)dictMode, mls); + if (ip < ms->window.base + ms->nextToUpdate) + return 0; /* skipped area */ + ZSTD_updateTree_internal(ms, ip, iHighLimit, mls, dictMode); + return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, mls); +} + +#define ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, mls) ZSTD_btGetAllMatches_##dictMode##_##mls + +#define GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, mls) \ + static U32 ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, mls)( \ + ZSTD_match_t* matches, \ + ZSTD_matchState_t* ms, \ + U32* nextToUpdate3, \ + const BYTE* ip, \ + const BYTE* const iHighLimit, \ + const U32 rep[ZSTD_REP_NUM], \ + U32 const ll0, \ + U32 const lengthToBeat) \ + { \ + return ZSTD_btGetAllMatches_internal( \ + matches, ms, nextToUpdate3, ip, iHighLimit, \ + rep, ll0, lengthToBeat, ZSTD_##dictMode, mls); \ } + +#define GEN_ZSTD_BT_GET_ALL_MATCHES(dictMode) \ + GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 3) \ + GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 4) \ + GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 5) \ + GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 6) + +GEN_ZSTD_BT_GET_ALL_MATCHES(noDict) +GEN_ZSTD_BT_GET_ALL_MATCHES(extDict) +GEN_ZSTD_BT_GET_ALL_MATCHES(dictMatchState) + +#define ZSTD_BT_GET_ALL_MATCHES_ARRAY(dictMode) \ + { \ + ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 3), \ + ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 4), \ + ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 5), \ + ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 6) \ + } + +static ZSTD_getAllMatchesFn +ZSTD_selectBtGetAllMatches(ZSTD_matchState_t const* ms, ZSTD_dictMode_e const dictMode) +{ + ZSTD_getAllMatchesFn const getAllMatchesFns[3][4] = { + ZSTD_BT_GET_ALL_MATCHES_ARRAY(noDict), + ZSTD_BT_GET_ALL_MATCHES_ARRAY(extDict), + ZSTD_BT_GET_ALL_MATCHES_ARRAY(dictMatchState) + }; + U32 const mls = BOUNDED(3, ms->cParams.minMatch, 6); + assert((U32)dictMode < 3); + assert(mls - 3 < 4); + return getAllMatchesFns[(int)dictMode][mls - 3]; } /************************* @@ -770,16 +892,18 @@ /* Struct containing info needed to make decision about ldm inclusion */ typedef struct { - rawSeqStore_t seqStore; /* External match candidates store for this block */ - U32 startPosInBlock; /* Start position of the current match candidate */ - U32 endPosInBlock; /* End position of the current match candidate */ - U32 offset; /* Offset of the match candidate */ + rawSeqStore_t seqStore; /* External match candidates store for this block */ + U32 startPosInBlock; /* Start position of the current match candidate */ + U32 endPosInBlock; /* End position of the current match candidate */ + U32 offset; /* Offset of the match candidate */ } ZSTD_optLdm_t; /* ZSTD_optLdm_skipRawSeqStoreBytes(): - * Moves forward in rawSeqStore by nbBytes, which will update the fields 'pos' and 'posInSequence'. + * Moves forward in @rawSeqStore by @nbBytes, + * which will update the fields 'pos' and 'posInSequence'. */ -static void ZSTD_optLdm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) { +static void ZSTD_optLdm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) +{ U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes); while (currPos && rawSeqStore->pos < rawSeqStore->size) { rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos]; @@ -800,8 +924,10 @@ * Calculates the beginning and end of the next match in the current block. * Updates 'pos' and 'posInSequence' of the ldmSeqStore. */ -static void ZSTD_opt_getNextMatchAndUpdateSeqStore(ZSTD_optLdm_t* optLdm, U32 currPosInBlock, - U32 blockBytesRemaining) { +static void +ZSTD_opt_getNextMatchAndUpdateSeqStore(ZSTD_optLdm_t* optLdm, U32 currPosInBlock, + U32 blockBytesRemaining) +{ rawSeq currSeq; U32 currBlockEndPos; U32 literalsBytesRemaining; @@ -813,8 +939,8 @@ optLdm->endPosInBlock = UINT_MAX; return; } - /* Calculate appropriate bytes left in matchLength and litLength after adjusting - based on ldmSeqStore->posInSequence */ + /* Calculate appropriate bytes left in matchLength and litLength + * after adjusting based on ldmSeqStore->posInSequence */ currSeq = optLdm->seqStore.seq[optLdm->seqStore.pos]; assert(optLdm->seqStore.posInSequence <= currSeq.litLength + currSeq.matchLength); currBlockEndPos = currPosInBlock + blockBytesRemaining; @@ -850,15 +976,16 @@ } /* ZSTD_optLdm_maybeAddMatch(): - * Adds a match if it's long enough, based on it's 'matchStartPosInBlock' - * and 'matchEndPosInBlock', into 'matches'. Maintains the correct ordering of 'matches' + * Adds a match if it's long enough, + * based on it's 'matchStartPosInBlock' and 'matchEndPosInBlock', + * into 'matches'. Maintains the correct ordering of 'matches'. */ static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches, - ZSTD_optLdm_t* optLdm, U32 currPosInBlock) { - U32 posDiff = currPosInBlock - optLdm->startPosInBlock; - /* Note: ZSTD_match_t actually contains offCode and matchLength (before subtracting MINMATCH) */ - U32 candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff; - U32 candidateOffCode = optLdm->offset + ZSTD_REP_MOVE; + const ZSTD_optLdm_t* optLdm, U32 currPosInBlock) +{ + U32 const posDiff = currPosInBlock - optLdm->startPosInBlock; + /* Note: ZSTD_match_t actually contains offBase and matchLength (before subtracting MINMATCH) */ + U32 const candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff; /* Ensure that current block position is not outside of the match */ if (currPosInBlock < optLdm->startPosInBlock @@ -868,10 +995,11 @@ } if (*nbMatches == 0 || ((candidateMatchLength > matches[*nbMatches-1].len) && *nbMatches < ZSTD_OPT_NUM)) { - DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offCode: %u matchLength %u) at block position=%u", - candidateOffCode, candidateMatchLength, currPosInBlock); + U32 const candidateOffBase = OFFSET_TO_OFFBASE(optLdm->offset); + DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offBase: %u matchLength %u) at block position=%u", + candidateOffBase, candidateMatchLength, currPosInBlock); matches[*nbMatches].len = candidateMatchLength; - matches[*nbMatches].off = candidateOffCode; + matches[*nbMatches].off = candidateOffBase; (*nbMatches)++; } } @@ -879,8 +1007,11 @@ /* ZSTD_optLdm_processMatchCandidate(): * Wrapper function to update ldm seq store and call ldm functions as necessary. */ -static void ZSTD_optLdm_processMatchCandidate(ZSTD_optLdm_t* optLdm, ZSTD_match_t* matches, U32* nbMatches, - U32 currPosInBlock, U32 remainingBytes) { +static void +ZSTD_optLdm_processMatchCandidate(ZSTD_optLdm_t* optLdm, + ZSTD_match_t* matches, U32* nbMatches, + U32 currPosInBlock, U32 remainingBytes) +{ if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) { return; } @@ -891,19 +1022,19 @@ * at the end of a match from the ldm seq store, and will often be some bytes * over beyond matchEndPosInBlock. As such, we need to correct for these "overshoots" */ - U32 posOvershoot = currPosInBlock - optLdm->endPosInBlock; + U32 const posOvershoot = currPosInBlock - optLdm->endPosInBlock; ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, posOvershoot); - } + } ZSTD_opt_getNextMatchAndUpdateSeqStore(optLdm, currPosInBlock, remainingBytes); } ZSTD_optLdm_maybeAddMatch(matches, nbMatches, optLdm, currPosInBlock); } + /*-******************************* * Optimal parser *********************************/ - static U32 ZSTD_totalLen(ZSTD_optimal_t sol) { return sol.litlen + sol.mlen; @@ -944,6 +1075,8 @@ const BYTE* const prefixStart = base + ms->window.dictLimit; const ZSTD_compressionParameters* const cParams = &ms->cParams; + ZSTD_getAllMatchesFn getAllMatches = ZSTD_selectBtGetAllMatches(ms, dictMode); + U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1); U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4; U32 nextToUpdate3 = ms->nextToUpdate; @@ -953,6 +1086,8 @@ ZSTD_optimal_t lastSequence; ZSTD_optLdm_t optLdm; + ZSTD_memset(&lastSequence, 0, sizeof(ZSTD_optimal_t)); + optLdm.seqStore = ms->ldmSeqStore ? *ms->ldmSeqStore : kNullRawSeqStore; optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0; ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (U32)(ip-istart), (U32)(iend-ip)); @@ -971,7 +1106,7 @@ /* find first match */ { U32 const litlen = (U32)(ip - anchor); U32 const ll0 = !litlen; - U32 nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch); + U32 nbMatches = getAllMatches(matches, ms, &nextToUpdate3, ip, iend, rep, ll0, minMatch); ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches, (U32)(ip-istart), (U32)(iend - ip)); if (!nbMatches) { ip++; continue; } @@ -985,18 +1120,18 @@ * in every price. We include the literal length to avoid negative * prices when we subtract the previous literal length. */ - opt[0].price = ZSTD_litLengthPrice(litlen, optStatePtr, optLevel); + opt[0].price = (int)ZSTD_litLengthPrice(litlen, optStatePtr, optLevel); /* large match -> immediate encoding */ { U32 const maxML = matches[nbMatches-1].len; - U32 const maxOffset = matches[nbMatches-1].off; - DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series", - nbMatches, maxML, maxOffset, (U32)(ip-prefixStart)); + U32 const maxOffBase = matches[nbMatches-1].off; + DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffBase=%u at cPos=%u => start new series", + nbMatches, maxML, maxOffBase, (U32)(ip-prefixStart)); if (maxML > sufficient_len) { lastSequence.litlen = litlen; lastSequence.mlen = maxML; - lastSequence.off = maxOffset; + lastSequence.off = maxOffBase; DEBUGLOG(6, "large match (%u>%u), immediate encoding", maxML, sufficient_len); cur = 0; @@ -1005,24 +1140,25 @@ } } /* set prices for first matches starting position == 0 */ - { U32 const literalsPrice = opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel); + assert(opt[0].price >= 0); + { U32 const literalsPrice = (U32)opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel); U32 pos; U32 matchNb; for (pos = 1; pos < minMatch; pos++) { opt[pos].price = ZSTD_MAX_PRICE; /* mlen, litlen and price will be fixed during forward scanning */ } for (matchNb = 0; matchNb < nbMatches; matchNb++) { - U32 const offset = matches[matchNb].off; + U32 const offBase = matches[matchNb].off; U32 const end = matches[matchNb].len; for ( ; pos <= end ; pos++ ) { - U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); + U32 const matchPrice = ZSTD_getMatchPrice(offBase, pos, optStatePtr, optLevel); U32 const sequencePrice = literalsPrice + matchPrice; DEBUGLOG(7, "rPos:%u => set initial price : %.2f", - pos, ZSTD_fCost(sequencePrice)); + pos, ZSTD_fCost((int)sequencePrice)); opt[pos].mlen = pos; - opt[pos].off = offset; + opt[pos].off = offBase; opt[pos].litlen = litlen; - opt[pos].price = sequencePrice; + opt[pos].price = (int)sequencePrice; } } last_pos = pos-1; } @@ -1037,9 +1173,9 @@ /* Fix current position with one literal if cheaper */ { U32 const litlen = (opt[cur-1].mlen == 0) ? opt[cur-1].litlen + 1 : 1; int const price = opt[cur-1].price - + ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel) - + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel) - - ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel); + + (int)ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel) + + (int)ZSTD_litLengthPrice(litlen, optStatePtr, optLevel) + - (int)ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel); assert(price < 1000000000); /* overflow check */ if (price <= opt[cur].price) { DEBUGLOG(7, "cPos:%zi==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)", @@ -1065,7 +1201,7 @@ assert(cur >= opt[cur].mlen); if (opt[cur].mlen != 0) { U32 const prev = cur - opt[cur].mlen; - repcodes_t newReps = ZSTD_updateRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0); + repcodes_t const newReps = ZSTD_newRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0); ZSTD_memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t)); } else { ZSTD_memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t)); @@ -1082,11 +1218,12 @@ continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */ } + assert(opt[cur].price >= 0); { U32 const ll0 = (opt[cur].mlen != 0); U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0; - U32 const previousPrice = opt[cur].price; + U32 const previousPrice = (U32)opt[cur].price; U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel); - U32 nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch); + U32 nbMatches = getAllMatches(matches, ms, &nextToUpdate3, inr, iend, opt[cur].rep, ll0, minMatch); U32 matchNb; ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches, @@ -1119,12 +1256,12 @@ U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch; U32 mlen; - DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u", + DEBUGLOG(7, "testing match %u => offBase=%4u, mlen=%2u, llen=%2u", matchNb, matches[matchNb].off, lastML, litlen); for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */ U32 const pos = cur + mlen; - int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); + int const price = (int)basePrice + (int)ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)", @@ -1154,7 +1291,7 @@ * update them while traversing the sequences. */ if (lastSequence.mlen != 0) { - repcodes_t reps = ZSTD_updateRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0); + repcodes_t const reps = ZSTD_newRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0); ZSTD_memcpy(rep, &reps, sizeof(reps)); } else { ZSTD_memcpy(rep, opt[cur].rep, sizeof(repcodes_t)); @@ -1185,7 +1322,7 @@ for (storePos=storeStart; storePos <= storeEnd; storePos++) { U32 const llen = opt[storePos].litlen; U32 const mlen = opt[storePos].mlen; - U32 const offCode = opt[storePos].off; + U32 const offBase = opt[storePos].off; U32 const advance = llen + mlen; DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u", anchor - istart, (unsigned)llen, (unsigned)mlen); @@ -1197,8 +1334,8 @@ } assert(anchor + llen <= iend); - ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen); - ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen-MINMATCH); + ZSTD_updateStats(optStatePtr, llen, anchor, offBase, mlen); + ZSTD_storeSeq(seqStore, llen, anchor, iend, offBase, mlen); anchor += advance; ip = anchor; } } @@ -1210,43 +1347,35 @@ return (size_t)(iend - anchor); } +static size_t ZSTD_compressBlock_opt0( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + const void* src, size_t srcSize, const ZSTD_dictMode_e dictMode) +{ + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /* optLevel */, dictMode); +} + +static size_t ZSTD_compressBlock_opt2( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + const void* src, size_t srcSize, const ZSTD_dictMode_e dictMode) +{ + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /* optLevel */, dictMode); +} size_t ZSTD_compressBlock_btopt( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btopt"); - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_noDict); + return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_noDict); } -/* used in 2-pass strategy */ -static U32 ZSTD_upscaleStat(unsigned* table, U32 lastEltIndex, int bonus) -{ - U32 s, sum=0; - assert(ZSTD_FREQ_DIV+bonus >= 0); - for (s=0; s<lastEltIndex+1; s++) { - table[s] <<= ZSTD_FREQ_DIV+bonus; - table[s]--; - sum += table[s]; - } - return sum; -} -/* used in 2-pass strategy */ -MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr) -{ - if (ZSTD_compressedLiterals(optPtr)) - optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0); - optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 0); - optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 0); - optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 0); -} /* ZSTD_initStats_ultra(): * make a first compression pass, just to seed stats with more accurate starting values. * only works on first block, with no dictionary and no ldm. - * this function cannot error, hence its contract must be respected. + * this function cannot error out, its narrow contract must be respected. */ static void ZSTD_initStats_ultra(ZSTD_matchState_t* ms, @@ -1263,17 +1392,15 @@ assert(ms->window.dictLimit == ms->window.lowLimit); /* no dictionary */ assert(ms->window.dictLimit - ms->nextToUpdate <= 1); /* no prefix (note: intentional overflow, defined as 2-complement) */ - ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); /* generate stats into ms->opt*/ + ZSTD_compressBlock_opt2(ms, seqStore, tmpRep, src, srcSize, ZSTD_noDict); /* generate stats into ms->opt*/ - /* invalidate first scan from history */ + /* invalidate first scan from history, only keep entropy stats */ ZSTD_resetSeqStore(seqStore); ms->window.base -= srcSize; ms->window.dictLimit += (U32)srcSize; ms->window.lowLimit = ms->window.dictLimit; ms->nextToUpdate = ms->window.dictLimit; - /* re-inforce weight of collected statistics */ - ZSTD_upscaleStats(&ms->opt); } size_t ZSTD_compressBlock_btultra( @@ -1281,7 +1408,7 @@ const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize); - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); + return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_noDict); } size_t ZSTD_compressBlock_btultra2( @@ -1291,53 +1418,53 @@ U32 const curr = (U32)((const BYTE*)src - ms->window.base); DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize); - /* 2-pass strategy: + /* 2-passes strategy: * this strategy makes a first pass over first block to collect statistics - * and seed next round's statistics with it. - * After 1st pass, function forgets everything, and starts a new block. + * in order to seed next round's statistics with it. + * After 1st pass, function forgets history, and starts a new block. * Consequently, this can only work if no data has been previously loaded in tables, * aka, no dictionary, no prefix, no ldm preprocessing. * The compression ratio gain is generally small (~0.5% on first block), - * the cost is 2x cpu time on first block. */ + ** the cost is 2x cpu time on first block. */ assert(srcSize <= ZSTD_BLOCKSIZE_MAX); if ( (ms->opt.litLengthSum==0) /* first block */ && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */ && (ms->window.dictLimit == ms->window.lowLimit) /* no dictionary */ - && (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ - && (srcSize > ZSTD_PREDEF_THRESHOLD) + && (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */ + && (srcSize > ZSTD_PREDEF_THRESHOLD) /* input large enough to not employ default stats */ ) { ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize); } - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); + return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_noDict); } size_t ZSTD_compressBlock_btopt_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_dictMatchState); + return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_btultra_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_dictMatchState); + return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_dictMatchState); } size_t ZSTD_compressBlock_btopt_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_extDict); + return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_extDict); } size_t ZSTD_compressBlock_btultra_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize) { - return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_extDict); + return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_extDict); } /* note : no btultra2 variant for extDict nor dictMatchState,
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.h b/Utilities/cmzstd/lib/compress/zstd_opt.h index 627255f..342e5a3 100644 --- a/Utilities/cmzstd/lib/compress/zstd_opt.h +++ b/Utilities/cmzstd/lib/compress/zstd_opt.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.c b/Utilities/cmzstd/lib/compress/zstdmt_compress.c index 22aa3e1..6786075 100644 --- a/Utilities/cmzstd/lib/compress/zstdmt_compress.c +++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,6 +20,7 @@ /* ====== Dependencies ====== */ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memset, INT_MAX, UINT_MAX */ #include "../common/mem.h" /* MEM_STATIC */ #include "../common/pool.h" /* threadpool */ @@ -102,9 +103,8 @@ buffer_t bTable[1]; /* variable size */ } ZSTDMT_bufferPool; -static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbWorkers, ZSTD_customMem cMem) +static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned maxNbBuffers, ZSTD_customMem cMem) { - unsigned const maxNbBuffers = 2*nbWorkers + 3; ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_customCalloc( sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem); if (bufPool==NULL) return NULL; @@ -160,9 +160,8 @@ } -static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, U32 nbWorkers) +static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, unsigned maxNbBuffers) { - unsigned const maxNbBuffers = 2*nbWorkers + 3; if (srcBufPool==NULL) return NULL; if (srcBufPool->totalBuffers >= maxNbBuffers) /* good enough */ return srcBufPool; @@ -171,7 +170,7 @@ size_t const bSize = srcBufPool->bufferSize; /* forward parameters */ ZSTDMT_bufferPool* newBufPool; ZSTDMT_freeBufferPool(srcBufPool); - newBufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + newBufPool = ZSTDMT_createBufferPool(maxNbBuffers, cMem); if (newBufPool==NULL) return newBufPool; ZSTDMT_setBufferSize(newBufPool, bSize); return newBufPool; @@ -263,6 +262,16 @@ ZSTD_customFree(buf.start, bufPool->cMem); } +/* We need 2 output buffers per worker since each dstBuff must be flushed after it is released. + * The 3 additional buffers are as follows: + * 1 buffer for input loading + * 1 buffer for "next input" when submitting current one + * 1 buffer stuck in queue */ +#define BUF_POOL_MAX_NB_BUFFERS(nbWorkers) (2*(nbWorkers) + 3) + +/* After a worker releases its rawSeqStore, it is immediately ready for reuse. + * So we only need one seq buffer per worker. */ +#define SEQ_POOL_MAX_NB_BUFFERS(nbWorkers) (nbWorkers) /* ===== Seq Pool Wrapper ====== */ @@ -316,7 +325,7 @@ static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem) { - ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(SEQ_POOL_MAX_NB_BUFFERS(nbWorkers), cMem); if (seqPool == NULL) return NULL; ZSTDMT_setNbSeq(seqPool, 0); return seqPool; @@ -329,7 +338,7 @@ static ZSTDMT_seqPool* ZSTDMT_expandSeqPool(ZSTDMT_seqPool* pool, U32 nbWorkers) { - return ZSTDMT_expandBufferPool(pool, nbWorkers); + return ZSTDMT_expandBufferPool(pool, SEQ_POOL_MAX_NB_BUFFERS(nbWorkers)); } @@ -467,7 +476,7 @@ ZSTD_dictContentType_e dictContentType) { /* Adjust parameters */ - if (params.ldmParams.enableLdm) { + if (params.ldmParams.enableLdm == ZSTD_ps_enable) { DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10); ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); @@ -478,7 +487,7 @@ serialState->nextJobID = 0; if (params.fParams.checksumFlag) XXH64_reset(&serialState->xxhState, 0); - if (params.ldmParams.enableLdm) { + if (params.ldmParams.enableLdm == ZSTD_ps_enable) { ZSTD_customMem cMem = params.customMem; unsigned const hashLog = params.ldmParams.hashLog; size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t); @@ -564,7 +573,7 @@ /* A future job may error and skip our job */ if (serialState->nextJobID == jobID) { /* It is now our turn, do any processing necessary */ - if (serialState->params.ldmParams.enableLdm) { + if (serialState->params.ldmParams.enableLdm == ZSTD_ps_enable) { size_t error; assert(seqStore.seq != NULL && seqStore.pos == 0 && seqStore.size == 0 && seqStore.capacity > 0); @@ -594,7 +603,7 @@ if (seqStore.size > 0) { size_t const err = ZSTD_referenceExternalSequences( jobCCtx, seqStore.seq, seqStore.size); - assert(serialState->params.ldmParams.enableLdm); + assert(serialState->params.ldmParams.enableLdm == ZSTD_ps_enable); assert(!ZSTD_isError(err)); (void)err; } @@ -672,7 +681,7 @@ if (dstBuff.start==NULL) JOB_ERROR(ERROR(memory_allocation)); job->dstBuff = dstBuff; /* this value can be read in ZSTDMT_flush, when it copies the whole job */ } - if (jobParams.ldmParams.enableLdm && rawSeqStore.seq == NULL) + if (jobParams.ldmParams.enableLdm == ZSTD_ps_enable && rawSeqStore.seq == NULL) JOB_ERROR(ERROR(memory_allocation)); /* Don't compute the checksum for chunks, since we compute it externally, @@ -680,7 +689,7 @@ */ if (job->jobID != 0) jobParams.fParams.checksumFlag = 0; /* Don't run LDM for the chunks, since we handle it externally */ - jobParams.ldmParams.enableLdm = 0; + jobParams.ldmParams.enableLdm = ZSTD_ps_disable; /* Correct nbWorkers to 0. */ jobParams.nbWorkers = 0; @@ -711,7 +720,7 @@ ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID); if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */ - size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); + size_t const hSize = ZSTD_compressContinue_public(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); if (ZSTD_isError(hSize)) JOB_ERROR(hSize); DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize); ZSTD_invalidateRepCodes(cctx); @@ -729,7 +738,7 @@ DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks); assert(job->cSize == 0); for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) { - size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize); + size_t const cSize = ZSTD_compressContinue_public(cctx, op, oend-op, ip, chunkSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); ip += chunkSize; op += cSize; assert(op < oend); @@ -749,8 +758,8 @@ size_t const lastBlockSize1 = job->src.size & (chunkSize-1); size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1; size_t const cSize = (job->lastJob) ? - ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) : - ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); + ZSTD_compressEnd_public(cctx, op, oend-op, ip, lastBlockSize) : + ZSTD_compressContinue_public(cctx, op, oend-op, ip, lastBlockSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); lastCBlockSize = cSize; } } @@ -807,6 +816,15 @@ static const roundBuff_t kNullRoundBuff = {NULL, 0, 0}; #define RSYNC_LENGTH 32 +/* Don't create chunks smaller than the zstd block size. + * This stops us from regressing compression ratio too much, + * and ensures our output fits in ZSTD_compressBound(). + * + * If this is shrunk < ZSTD_BLOCKSIZELOG_MIN then + * ZSTD_COMPRESSBOUND() will need to be updated. + */ +#define RSYNC_MIN_BLOCK_LOG ZSTD_BLOCKSIZELOG_MAX +#define RSYNC_MIN_BLOCK_SIZE (1<<RSYNC_MIN_BLOCK_LOG) typedef struct { U64 hash; @@ -927,7 +945,7 @@ mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem); assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0); /* ensure nbJobs is a power of 2 */ mtctx->jobIDMask = nbJobs - 1; - mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + mtctx->bufPool = ZSTDMT_createBufferPool(BUF_POOL_MAX_NB_BUFFERS(nbWorkers), cMem); mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem); mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem); initError = ZSTDMT_serialState_init(&mtctx->serial); @@ -1030,7 +1048,7 @@ { if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation); FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbWorkers) , ""); - mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers); + mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, BUF_POOL_MAX_NB_BUFFERS(nbWorkers)); if (mtctx->bufPool == NULL) return ERROR(memory_allocation); mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers); if (mtctx->cctxPool == NULL) return ERROR(memory_allocation); @@ -1135,7 +1153,7 @@ static unsigned ZSTDMT_computeTargetJobLog(const ZSTD_CCtx_params* params) { unsigned jobLog; - if (params->ldmParams.enableLdm) { + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { /* In Long Range Mode, the windowLog is typically oversized. * In which case, it's preferable to determine the jobSize * based on cycleLog instead. */ @@ -1179,7 +1197,7 @@ int const overlapRLog = 9 - ZSTDMT_overlapLog(params->overlapLog, params->cParams.strategy); int ovLog = (overlapRLog >= 8) ? 0 : (params->cParams.windowLog - overlapRLog); assert(0 <= overlapRLog && overlapRLog <= 8); - if (params->ldmParams.enableLdm) { + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { /* In Long Range Mode, the windowLog is typically oversized. * In which case, it's preferable to determine the jobSize * based on chainLog instead. @@ -1252,6 +1270,9 @@ /* Aim for the targetsectionSize as the average job size. */ U32 const jobSizeKB = (U32)(mtctx->targetSectionSize >> 10); U32 const rsyncBits = (assert(jobSizeKB >= 1), ZSTD_highbit32(jobSizeKB) + 10); + /* We refuse to create jobs < RSYNC_MIN_BLOCK_SIZE bytes, so make sure our + * expected job size is at least 4x larger. */ + assert(rsyncBits >= RSYNC_MIN_BLOCK_LOG + 2); DEBUGLOG(4, "rsyncLog = %u", rsyncBits); mtctx->rsync.hash = 0; mtctx->rsync.hitMask = (1ULL << rsyncBits) - 1; @@ -1263,7 +1284,7 @@ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize)); { /* If ldm is enabled we need windowSize space. */ - size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0; + size_t const windowSize = mtctx->params.ldmParams.enableLdm == ZSTD_ps_enable ? (1U << mtctx->params.cParams.windowLog) : 0; /* Two buffers of slack, plus extra space for the overlap * This is the minimum slack that LDM works with. One extra because * flush might waste up to targetSectionSize-1 bytes. Another extra @@ -1538,17 +1559,21 @@ static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range) { BYTE const* const bufferStart = (BYTE const*)buffer.start; - BYTE const* const bufferEnd = bufferStart + buffer.capacity; BYTE const* const rangeStart = (BYTE const*)range.start; - BYTE const* const rangeEnd = range.size != 0 ? rangeStart + range.size : rangeStart; if (rangeStart == NULL || bufferStart == NULL) return 0; - /* Empty ranges cannot overlap */ - if (bufferStart == bufferEnd || rangeStart == rangeEnd) - return 0; - return bufferStart < rangeEnd && rangeStart < bufferEnd; + { + BYTE const* const bufferEnd = bufferStart + buffer.capacity; + BYTE const* const rangeEnd = rangeStart + range.size; + + /* Empty ranges cannot overlap */ + if (bufferStart == bufferEnd || rangeStart == rangeEnd) + return 0; + + return bufferStart < rangeEnd && rangeStart < bufferEnd; + } } static int ZSTDMT_doesOverlapWindow(buffer_t buffer, ZSTD_window_t window) @@ -1575,7 +1600,7 @@ static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx* mtctx, buffer_t buffer) { - if (mtctx->params.ldmParams.enableLdm) { + if (mtctx->params.ldmParams.enableLdm == ZSTD_ps_enable) { ZSTD_pthread_mutex_t* mutex = &mtctx->serial.ldmWindowMutex; DEBUGLOG(5, "ZSTDMT_waitForLdmComplete"); DEBUGLOG(5, "source [0x%zx, 0x%zx)", @@ -1678,6 +1703,11 @@ if (!mtctx->params.rsyncable) /* Rsync is disabled. */ return syncPoint; + if (mtctx->inBuff.filled + input.size - input.pos < RSYNC_MIN_BLOCK_SIZE) + /* We don't emit synchronization points if it would produce too small blocks. + * We don't have enough input to find a synchronization point, so don't look. + */ + return syncPoint; if (mtctx->inBuff.filled + syncPoint.toLoad < RSYNC_LENGTH) /* Not enough to compute the hash. * We will miss any synchronization points in this RSYNC_LENGTH byte @@ -1688,10 +1718,28 @@ */ return syncPoint; /* Initialize the loop variables. */ - if (mtctx->inBuff.filled >= RSYNC_LENGTH) { - /* We have enough bytes buffered to initialize the hash. + if (mtctx->inBuff.filled < RSYNC_MIN_BLOCK_SIZE) { + /* We don't need to scan the first RSYNC_MIN_BLOCK_SIZE positions + * because they can't possibly be a sync point. So we can start + * part way through the input buffer. + */ + pos = RSYNC_MIN_BLOCK_SIZE - mtctx->inBuff.filled; + if (pos >= RSYNC_LENGTH) { + prev = istart + pos - RSYNC_LENGTH; + hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH); + } else { + assert(mtctx->inBuff.filled >= RSYNC_LENGTH); + prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH; + hash = ZSTD_rollingHash_compute(prev + pos, (RSYNC_LENGTH - pos)); + hash = ZSTD_rollingHash_append(hash, istart, pos); + } + } else { + /* We have enough bytes buffered to initialize the hash, + * and have processed enough bytes to find a sync point. * Start scanning at the beginning of the input. */ + assert(mtctx->inBuff.filled >= RSYNC_MIN_BLOCK_SIZE); + assert(RSYNC_MIN_BLOCK_SIZE >= RSYNC_LENGTH); pos = 0; prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH; hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH); @@ -1705,16 +1753,6 @@ syncPoint.flush = 1; return syncPoint; } - } else { - /* We don't have enough bytes buffered to initialize the hash, but - * we know we have at least RSYNC_LENGTH bytes total. - * Start scanning after the first RSYNC_LENGTH bytes less the bytes - * already buffered. - */ - pos = RSYNC_LENGTH - mtctx->inBuff.filled; - prev = (BYTE const*)mtctx->inBuff.buffer.start - pos; - hash = ZSTD_rollingHash_compute(mtctx->inBuff.buffer.start, mtctx->inBuff.filled); - hash = ZSTD_rollingHash_append(hash, istart, pos); } /* Starting with the hash of the previous RSYNC_LENGTH bytes, roll * through the input. If we hit a synchronization point, then cut the @@ -1724,16 +1762,24 @@ * then a block will be emitted anyways, but this is okay, since if we * are already synchronized we will remain synchronized. */ + assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); for (; pos < syncPoint.toLoad; ++pos) { BYTE const toRemove = pos < RSYNC_LENGTH ? prev[pos] : istart[pos - RSYNC_LENGTH]; - /* if (pos >= RSYNC_LENGTH) assert(ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); */ + /* This assert is very expensive, and Debian compiles with asserts enabled. + * So disable it for now. We can get similar coverage by checking it at the + * beginning & end of the loop. + * assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); + */ hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower); + assert(mtctx->inBuff.filled + pos >= RSYNC_MIN_BLOCK_SIZE); if ((hash & hitMask) == hitMask) { syncPoint.toLoad = pos + 1; syncPoint.flush = 1; + ++pos; /* for assert */ break; } } + assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); return syncPoint; }
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.h b/Utilities/cmzstd/lib/compress/zstdmt_compress.h index 2fee2ec..ed4dc0e 100644 --- a/Utilities/cmzstd/lib/compress/zstdmt_compress.h +++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -65,8 +65,11 @@ * Private use only. Init streaming operation. * expects params to be valid. * must receive dict, or cdict, or none, but not both. + * mtctx can be freshly constructed or reused from a prior compression. + * If mtctx is reused, memory allocations from the prior compression may not be freed, + * even if they are not needed for the current compression. * @return : 0, or an error code */ -size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, +size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType, const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
diff --git a/Utilities/cmzstd/lib/decompress/huf_decompress.c b/Utilities/cmzstd/lib/decompress/huf_decompress.c index b93c9a0..5b217ac 100644 --- a/Utilities/cmzstd/lib/decompress/huf_decompress.c +++ b/Utilities/cmzstd/lib/decompress/huf_decompress.c
@@ -1,7 +1,7 @@ /* ****************************************************************** * huff0 huffman decoder, * part of Finite State Entropy library - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * You can contact the author at : * - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy @@ -19,9 +19,16 @@ #include "../common/compiler.h" #include "../common/bitstream.h" /* BIT_* */ #include "../common/fse.h" /* to compress headers */ -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/error_private.h" +#include "../common/zstd_internal.h" +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_countTrailingZeros64 */ + +/* ************************************************************** +* Constants +****************************************************************/ + +#define HUF_DECODER_FAST_TABLELOG 11 /* ************************************************************** * Macros @@ -36,6 +43,28 @@ #error "Cannot force the use of the X1 and X2 decoders at the same time!" #endif +/* When DYNAMIC_BMI2 is enabled, fast decoders are only called when bmi2 is + * supported at runtime, so we can add the BMI2 target attribute. + * When it is disabled, we will still get BMI2 if it is enabled statically. + */ +#if DYNAMIC_BMI2 +# define HUF_FAST_BMI2_ATTRS BMI2_TARGET_ATTRIBUTE +#else +# define HUF_FAST_BMI2_ATTRS +#endif + +#ifdef __cplusplus +# define HUF_EXTERN_C extern "C" +#else +# define HUF_EXTERN_C +#endif +#define HUF_ASM_DECL HUF_EXTERN_C + +#if DYNAMIC_BMI2 +# define HUF_NEED_BMI2_FUNCTION 1 +#else +# define HUF_NEED_BMI2_FUNCTION 0 +#endif /* ************************************************************** * Error Management @@ -53,6 +82,11 @@ /* ************************************************************** * BMI2 Variant Wrappers ****************************************************************/ +typedef size_t (*HUF_DecompressUsingDTableFn)(void *dst, size_t dstSize, + const void *cSrc, + size_t cSrcSize, + const HUF_DTable *DTable); + #if DYNAMIC_BMI2 #define HUF_DGEN(fn) \ @@ -65,7 +99,7 @@ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } \ \ - static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \ + static BMI2_TARGET_ATTRIBUTE size_t fn##_bmi2( \ void* dst, size_t dstSize, \ const void* cSrc, size_t cSrcSize, \ const HUF_DTable* DTable) \ @@ -74,9 +108,9 @@ } \ \ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + size_t cSrcSize, HUF_DTable const* DTable, int flags) \ { \ - if (bmi2) { \ + if (flags & HUF_flags_bmi2) { \ return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \ } \ return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \ @@ -86,9 +120,9 @@ #define HUF_DGEN(fn) \ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \ - size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \ + size_t cSrcSize, HUF_DTable const* DTable, int flags) \ { \ - (void)bmi2; \ + (void)flags; \ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \ } @@ -107,13 +141,164 @@ return dtd; } +static size_t HUF_initFastDStream(BYTE const* ip) { + BYTE const lastByte = ip[7]; + size_t const bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; + size_t const value = MEM_readLEST(ip) | 1; + assert(bitsConsumed <= 8); + assert(sizeof(size_t) == 8); + return value << bitsConsumed; +} + + +/** + * The input/output arguments to the Huffman fast decoding loop: + * + * ip [in/out] - The input pointers, must be updated to reflect what is consumed. + * op [in/out] - The output pointers, must be updated to reflect what is written. + * bits [in/out] - The bitstream containers, must be updated to reflect the current state. + * dt [in] - The decoding table. + * ilimit [in] - The input limit, stop when any input pointer is below ilimit. + * oend [in] - The end of the output stream. op[3] must not cross oend. + * iend [in] - The end of each input stream. ip[i] may cross iend[i], + * as long as it is above ilimit, but that indicates corruption. + */ +typedef struct { + BYTE const* ip[4]; + BYTE* op[4]; + U64 bits[4]; + void const* dt; + BYTE const* ilimit; + BYTE* oend; + BYTE const* iend[4]; +} HUF_DecompressFastArgs; + +typedef void (*HUF_DecompressFastLoopFn)(HUF_DecompressFastArgs*); + +/** + * Initializes args for the fast decoding loop. + * @returns 1 on success + * 0 if the fallback implementation should be used. + * Or an error code on failure. + */ +static size_t HUF_DecompressFastArgs_init(HUF_DecompressFastArgs* args, void* dst, size_t dstSize, void const* src, size_t srcSize, const HUF_DTable* DTable) +{ + void const* dt = DTable + 1; + U32 const dtLog = HUF_getDTableDesc(DTable).tableLog; + + const BYTE* const ilimit = (const BYTE*)src + 6 + 8; + + BYTE* const oend = (BYTE*)dst + dstSize; + + /* The fast decoding loop assumes 64-bit little-endian. + * This condition is false on x32. + */ + if (!MEM_isLittleEndian() || MEM_32bits()) + return 0; + + /* strict minimum : jump table + 1 byte per stream */ + if (srcSize < 10) + return ERROR(corruption_detected); + + /* Must have at least 8 bytes per stream because we don't handle initializing smaller bit containers. + * If table log is not correct at this point, fallback to the old decoder. + * On small inputs we don't have enough data to trigger the fast loop, so use the old decoder. + */ + if (dtLog != HUF_DECODER_FAST_TABLELOG) + return 0; + + /* Read the jump table. */ + { + const BYTE* const istart = (const BYTE*)src; + size_t const length1 = MEM_readLE16(istart); + size_t const length2 = MEM_readLE16(istart+2); + size_t const length3 = MEM_readLE16(istart+4); + size_t const length4 = srcSize - (length1 + length2 + length3 + 6); + args->iend[0] = istart + 6; /* jumpTable */ + args->iend[1] = args->iend[0] + length1; + args->iend[2] = args->iend[1] + length2; + args->iend[3] = args->iend[2] + length3; + + /* HUF_initFastDStream() requires this, and this small of an input + * won't benefit from the ASM loop anyways. + * length1 must be >= 16 so that ip[0] >= ilimit before the loop + * starts. + */ + if (length1 < 16 || length2 < 8 || length3 < 8 || length4 < 8) + return 0; + if (length4 > srcSize) return ERROR(corruption_detected); /* overflow */ + } + /* ip[] contains the position that is currently loaded into bits[]. */ + args->ip[0] = args->iend[1] - sizeof(U64); + args->ip[1] = args->iend[2] - sizeof(U64); + args->ip[2] = args->iend[3] - sizeof(U64); + args->ip[3] = (BYTE const*)src + srcSize - sizeof(U64); + + /* op[] contains the output pointers. */ + args->op[0] = (BYTE*)dst; + args->op[1] = args->op[0] + (dstSize+3)/4; + args->op[2] = args->op[1] + (dstSize+3)/4; + args->op[3] = args->op[2] + (dstSize+3)/4; + + /* No point to call the ASM loop for tiny outputs. */ + if (args->op[3] >= oend) + return 0; + + /* bits[] is the bit container. + * It is read from the MSB down to the LSB. + * It is shifted left as it is read, and zeros are + * shifted in. After the lowest valid bit a 1 is + * set, so that CountTrailingZeros(bits[]) can be used + * to count how many bits we've consumed. + */ + args->bits[0] = HUF_initFastDStream(args->ip[0]); + args->bits[1] = HUF_initFastDStream(args->ip[1]); + args->bits[2] = HUF_initFastDStream(args->ip[2]); + args->bits[3] = HUF_initFastDStream(args->ip[3]); + + /* If ip[] >= ilimit, it is guaranteed to be safe to + * reload bits[]. It may be beyond its section, but is + * guaranteed to be valid (>= istart). + */ + args->ilimit = ilimit; + + args->oend = oend; + args->dt = dt; + + return 1; +} + +static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressFastArgs const* args, int stream, BYTE* segmentEnd) +{ + /* Validate that we haven't overwritten. */ + if (args->op[stream] > segmentEnd) + return ERROR(corruption_detected); + /* Validate that we haven't read beyond iend[]. + * Note that ip[] may be < iend[] because the MSB is + * the next bit to read, and we may have consumed 100% + * of the stream, so down to iend[i] - 8 is valid. + */ + if (args->ip[stream] < args->iend[stream] - 8) + return ERROR(corruption_detected); + + /* Construct the BIT_DStream_t. */ + assert(sizeof(size_t) == 8); + bit->bitContainer = MEM_readLEST(args->ip[stream]); + bit->bitsConsumed = ZSTD_countTrailingZeros64(args->bits[stream]); + bit->start = (const char*)args->iend[0]; + bit->limitPtr = bit->start + sizeof(size_t); + bit->ptr = (const char*)args->ip[stream]; + + return 0; +} + #ifndef HUF_FORCE_DECOMPRESS_X2 /*-***************************/ /* single-symbol decoding */ /*-***************************/ -typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX1; /* single-symbol decoding */ +typedef struct { BYTE nbBits; BYTE byte; } HUF_DEltX1; /* single-symbol decoding */ /** * Packs 4 HUF_DEltX1 structs into a U64. This is used to lay down 4 entries at @@ -122,14 +307,45 @@ static U64 HUF_DEltX1_set4(BYTE symbol, BYTE nbBits) { U64 D4; if (MEM_isLittleEndian()) { - D4 = symbol + (nbBits << 8); + D4 = (U64)((symbol << 8) + nbBits); } else { - D4 = (symbol << 8) + nbBits; + D4 = (U64)(symbol + (nbBits << 8)); } + assert(D4 < (1U << 16)); D4 *= 0x0001000100010001ULL; return D4; } +/** + * Increase the tableLog to targetTableLog and rescales the stats. + * If tableLog > targetTableLog this is a no-op. + * @returns New tableLog + */ +static U32 HUF_rescaleStats(BYTE* huffWeight, U32* rankVal, U32 nbSymbols, U32 tableLog, U32 targetTableLog) +{ + if (tableLog > targetTableLog) + return tableLog; + if (tableLog < targetTableLog) { + U32 const scale = targetTableLog - tableLog; + U32 s; + /* Increase the weight for all non-zero probability symbols by scale. */ + for (s = 0; s < nbSymbols; ++s) { + huffWeight[s] += (BYTE)((huffWeight[s] == 0) ? 0 : scale); + } + /* Update rankVal to reflect the new weights. + * All weights except 0 get moved to weight + scale. + * Weights [1, scale] are empty. + */ + for (s = targetTableLog; s > scale; --s) { + rankVal[s] = rankVal[s - scale]; + } + for (s = scale; s > 0; --s) { + rankVal[s] = 0; + } + } + return targetTableLog; +} + typedef struct { U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1]; U32 rankStart[HUF_TABLELOG_ABSOLUTEMAX + 1]; @@ -138,13 +354,7 @@ BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; } HUF_ReadDTableX1_Workspace; - -size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize) -{ - return HUF_readDTableX1_wksp_bmi2(DTable, src, srcSize, workSpace, wkspSize, /* bmi2 */ 0); -} - -size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags) { U32 tableLog = 0; U32 nbSymbols = 0; @@ -159,11 +369,15 @@ DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); /* ZSTD_memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ - iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), bmi2); + iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), flags); if (HUF_isError(iSize)) return iSize; + /* Table header */ { DTableDesc dtd = HUF_getDTableDesc(DTable); + U32 const maxTableLog = dtd.maxTableLog + 1; + U32 const targetTableLog = MIN(maxTableLog, HUF_DECODER_FAST_TABLELOG); + tableLog = HUF_rescaleStats(wksp->huffWeight, wksp->rankVal, nbSymbols, tableLog, targetTableLog); if (tableLog > (U32)(dtd.maxTableLog+1)) return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */ dtd.tableType = 0; dtd.tableLog = (BYTE)tableLog; @@ -182,9 +396,8 @@ * rankStart[0] is not filled because there are no entries in the table for * weight 0. */ - { - int n; - int nextRankStart = 0; + { int n; + U32 nextRankStart = 0; int const unroll = 4; int const nLimit = (int)nbSymbols - unroll + 1; for (n=0; n<(int)tableLog+1; n++) { @@ -207,14 +420,13 @@ /* fill DTable * We fill all entries of each weight in order. - * That way length is a constant for each iteration of the outter loop. + * That way length is a constant for each iteration of the outer loop. * We can switch based on the length to a different inner loop which is * optimized for that particular case. */ - { - U32 w; - int symbol=wksp->rankVal[0]; - int rankStart=0; + { U32 w; + int symbol = wksp->rankVal[0]; + int rankStart = 0; for (w=1; w<tableLog+1; ++w) { int const symbolCount = wksp->rankVal[w]; int const length = (1 << w) >> 1; @@ -304,11 +516,15 @@ BYTE* const pStart = p; /* up to 4 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) { - HUF_DECODE_SYMBOLX1_2(p, bitDPtr); - HUF_DECODE_SYMBOLX1_1(p, bitDPtr); - HUF_DECODE_SYMBOLX1_2(p, bitDPtr); - HUF_DECODE_SYMBOLX1_0(p, bitDPtr); + if ((pEnd - p) > 3) { + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) { + HUF_DECODE_SYMBOLX1_2(p, bitDPtr); + HUF_DECODE_SYMBOLX1_1(p, bitDPtr); + HUF_DECODE_SYMBOLX1_2(p, bitDPtr); + HUF_DECODE_SYMBOLX1_0(p, bitDPtr); + } + } else { + BIT_reloadDStream(bitDPtr); } /* [0-3] symbols remaining */ @@ -320,7 +536,7 @@ while (p < pEnd) HUF_DECODE_SYMBOLX1_0(p, bitDPtr); - return pEnd-pStart; + return (size_t)(pEnd-pStart); } FORCE_INLINE_TEMPLATE size_t @@ -346,6 +562,10 @@ return dstSize; } +/* HUF_decompress4X1_usingDTable_internal_body(): + * Conditions : + * @dstSize >= 6 + */ FORCE_INLINE_TEMPLATE size_t HUF_decompress4X1_usingDTable_internal_body( void* dst, size_t dstSize, @@ -388,33 +608,37 @@ U32 endSignal = 1; if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + if (opStart4 > oend) return ERROR(corruption_detected); /* overflow */ + if (dstSize < 6) return ERROR(corruption_detected); /* stream 4-split doesn't work */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */ - for ( ; (endSignal) & (op4 < olimit) ; ) { - HUF_DECODE_SYMBOLX1_2(op1, &bitD1); - HUF_DECODE_SYMBOLX1_2(op2, &bitD2); - HUF_DECODE_SYMBOLX1_2(op3, &bitD3); - HUF_DECODE_SYMBOLX1_2(op4, &bitD4); - HUF_DECODE_SYMBOLX1_1(op1, &bitD1); - HUF_DECODE_SYMBOLX1_1(op2, &bitD2); - HUF_DECODE_SYMBOLX1_1(op3, &bitD3); - HUF_DECODE_SYMBOLX1_1(op4, &bitD4); - HUF_DECODE_SYMBOLX1_2(op1, &bitD1); - HUF_DECODE_SYMBOLX1_2(op2, &bitD2); - HUF_DECODE_SYMBOLX1_2(op3, &bitD3); - HUF_DECODE_SYMBOLX1_2(op4, &bitD4); - HUF_DECODE_SYMBOLX1_0(op1, &bitD1); - HUF_DECODE_SYMBOLX1_0(op2, &bitD2); - HUF_DECODE_SYMBOLX1_0(op3, &bitD3); - HUF_DECODE_SYMBOLX1_0(op4, &bitD4); - endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished; - endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; - endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished; - endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; + if ((size_t)(oend - op4) >= sizeof(size_t)) { + for ( ; (endSignal) & (op4 < olimit) ; ) { + HUF_DECODE_SYMBOLX1_2(op1, &bitD1); + HUF_DECODE_SYMBOLX1_2(op2, &bitD2); + HUF_DECODE_SYMBOLX1_2(op3, &bitD3); + HUF_DECODE_SYMBOLX1_2(op4, &bitD4); + HUF_DECODE_SYMBOLX1_1(op1, &bitD1); + HUF_DECODE_SYMBOLX1_1(op2, &bitD2); + HUF_DECODE_SYMBOLX1_1(op3, &bitD3); + HUF_DECODE_SYMBOLX1_1(op4, &bitD4); + HUF_DECODE_SYMBOLX1_2(op1, &bitD1); + HUF_DECODE_SYMBOLX1_2(op2, &bitD2); + HUF_DECODE_SYMBOLX1_2(op3, &bitD3); + HUF_DECODE_SYMBOLX1_2(op4, &bitD4); + HUF_DECODE_SYMBOLX1_0(op1, &bitD1); + HUF_DECODE_SYMBOLX1_0(op2, &bitD2); + HUF_DECODE_SYMBOLX1_0(op3, &bitD3); + HUF_DECODE_SYMBOLX1_0(op4, &bitD4); + endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished; + endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; + endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished; + endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; + } } /* check corruption */ @@ -440,74 +664,232 @@ } } +#if HUF_NEED_BMI2_FUNCTION +static BMI2_TARGET_ATTRIBUTE +size_t HUF_decompress4X1_usingDTable_internal_bmi2(void* dst, size_t dstSize, void const* cSrc, + size_t cSrcSize, HUF_DTable const* DTable) { + return HUF_decompress4X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); +} +#endif -typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize, - const void *cSrc, - size_t cSrcSize, - const HUF_DTable *DTable); +static +size_t HUF_decompress4X1_usingDTable_internal_default(void* dst, size_t dstSize, void const* cSrc, + size_t cSrcSize, HUF_DTable const* DTable) { + return HUF_decompress4X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); +} + +#if ZSTD_ENABLE_ASM_X86_64_BMI2 + +HUF_ASM_DECL void HUF_decompress4X1_usingDTable_internal_fast_asm_loop(HUF_DecompressFastArgs* args) ZSTDLIB_HIDDEN; + +#endif + +static HUF_FAST_BMI2_ATTRS +void HUF_decompress4X1_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* args) +{ + U64 bits[4]; + BYTE const* ip[4]; + BYTE* op[4]; + U16 const* const dtable = (U16 const*)args->dt; + BYTE* const oend = args->oend; + BYTE const* const ilimit = args->ilimit; + + /* Copy the arguments to local variables */ + ZSTD_memcpy(&bits, &args->bits, sizeof(bits)); + ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip)); + ZSTD_memcpy(&op, &args->op, sizeof(op)); + + assert(MEM_isLittleEndian()); + assert(!MEM_32bits()); + + for (;;) { + BYTE* olimit; + int stream; + int symbol; + + /* Assert loop preconditions */ +#ifndef NDEBUG + for (stream = 0; stream < 4; ++stream) { + assert(op[stream] <= (stream == 3 ? oend : op[stream + 1])); + assert(ip[stream] >= ilimit); + } +#endif + /* Compute olimit */ + { + /* Each iteration produces 5 output symbols per stream */ + size_t const oiters = (size_t)(oend - op[3]) / 5; + /* Each iteration consumes up to 11 bits * 5 = 55 bits < 7 bytes + * per stream. + */ + size_t const iiters = (size_t)(ip[0] - ilimit) / 7; + /* We can safely run iters iterations before running bounds checks */ + size_t const iters = MIN(oiters, iiters); + size_t const symbols = iters * 5; + + /* We can simply check that op[3] < olimit, instead of checking all + * of our bounds, since we can't hit the other bounds until we've run + * iters iterations, which only happens when op[3] == olimit. + */ + olimit = op[3] + symbols; + + /* Exit fast decoding loop once we get close to the end. */ + if (op[3] + 20 > olimit) + break; + + /* Exit the decoding loop if any input pointer has crossed the + * previous one. This indicates corruption, and a precondition + * to our loop is that ip[i] >= ip[0]. + */ + for (stream = 1; stream < 4; ++stream) { + if (ip[stream] < ip[stream - 1]) + goto _out; + } + } + +#ifndef NDEBUG + for (stream = 1; stream < 4; ++stream) { + assert(ip[stream] >= ip[stream - 1]); + } +#endif + + do { + /* Decode 5 symbols in each of the 4 streams */ + for (symbol = 0; symbol < 5; ++symbol) { + for (stream = 0; stream < 4; ++stream) { + int const index = (int)(bits[stream] >> 53); + int const entry = (int)dtable[index]; + bits[stream] <<= (entry & 63); + op[stream][symbol] = (BYTE)((entry >> 8) & 0xFF); + } + } + /* Reload the bitstreams */ + for (stream = 0; stream < 4; ++stream) { + int const ctz = ZSTD_countTrailingZeros64(bits[stream]); + int const nbBits = ctz & 7; + int const nbBytes = ctz >> 3; + op[stream] += 5; + ip[stream] -= nbBytes; + bits[stream] = MEM_read64(ip[stream]) | 1; + bits[stream] <<= nbBits; + } + } while (op[3] < olimit); + } + +_out: + + /* Save the final values of each of the state variables back to args. */ + ZSTD_memcpy(&args->bits, &bits, sizeof(bits)); + ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip)); + ZSTD_memcpy(&args->op, &op, sizeof(op)); +} + +/** + * @returns @p dstSize on success (>= 6) + * 0 if the fallback implementation should be used + * An error if an error occurred + */ +static HUF_FAST_BMI2_ATTRS +size_t +HUF_decompress4X1_usingDTable_internal_fast( + void* dst, size_t dstSize, + const void* cSrc, size_t cSrcSize, + const HUF_DTable* DTable, + HUF_DecompressFastLoopFn loopFn) +{ + void const* dt = DTable + 1; + const BYTE* const iend = (const BYTE*)cSrc + 6; + BYTE* const oend = (BYTE*)dst + dstSize; + HUF_DecompressFastArgs args; + { size_t const ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); + FORWARD_IF_ERROR(ret, "Failed to init fast loop args"); + if (ret == 0) + return 0; + } + + assert(args.ip[0] >= args.ilimit); + loopFn(&args); + + /* Our loop guarantees that ip[] >= ilimit and that we haven't + * overwritten any op[]. + */ + assert(args.ip[0] >= iend); + assert(args.ip[1] >= iend); + assert(args.ip[2] >= iend); + assert(args.ip[3] >= iend); + assert(args.op[3] <= oend); + (void)iend; + + /* finish bit streams one by one. */ + { size_t const segmentSize = (dstSize+3) / 4; + BYTE* segmentEnd = (BYTE*)dst; + int i; + for (i = 0; i < 4; ++i) { + BIT_DStream_t bit; + if (segmentSize <= (size_t)(oend - segmentEnd)) + segmentEnd += segmentSize; + else + segmentEnd = oend; + FORWARD_IF_ERROR(HUF_initRemainingDStream(&bit, &args, i, segmentEnd), "corruption"); + /* Decompress and validate that we've produced exactly the expected length. */ + args.op[i] += HUF_decodeStreamX1(args.op[i], &bit, segmentEnd, (HUF_DEltX1 const*)dt, HUF_DECODER_FAST_TABLELOG); + if (args.op[i] != segmentEnd) return ERROR(corruption_detected); + } + } + + /* decoded size */ + assert(dstSize != 0); + return dstSize; +} HUF_DGEN(HUF_decompress1X1_usingDTable_internal) -HUF_DGEN(HUF_decompress4X1_usingDTable_internal) - - -size_t HUF_decompress1X1_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) +static size_t HUF_decompress4X1_usingDTable_internal(void* dst, size_t dstSize, void const* cSrc, + size_t cSrcSize, HUF_DTable const* DTable, int flags) { - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + HUF_DecompressUsingDTableFn fallbackFn = HUF_decompress4X1_usingDTable_internal_default; + HUF_DecompressFastLoopFn loopFn = HUF_decompress4X1_usingDTable_internal_fast_c_loop; + +#if DYNAMIC_BMI2 + if (flags & HUF_flags_bmi2) { + fallbackFn = HUF_decompress4X1_usingDTable_internal_bmi2; +# if ZSTD_ENABLE_ASM_X86_64_BMI2 + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X1_usingDTable_internal_fast_asm_loop; + } +# endif + } else { + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); + } +#endif + +#if ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__) + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X1_usingDTable_internal_fast_asm_loop; + } +#endif + + if (!(flags & HUF_flags_disableFast)) { + size_t const ret = HUF_decompress4X1_usingDTable_internal_fast(dst, dstSize, cSrc, cSrcSize, DTable, loopFn); + if (ret != 0) + return ret; + } + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); } -size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, +static size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize); + size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); } - -size_t HUF_decompress4X1_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 0) return ERROR(GENERIC); - return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -} - -static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize, int bmi2) -{ - const BYTE* ip = (const BYTE*) cSrc; - - size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2); - if (HUF_isError(hSize)) return hSize; - if (hSize >= cSrcSize) return ERROR(srcSize_wrong); - ip += hSize; cSrcSize -= hSize; - - return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); -} - -size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0); -} - - #endif /* HUF_FORCE_DECOMPRESS_X2 */ @@ -518,106 +900,226 @@ /* *************************/ typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2; /* double-symbols decoding */ -typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t; +typedef struct { BYTE symbol; } sortedSymbol_t; typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX]; +/** + * Constructs a HUF_DEltX2 in a U32. + */ +static U32 HUF_buildDEltX2U32(U32 symbol, U32 nbBits, U32 baseSeq, int level) +{ + U32 seq; + DEBUG_STATIC_ASSERT(offsetof(HUF_DEltX2, sequence) == 0); + DEBUG_STATIC_ASSERT(offsetof(HUF_DEltX2, nbBits) == 2); + DEBUG_STATIC_ASSERT(offsetof(HUF_DEltX2, length) == 3); + DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(U32)); + if (MEM_isLittleEndian()) { + seq = level == 1 ? symbol : (baseSeq + (symbol << 8)); + return seq + (nbBits << 16) + ((U32)level << 24); + } else { + seq = level == 1 ? (symbol << 8) : ((baseSeq << 8) + symbol); + return (seq << 16) + (nbBits << 8) + (U32)level; + } +} + +/** + * Constructs a HUF_DEltX2. + */ +static HUF_DEltX2 HUF_buildDEltX2(U32 symbol, U32 nbBits, U32 baseSeq, int level) +{ + HUF_DEltX2 DElt; + U32 const val = HUF_buildDEltX2U32(symbol, nbBits, baseSeq, level); + DEBUG_STATIC_ASSERT(sizeof(DElt) == sizeof(val)); + ZSTD_memcpy(&DElt, &val, sizeof(val)); + return DElt; +} + +/** + * Constructs 2 HUF_DEltX2s and packs them into a U64. + */ +static U64 HUF_buildDEltX2U64(U32 symbol, U32 nbBits, U16 baseSeq, int level) +{ + U32 DElt = HUF_buildDEltX2U32(symbol, nbBits, baseSeq, level); + return (U64)DElt + ((U64)DElt << 32); +} + +/** + * Fills the DTable rank with all the symbols from [begin, end) that are each + * nbBits long. + * + * @param DTableRank The start of the rank in the DTable. + * @param begin The first symbol to fill (inclusive). + * @param end The last symbol to fill (exclusive). + * @param nbBits Each symbol is nbBits long. + * @param tableLog The table log. + * @param baseSeq If level == 1 { 0 } else { the first level symbol } + * @param level The level in the table. Must be 1 or 2. + */ +static void HUF_fillDTableX2ForWeight( + HUF_DEltX2* DTableRank, + sortedSymbol_t const* begin, sortedSymbol_t const* end, + U32 nbBits, U32 tableLog, + U16 baseSeq, int const level) +{ + U32 const length = 1U << ((tableLog - nbBits) & 0x1F /* quiet static-analyzer */); + const sortedSymbol_t* ptr; + assert(level >= 1 && level <= 2); + switch (length) { + case 1: + for (ptr = begin; ptr != end; ++ptr) { + HUF_DEltX2 const DElt = HUF_buildDEltX2(ptr->symbol, nbBits, baseSeq, level); + *DTableRank++ = DElt; + } + break; + case 2: + for (ptr = begin; ptr != end; ++ptr) { + HUF_DEltX2 const DElt = HUF_buildDEltX2(ptr->symbol, nbBits, baseSeq, level); + DTableRank[0] = DElt; + DTableRank[1] = DElt; + DTableRank += 2; + } + break; + case 4: + for (ptr = begin; ptr != end; ++ptr) { + U64 const DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level); + ZSTD_memcpy(DTableRank + 0, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 2, &DEltX2, sizeof(DEltX2)); + DTableRank += 4; + } + break; + case 8: + for (ptr = begin; ptr != end; ++ptr) { + U64 const DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level); + ZSTD_memcpy(DTableRank + 0, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 2, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 4, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 6, &DEltX2, sizeof(DEltX2)); + DTableRank += 8; + } + break; + default: + for (ptr = begin; ptr != end; ++ptr) { + U64 const DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level); + HUF_DEltX2* const DTableRankEnd = DTableRank + length; + for (; DTableRank != DTableRankEnd; DTableRank += 8) { + ZSTD_memcpy(DTableRank + 0, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 2, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 4, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTableRank + 6, &DEltX2, sizeof(DEltX2)); + } + } + break; + } +} /* HUF_fillDTableX2Level2() : * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ -static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed, - const U32* rankValOrigin, const int minWeight, - const sortedSymbol_t* sortedSymbols, const U32 sortedListSize, - U32 nbBitsBaseline, U16 baseSeq, U32* wksp, size_t wkspSize) +static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 targetLog, const U32 consumedBits, + const U32* rankVal, const int minWeight, const int maxWeight1, + const sortedSymbol_t* sortedSymbols, U32 const* rankStart, + U32 nbBitsBaseline, U16 baseSeq) { - HUF_DEltX2 DElt; - U32* rankVal = wksp; - - assert(wkspSize >= HUF_TABLELOG_MAX + 1); - (void)wkspSize; - /* get pre-calculated rankVal */ - ZSTD_memcpy(rankVal, rankValOrigin, sizeof(U32) * (HUF_TABLELOG_MAX + 1)); - - /* fill skipped values */ + /* Fill skipped values (all positions up to rankVal[minWeight]). + * These are positions only get a single symbol because the combined weight + * is too large. + */ if (minWeight>1) { - U32 i, skipSize = rankVal[minWeight]; - MEM_writeLE16(&(DElt.sequence), baseSeq); - DElt.nbBits = (BYTE)(consumed); - DElt.length = 1; - for (i = 0; i < skipSize; i++) - DTable[i] = DElt; + U32 const length = 1U << ((targetLog - consumedBits) & 0x1F /* quiet static-analyzer */); + U64 const DEltX2 = HUF_buildDEltX2U64(baseSeq, consumedBits, /* baseSeq */ 0, /* level */ 1); + int const skipSize = rankVal[minWeight]; + assert(length > 1); + assert((U32)skipSize < length); + switch (length) { + case 2: + assert(skipSize == 1); + ZSTD_memcpy(DTable, &DEltX2, sizeof(DEltX2)); + break; + case 4: + assert(skipSize <= 4); + ZSTD_memcpy(DTable + 0, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTable + 2, &DEltX2, sizeof(DEltX2)); + break; + default: + { + int i; + for (i = 0; i < skipSize; i += 8) { + ZSTD_memcpy(DTable + i + 0, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTable + i + 2, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTable + i + 4, &DEltX2, sizeof(DEltX2)); + ZSTD_memcpy(DTable + i + 6, &DEltX2, sizeof(DEltX2)); + } + } + } } - /* fill DTable */ - { U32 s; for (s=0; s<sortedListSize; s++) { /* note : sortedSymbols already skipped */ - const U32 symbol = sortedSymbols[s].symbol; - const U32 weight = sortedSymbols[s].weight; - const U32 nbBits = nbBitsBaseline - weight; - const U32 length = 1 << (sizeLog-nbBits); - const U32 start = rankVal[weight]; - U32 i = start; - const U32 end = start + length; - - MEM_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8))); - DElt.nbBits = (BYTE)(nbBits + consumed); - DElt.length = 2; - do { DTable[i++] = DElt; } while (i<end); /* since length >= 1 */ - - rankVal[weight] += length; - } } + /* Fill each of the second level symbols by weight. */ + { + int w; + for (w = minWeight; w < maxWeight1; ++w) { + int const begin = rankStart[w]; + int const end = rankStart[w+1]; + U32 const nbBits = nbBitsBaseline - w; + U32 const totalBits = nbBits + consumedBits; + HUF_fillDTableX2ForWeight( + DTable + rankVal[w], + sortedSymbols + begin, sortedSymbols + end, + totalBits, targetLog, + baseSeq, /* level */ 2); + } + } } - static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog, - const sortedSymbol_t* sortedList, const U32 sortedListSize, - const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight, - const U32 nbBitsBaseline, U32* wksp, size_t wkspSize) + const sortedSymbol_t* sortedList, + const U32* rankStart, rankValCol_t* rankValOrigin, const U32 maxWeight, + const U32 nbBitsBaseline) { - U32* rankVal = wksp; + U32* const rankVal = rankValOrigin[0]; const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ const U32 minBits = nbBitsBaseline - maxWeight; - U32 s; + int w; + int const wEnd = (int)maxWeight + 1; - assert(wkspSize >= HUF_TABLELOG_MAX + 1); - wksp += HUF_TABLELOG_MAX + 1; - wkspSize -= HUF_TABLELOG_MAX + 1; + /* Fill DTable in order of weight. */ + for (w = 1; w < wEnd; ++w) { + int const begin = (int)rankStart[w]; + int const end = (int)rankStart[w+1]; + U32 const nbBits = nbBitsBaseline - w; - ZSTD_memcpy(rankVal, rankValOrigin, sizeof(U32) * (HUF_TABLELOG_MAX + 1)); - - /* fill DTable */ - for (s=0; s<sortedListSize; s++) { - const U16 symbol = sortedList[s].symbol; - const U32 weight = sortedList[s].weight; - const U32 nbBits = nbBitsBaseline - weight; - const U32 start = rankVal[weight]; - const U32 length = 1 << (targetLog-nbBits); - - if (targetLog-nbBits >= minBits) { /* enough room for a second symbol */ - U32 sortedRank; + if (targetLog-nbBits >= minBits) { + /* Enough room for a second symbol. */ + int start = rankVal[w]; + U32 const length = 1U << ((targetLog - nbBits) & 0x1F /* quiet static-analyzer */); int minWeight = nbBits + scaleLog; + int s; if (minWeight < 1) minWeight = 1; - sortedRank = rankStart[minWeight]; - HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits, - rankValOrigin[nbBits], minWeight, - sortedList+sortedRank, sortedListSize-sortedRank, - nbBitsBaseline, symbol, wksp, wkspSize); + /* Fill the DTable for every symbol of weight w. + * These symbols get at least 1 second symbol. + */ + for (s = begin; s != end; ++s) { + HUF_fillDTableX2Level2( + DTable + start, targetLog, nbBits, + rankValOrigin[nbBits], minWeight, wEnd, + sortedList, rankStart, + nbBitsBaseline, sortedList[s].symbol); + start += length; + } } else { - HUF_DEltX2 DElt; - MEM_writeLE16(&(DElt.sequence), symbol); - DElt.nbBits = (BYTE)(nbBits); - DElt.length = 1; - { U32 const end = start + length; - U32 u; - for (u = start; u < end; u++) DTable[u] = DElt; - } } - rankVal[weight] += length; + /* Only a single symbol. */ + HUF_fillDTableX2ForWeight( + DTable + rankVal[w], + sortedList + begin, sortedList + end, + nbBits, targetLog, + /* baseSeq */ 0, /* level */ 1); + } } } typedef struct { rankValCol_t rankVal[HUF_TABLELOG_MAX]; U32 rankStats[HUF_TABLELOG_MAX + 1]; - U32 rankStart0[HUF_TABLELOG_MAX + 2]; + U32 rankStart0[HUF_TABLELOG_MAX + 3]; sortedSymbol_t sortedSymbol[HUF_SYMBOLVALUE_MAX + 1]; BYTE weightList[HUF_SYMBOLVALUE_MAX + 1]; U32 calleeWksp[HUF_READ_STATS_WORKSPACE_SIZE_U32]; @@ -625,11 +1127,11 @@ size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int flags) { - U32 tableLog, maxW, sizeOfSort, nbSymbols; + U32 tableLog, maxW, nbSymbols; DTableDesc dtd = HUF_getDTableDesc(DTable); - U32 const maxTableLog = dtd.maxTableLog; + U32 maxTableLog = dtd.maxTableLog; size_t iSize; void* dtPtr = DTable+1; /* force compiler to avoid strict-aliasing */ HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr; @@ -647,11 +1149,12 @@ if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge); /* ZSTD_memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ - iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), /* bmi2 */ 0); + iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), flags); if (HUF_isError(iSize)) return iSize; /* check result */ if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ + if (tableLog <= HUF_DECODER_FAST_TABLELOG && maxTableLog > HUF_DECODER_FAST_TABLELOG) maxTableLog = HUF_DECODER_FAST_TABLELOG; /* find maxWeight */ for (maxW = tableLog; wksp->rankStats[maxW]==0; maxW--) {} /* necessarily finds a solution before 0 */ @@ -664,7 +1167,7 @@ rankStart[w] = curr; } rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/ - sizeOfSort = nextRankStart; + rankStart[maxW+1] = nextRankStart; } /* sort symbols by weight */ @@ -673,7 +1176,6 @@ U32 const w = wksp->weightList[s]; U32 const r = rankStart[w]++; wksp->sortedSymbol[r].symbol = (BYTE)s; - wksp->sortedSymbol[r].weight = (BYTE)w; } rankStart[0] = 0; /* forget 0w symbols; this is beginning of weight(1) */ } @@ -698,10 +1200,9 @@ } } } } HUF_fillDTableX2(dt, maxTableLog, - wksp->sortedSymbol, sizeOfSort, + wksp->sortedSymbol, wksp->rankStart0, wksp->rankVal, maxW, - tableLog+1, - wksp->calleeWksp, sizeof(wksp->calleeWksp) / sizeof(U32)); + tableLog+1); dtd.tableLog = (BYTE)maxTableLog; dtd.tableType = 1; @@ -714,7 +1215,7 @@ HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ - ZSTD_memcpy(op, dt+val, 2); + ZSTD_memcpy(op, &dt[val].sequence, 2); BIT_skipBits(DStream, dt[val].nbBits); return dt[val].length; } @@ -723,15 +1224,17 @@ HUF_decodeLastSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog) { size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ - ZSTD_memcpy(op, dt+val, 1); - if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits); - else { + ZSTD_memcpy(op, &dt[val].sequence, 1); + if (dt[val].length==1) { + BIT_skipBits(DStream, dt[val].nbBits); + } else { if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) { BIT_skipBits(DStream, dt[val].nbBits); if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8)) /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8); - } } + } + } return 1; } @@ -753,19 +1256,37 @@ BYTE* const pStart = p; /* up to 8 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_1(p, bitDPtr); - HUF_DECODE_SYMBOLX2_2(p, bitDPtr); - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + if ((size_t)(pEnd - p) >= sizeof(bitDPtr->bitContainer)) { + if (dtLog <= 11 && MEM_64bits()) { + /* up to 10 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-9)) { + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + } + } else { + /* up to 8 symbols at a time */ + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) { + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_1(p, bitDPtr); + HUF_DECODE_SYMBOLX2_2(p, bitDPtr); + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + } + } + } else { + BIT_reloadDStream(bitDPtr); } /* closer to end : up to 2 symbols at a time */ - while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); + if ((size_t)(pEnd - p) >= 2) { + while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2)) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); - while (p <= pEnd-2) - HUF_DECODE_SYMBOLX2_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ + while (p <= pEnd-2) + HUF_DECODE_SYMBOLX2_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ + } if (p < pEnd) p += HUF_decodeLastSymbolX2(p, bitDPtr, dt, dtLog); @@ -800,6 +1321,10 @@ return dstSize; } +/* HUF_decompress4X2_usingDTable_internal_body(): + * Conditions: + * @dstSize >= 6 + */ FORCE_INLINE_TEMPLATE size_t HUF_decompress4X2_usingDTable_internal_body( void* dst, size_t dstSize, @@ -840,58 +1365,62 @@ DTableDesc const dtd = HUF_getDTableDesc(DTable); U32 const dtLog = dtd.tableLog; - if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */ + if (opStart4 > oend) return ERROR(corruption_detected); /* overflow */ + if (dstSize < 6) return ERROR(corruption_detected); /* stream 4-split doesn't work */ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) ); CHECK_F( BIT_initDStream(&bitD2, istart2, length2) ); CHECK_F( BIT_initDStream(&bitD3, istart3, length3) ); CHECK_F( BIT_initDStream(&bitD4, istart4, length4) ); /* 16-32 symbols per loop (4-8 symbols per stream) */ - for ( ; (endSignal) & (op4 < olimit); ) { + if ((size_t)(oend - op4) >= sizeof(size_t)) { + for ( ; (endSignal) & (op4 < olimit); ) { #if defined(__clang__) && (defined(__x86_64__) || defined(__i386__)) - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_1(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_0(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_1(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_0(op2, &bitD2); - endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished; - endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_1(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_0(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_1(op4, &bitD4); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_0(op4, &bitD4); - endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished; - endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_1(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_0(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_1(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_0(op2, &bitD2); + endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished; + endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished; + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_1(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_0(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_1(op4, &bitD4); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_0(op4, &bitD4); + endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished; + endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished; #else - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_1(op1, &bitD1); - HUF_DECODE_SYMBOLX2_1(op2, &bitD2); - HUF_DECODE_SYMBOLX2_1(op3, &bitD3); - HUF_DECODE_SYMBOLX2_1(op4, &bitD4); - HUF_DECODE_SYMBOLX2_2(op1, &bitD1); - HUF_DECODE_SYMBOLX2_2(op2, &bitD2); - HUF_DECODE_SYMBOLX2_2(op3, &bitD3); - HUF_DECODE_SYMBOLX2_2(op4, &bitD4); - HUF_DECODE_SYMBOLX2_0(op1, &bitD1); - HUF_DECODE_SYMBOLX2_0(op2, &bitD2); - HUF_DECODE_SYMBOLX2_0(op3, &bitD3); - HUF_DECODE_SYMBOLX2_0(op4, &bitD4); - endSignal = (U32)LIKELY( - (BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished) - & (BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished) - & (BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished) - & (BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished)); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_1(op1, &bitD1); + HUF_DECODE_SYMBOLX2_1(op2, &bitD2); + HUF_DECODE_SYMBOLX2_1(op3, &bitD3); + HUF_DECODE_SYMBOLX2_1(op4, &bitD4); + HUF_DECODE_SYMBOLX2_2(op1, &bitD1); + HUF_DECODE_SYMBOLX2_2(op2, &bitD2); + HUF_DECODE_SYMBOLX2_2(op3, &bitD3); + HUF_DECODE_SYMBOLX2_2(op4, &bitD4); + HUF_DECODE_SYMBOLX2_0(op1, &bitD1); + HUF_DECODE_SYMBOLX2_0(op2, &bitD2); + HUF_DECODE_SYMBOLX2_0(op3, &bitD3); + HUF_DECODE_SYMBOLX2_0(op4, &bitD4); + endSignal = (U32)LIKELY((U32) + (BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished) + & (BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished) + & (BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished) + & (BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished)); #endif + } } /* check corruption */ @@ -915,68 +1444,281 @@ } } -HUF_DGEN(HUF_decompress1X2_usingDTable_internal) -HUF_DGEN(HUF_decompress4X2_usingDTable_internal) +#if HUF_NEED_BMI2_FUNCTION +static BMI2_TARGET_ATTRIBUTE +size_t HUF_decompress4X2_usingDTable_internal_bmi2(void* dst, size_t dstSize, void const* cSrc, + size_t cSrcSize, HUF_DTable const* DTable) { + return HUF_decompress4X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); +} +#endif -size_t HUF_decompress1X2_usingDTable( +static +size_t HUF_decompress4X2_usingDTable_internal_default(void* dst, size_t dstSize, void const* cSrc, + size_t cSrcSize, HUF_DTable const* DTable) { + return HUF_decompress4X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); +} + +#if ZSTD_ENABLE_ASM_X86_64_BMI2 + +HUF_ASM_DECL void HUF_decompress4X2_usingDTable_internal_fast_asm_loop(HUF_DecompressFastArgs* args) ZSTDLIB_HIDDEN; + +#endif + +static HUF_FAST_BMI2_ATTRS +void HUF_decompress4X2_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* args) +{ + U64 bits[4]; + BYTE const* ip[4]; + BYTE* op[4]; + BYTE* oend[4]; + HUF_DEltX2 const* const dtable = (HUF_DEltX2 const*)args->dt; + BYTE const* const ilimit = args->ilimit; + + /* Copy the arguments to local registers. */ + ZSTD_memcpy(&bits, &args->bits, sizeof(bits)); + ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip)); + ZSTD_memcpy(&op, &args->op, sizeof(op)); + + oend[0] = op[1]; + oend[1] = op[2]; + oend[2] = op[3]; + oend[3] = args->oend; + + assert(MEM_isLittleEndian()); + assert(!MEM_32bits()); + + for (;;) { + BYTE* olimit; + int stream; + int symbol; + + /* Assert loop preconditions */ +#ifndef NDEBUG + for (stream = 0; stream < 4; ++stream) { + assert(op[stream] <= oend[stream]); + assert(ip[stream] >= ilimit); + } +#endif + /* Compute olimit */ + { + /* Each loop does 5 table lookups for each of the 4 streams. + * Each table lookup consumes up to 11 bits of input, and produces + * up to 2 bytes of output. + */ + /* We can consume up to 7 bytes of input per iteration per stream. + * We also know that each input pointer is >= ip[0]. So we can run + * iters loops before running out of input. + */ + size_t iters = (size_t)(ip[0] - ilimit) / 7; + /* Each iteration can produce up to 10 bytes of output per stream. + * Each output stream my advance at different rates. So take the + * minimum number of safe iterations among all the output streams. + */ + for (stream = 0; stream < 4; ++stream) { + size_t const oiters = (size_t)(oend[stream] - op[stream]) / 10; + iters = MIN(iters, oiters); + } + + /* Each iteration produces at least 5 output symbols. So until + * op[3] crosses olimit, we know we haven't executed iters + * iterations yet. This saves us maintaining an iters counter, + * at the expense of computing the remaining # of iterations + * more frequently. + */ + olimit = op[3] + (iters * 5); + + /* Exit the fast decoding loop if we are too close to the end. */ + if (op[3] + 10 > olimit) + break; + + /* Exit the decoding loop if any input pointer has crossed the + * previous one. This indicates corruption, and a precondition + * to our loop is that ip[i] >= ip[0]. + */ + for (stream = 1; stream < 4; ++stream) { + if (ip[stream] < ip[stream - 1]) + goto _out; + } + } + +#ifndef NDEBUG + for (stream = 1; stream < 4; ++stream) { + assert(ip[stream] >= ip[stream - 1]); + } +#endif + + do { + /* Do 5 table lookups for each of the first 3 streams */ + for (symbol = 0; symbol < 5; ++symbol) { + for (stream = 0; stream < 3; ++stream) { + int const index = (int)(bits[stream] >> 53); + HUF_DEltX2 const entry = dtable[index]; + MEM_write16(op[stream], entry.sequence); + bits[stream] <<= (entry.nbBits); + op[stream] += (entry.length); + } + } + /* Do 1 table lookup from the final stream */ + { + int const index = (int)(bits[3] >> 53); + HUF_DEltX2 const entry = dtable[index]; + MEM_write16(op[3], entry.sequence); + bits[3] <<= (entry.nbBits); + op[3] += (entry.length); + } + /* Do 4 table lookups from the final stream & reload bitstreams */ + for (stream = 0; stream < 4; ++stream) { + /* Do a table lookup from the final stream. + * This is interleaved with the reloading to reduce register + * pressure. This shouldn't be necessary, but compilers can + * struggle with codegen with high register pressure. + */ + { + int const index = (int)(bits[3] >> 53); + HUF_DEltX2 const entry = dtable[index]; + MEM_write16(op[3], entry.sequence); + bits[3] <<= (entry.nbBits); + op[3] += (entry.length); + } + /* Reload the bistreams. The final bitstream must be reloaded + * after the 5th symbol was decoded. + */ + { + int const ctz = ZSTD_countTrailingZeros64(bits[stream]); + int const nbBits = ctz & 7; + int const nbBytes = ctz >> 3; + ip[stream] -= nbBytes; + bits[stream] = MEM_read64(ip[stream]) | 1; + bits[stream] <<= nbBits; + } + } + } while (op[3] < olimit); + } + +_out: + + /* Save the final values of each of the state variables back to args. */ + ZSTD_memcpy(&args->bits, &bits, sizeof(bits)); + ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip)); + ZSTD_memcpy(&args->op, &op, sizeof(op)); +} + + +static HUF_FAST_BMI2_ATTRS size_t +HUF_decompress4X2_usingDTable_internal_fast( void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); + const HUF_DTable* DTable, + HUF_DecompressFastLoopFn loopFn) { + void const* dt = DTable + 1; + const BYTE* const iend = (const BYTE*)cSrc + 6; + BYTE* const oend = (BYTE*)dst + dstSize; + HUF_DecompressFastArgs args; + { + size_t const ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); + FORWARD_IF_ERROR(ret, "Failed to init asm args"); + if (ret == 0) + return 0; + } + + assert(args.ip[0] >= args.ilimit); + loopFn(&args); + + /* note : op4 already verified within main loop */ + assert(args.ip[0] >= iend); + assert(args.ip[1] >= iend); + assert(args.ip[2] >= iend); + assert(args.ip[3] >= iend); + assert(args.op[3] <= oend); + (void)iend; + + /* finish bitStreams one by one */ + { + size_t const segmentSize = (dstSize+3) / 4; + BYTE* segmentEnd = (BYTE*)dst; + int i; + for (i = 0; i < 4; ++i) { + BIT_DStream_t bit; + if (segmentSize <= (size_t)(oend - segmentEnd)) + segmentEnd += segmentSize; + else + segmentEnd = oend; + FORWARD_IF_ERROR(HUF_initRemainingDStream(&bit, &args, i, segmentEnd), "corruption"); + args.op[i] += HUF_decodeStreamX2(args.op[i], &bit, segmentEnd, (HUF_DEltX2 const*)dt, HUF_DECODER_FAST_TABLELOG); + if (args.op[i] != segmentEnd) + return ERROR(corruption_detected); + } + } + + /* decoded size */ + return dstSize; } +static size_t HUF_decompress4X2_usingDTable_internal(void* dst, size_t dstSize, void const* cSrc, + size_t cSrcSize, HUF_DTable const* DTable, int flags) +{ + HUF_DecompressUsingDTableFn fallbackFn = HUF_decompress4X2_usingDTable_internal_default; + HUF_DecompressFastLoopFn loopFn = HUF_decompress4X2_usingDTable_internal_fast_c_loop; + +#if DYNAMIC_BMI2 + if (flags & HUF_flags_bmi2) { + fallbackFn = HUF_decompress4X2_usingDTable_internal_bmi2; +# if ZSTD_ENABLE_ASM_X86_64_BMI2 + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X2_usingDTable_internal_fast_asm_loop; + } +# endif + } else { + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); + } +#endif + +#if ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__) + if (!(flags & HUF_flags_disableAsm)) { + loopFn = HUF_decompress4X2_usingDTable_internal_fast_asm_loop; + } +#endif + + if (!(flags & HUF_flags_disableFast)) { + size_t const ret = HUF_decompress4X2_usingDTable_internal_fast(dst, dstSize, cSrc, cSrcSize, DTable, loopFn); + if (ret != 0) + return ret; + } + return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable); +} + +HUF_DGEN(HUF_decompress1X2_usingDTable_internal) + size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, - workSpace, wkspSize); + workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0); + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, flags); } - -size_t HUF_decompress4X2_usingDTable( - void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc dtd = HUF_getDTableDesc(DTable); - if (dtd.tableType != 1) return ERROR(GENERIC); - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -} - -static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, +static size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize, int bmi2) + void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; size_t hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, - workSpace, wkspSize); + workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); } -size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) -{ - return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0); -} - - #endif /* HUF_FORCE_DECOMPRESS_X1 */ @@ -984,66 +1726,28 @@ /* Universal decompression selectors */ /* ***********************************/ -size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc const dtd = HUF_getDTableDesc(DTable); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)dtd; - assert(dtd.tableType == 0); - return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)dtd; - assert(dtd.tableType == 1); - return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#else - return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : - HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#endif -} - -size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, - const void* cSrc, size_t cSrcSize, - const HUF_DTable* DTable) -{ - DTableDesc const dtd = HUF_getDTableDesc(DTable); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)dtd; - assert(dtd.tableType == 0); - return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)dtd; - assert(dtd.tableType == 1); - return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#else - return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) : - HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0); -#endif -} - #if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2) typedef struct { U32 tableTime; U32 decode256Time; } algo_time_t; -static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] = +static const algo_time_t algoTime[16 /* Quantization */][2 /* single, double */] = { /* single, double, quad */ - {{0,0}, {1,1}, {2,2}}, /* Q==0 : impossible */ - {{0,0}, {1,1}, {2,2}}, /* Q==1 : impossible */ - {{ 38,130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */ - {{ 448,128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */ - {{ 556,128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */ - {{ 714,128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */ - {{ 883,128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */ - {{ 897,128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */ - {{ 926,128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */ - {{ 947,128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */ - {{1107,128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */ - {{1177,128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */ - {{1242,128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */ - {{1349,128}, {2644,106}, {5260,106}}, /* Q ==13 : 81-87% */ - {{1455,128}, {2422,124}, {4174,124}}, /* Q ==14 : 87-93% */ - {{ 722,128}, {1891,145}, {1936,146}}, /* Q ==15 : 93-99% */ + {{0,0}, {1,1}}, /* Q==0 : impossible */ + {{0,0}, {1,1}}, /* Q==1 : impossible */ + {{ 150,216}, { 381,119}}, /* Q == 2 : 12-18% */ + {{ 170,205}, { 514,112}}, /* Q == 3 : 18-25% */ + {{ 177,199}, { 539,110}}, /* Q == 4 : 25-32% */ + {{ 197,194}, { 644,107}}, /* Q == 5 : 32-38% */ + {{ 221,192}, { 735,107}}, /* Q == 6 : 38-44% */ + {{ 256,189}, { 881,106}}, /* Q == 7 : 44-50% */ + {{ 359,188}, {1167,109}}, /* Q == 8 : 50-56% */ + {{ 582,187}, {1570,114}}, /* Q == 9 : 56-62% */ + {{ 688,187}, {1712,122}}, /* Q ==10 : 62-69% */ + {{ 825,186}, {1965,136}}, /* Q ==11 : 69-75% */ + {{ 976,185}, {2131,150}}, /* Q ==12 : 75-81% */ + {{1180,186}, {2070,175}}, /* Q ==13 : 81-87% */ + {{1377,185}, {1731,202}}, /* Q ==14 : 87-93% */ + {{1412,185}, {1695,202}}, /* Q ==15 : 93-99% */ }; #endif @@ -1070,42 +1774,15 @@ U32 const D256 = (U32)(dstSize >> 8); U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256); U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256); - DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, to reduce cache eviction */ + DTime1 += DTime1 >> 5; /* small advantage to algorithm using less memory, to reduce cache eviction */ return DTime1 < DTime0; } #endif } - -size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, - size_t dstSize, const void* cSrc, - size_t cSrcSize, void* workSpace, - size_t wkspSize) -{ - /* validation checks */ - if (dstSize == 0) return ERROR(dstSize_tooSmall); - if (cSrcSize == 0) return ERROR(corruption_detected); - - { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)algoNb; - assert(algoNb == 0); - return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)algoNb; - assert(algoNb == 1); - return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); -#else - return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize): - HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize); -#endif - } -} - size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, int flags) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); @@ -1118,71 +1795,71 @@ (void)algoNb; assert(algoNb == 0); return HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize); + cSrcSize, workSpace, wkspSize, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); return HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize); + cSrcSize, workSpace, wkspSize, flags); #else return algoNb ? HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize): + cSrcSize, workSpace, wkspSize, flags): HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc, - cSrcSize, workSpace, wkspSize); + cSrcSize, workSpace, wkspSize, flags); #endif } } -size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) +size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); - return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); - return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #else - return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : - HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) : + HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #endif } #ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags) { const BYTE* ip = (const BYTE*) cSrc; - size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); if (HUF_isError(hSize)) return hSize; if (hSize >= cSrcSize) return ERROR(srcSize_wrong); ip += hSize; cSrcSize -= hSize; - return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2); + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); } #endif -size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2) +size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags) { DTableDesc const dtd = HUF_getDTableDesc(DTable); #if defined(HUF_FORCE_DECOMPRESS_X1) (void)dtd; assert(dtd.tableType == 0); - return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)dtd; assert(dtd.tableType == 1); - return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #else - return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) : - HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2); + return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) : + HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags); #endif } -size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2) +size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags) { /* validation checks */ if (dstSize == 0) return ERROR(dstSize_tooSmall); @@ -1192,160 +1869,14 @@ #if defined(HUF_FORCE_DECOMPRESS_X1) (void)algoNb; assert(algoNb == 0); - return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags); #elif defined(HUF_FORCE_DECOMPRESS_X2) (void)algoNb; assert(algoNb == 1); - return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags); #else - return algoNb ? HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) : - HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2); + return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags) : + HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags); #endif } } - -#ifndef ZSTD_NO_UNUSED_FUNCTIONS -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX1_wksp(DTable, src, srcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_readDTableX2_wksp(DTable, src, srcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); - return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -#ifndef HUF_FORCE_DECOMPRESS_X2 -size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} -size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -#ifndef HUF_FORCE_DECOMPRESS_X1 -size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX); - return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize); -} -#endif - -typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); - -size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ -#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2) - static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 }; -#endif - - /* validation checks */ - if (dstSize == 0) return ERROR(dstSize_tooSmall); - if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ - if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ - if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ - - { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)algoNb; - assert(algoNb == 0); - return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)algoNb; - assert(algoNb == 1); - return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize); -#else - return decompress[algoNb](dst, dstSize, cSrc, cSrcSize); -#endif - } -} - -size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - /* validation checks */ - if (dstSize == 0) return ERROR(dstSize_tooSmall); - if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */ - if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */ - if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */ - - { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -#if defined(HUF_FORCE_DECOMPRESS_X1) - (void)algoNb; - assert(algoNb == 0); - return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize); -#elif defined(HUF_FORCE_DECOMPRESS_X2) - (void)algoNb; - assert(algoNb == 1); - return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize); -#else - return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) : - HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ; -#endif - } -} - -size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} - -size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, - const void* cSrc, size_t cSrcSize) -{ - U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; - return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, - workSpace, sizeof(workSpace)); -} -#endif
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.c b/Utilities/cmzstd/lib/decompress/zstd_ddict.c index ce33547..309ec0d 100644 --- a/Utilities/cmzstd/lib/decompress/zstd_ddict.c +++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,12 +14,12 @@ /*-******************************************************* * Dependencies *********************************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */ #include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "zstd_decompress_internal.h" #include "zstd_ddict.h" @@ -134,7 +134,7 @@ ZSTD_memcpy(internalBuffer, dict, dictSize); } ddict->dictSize = dictSize; - ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ + ddict->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); /* cover both little and big endian */ /* parse dictionary content */ FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) , ""); @@ -240,5 +240,5 @@ unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict) { if (ddict==NULL) return 0; - return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); + return ddict->dictID; }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.h b/Utilities/cmzstd/lib/decompress/zstd_ddict.h index bd03268..c4ca887 100644 --- a/Utilities/cmzstd/lib/decompress/zstd_ddict.h +++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress.c b/Utilities/cmzstd/lib/decompress/zstd_decompress.c index 910bc03..7bc2713 100644 --- a/Utilities/cmzstd/lib/decompress/zstd_decompress.c +++ b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -55,18 +55,18 @@ /*-******************************************************* * Dependencies *********************************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */ -#include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/xxhash.h" /* XXH64_reset, XXH64_update, XXH64_digest, XXH64 */ #include "../common/zstd_internal.h" /* blockProperties_t */ #include "zstd_decompress_internal.h" /* ZSTD_DCtx */ #include "zstd_ddict.h" /* ZSTD_DDictDictContent */ #include "zstd_decompress_block.h" /* ZSTD_decompressBlock_internal */ +#include "../common/bits.h" /* ZSTD_highbit32 */ #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) # include "../legacy/zstd_legacy.h" @@ -79,11 +79,11 @@ *************************************/ #define DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT 4 -#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3 /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float. - * Currently, that means a 0.75 load factor. - * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded - * the load factor of the ddict hash set. - */ +#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3 /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float. + * Currently, that means a 0.75 load factor. + * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded + * the load factor of the ddict hash set. + */ #define DDICT_HASHSET_TABLE_BASE_SIZE 64 #define DDICT_HASHSET_RESIZE_FACTOR 2 @@ -177,12 +177,15 @@ static ZSTD_DDictHashSet* ZSTD_createDDictHashSet(ZSTD_customMem customMem) { ZSTD_DDictHashSet* ret = (ZSTD_DDictHashSet*)ZSTD_customMalloc(sizeof(ZSTD_DDictHashSet), customMem); DEBUGLOG(4, "Allocating new hash set"); + if (!ret) + return NULL; ret->ddictPtrTable = (const ZSTD_DDict**)ZSTD_customCalloc(DDICT_HASHSET_TABLE_BASE_SIZE * sizeof(ZSTD_DDict*), customMem); - ret->ddictPtrTableSize = DDICT_HASHSET_TABLE_BASE_SIZE; - ret->ddictPtrCount = 0; - if (!ret || !ret->ddictPtrTable) { + if (!ret->ddictPtrTable) { + ZSTD_customFree(ret, customMem); return NULL; } + ret->ddictPtrTableSize = DDICT_HASHSET_TABLE_BASE_SIZE; + ret->ddictPtrCount = 0; return ret; } @@ -241,6 +244,7 @@ dctx->outBufferMode = ZSTD_bm_buffered; dctx->forceIgnoreChecksum = ZSTD_d_validateChecksum; dctx->refMultipleDDicts = ZSTD_rmd_refSingleDDict; + dctx->disableHufAsm = 0; } static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx) @@ -255,11 +259,15 @@ dctx->inBuffSize = 0; dctx->outBuffSize = 0; dctx->streamStage = zdss_init; +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) dctx->legacyContext = NULL; dctx->previousLegacyVersion = 0; +#endif dctx->noForwardProgress = 0; dctx->oversizedDuration = 0; - dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); +#if DYNAMIC_BMI2 + dctx->bmi2 = ZSTD_cpuSupportsBmi2(); +#endif dctx->ddictSet = NULL; ZSTD_DCtx_resetParameters(dctx); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION @@ -280,8 +288,7 @@ return dctx; } -ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem) -{ +static ZSTD_DCtx* ZSTD_createDCtx_internal(ZSTD_customMem customMem) { if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL; { ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_customMalloc(sizeof(*dctx), customMem); @@ -292,10 +299,15 @@ } } +ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem) +{ + return ZSTD_createDCtx_internal(customMem); +} + ZSTD_DCtx* ZSTD_createDCtx(void) { DEBUGLOG(3, "ZSTD_createDCtx"); - return ZSTD_createDCtx_advanced(ZSTD_defaultCMem); + return ZSTD_createDCtx_internal(ZSTD_defaultCMem); } static void ZSTD_clearDict(ZSTD_DCtx* dctx) @@ -380,6 +392,19 @@ return 0; } +/*! ZSTD_isSkippableFrame() : + * Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame. + * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. + */ +unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size) +{ + if (size < ZSTD_FRAMEIDSIZE) return 0; + { U32 const magic = MEM_readLE32(buffer); + if ((magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) return 1; + } + return 0; +} + /** ZSTD_frameHeaderSize_internal() : * srcSize must be large enough to reach header size fields. * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless. @@ -415,16 +440,40 @@ * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless * @return : 0, `zfhPtr` is correctly filled, * >0, `srcSize` is too small, value is wanted `srcSize` amount, - * or an error code, which can be tested using ZSTD_isError() */ +** or an error code, which can be tested using ZSTD_isError() */ size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format) { const BYTE* ip = (const BYTE*)src; size_t const minInputSize = ZSTD_startingInputLength(format); - ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */ - if (srcSize < minInputSize) return minInputSize; - RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter"); + DEBUGLOG(5, "ZSTD_getFrameHeader_advanced: minInputSize = %zu, srcSize = %zu", minInputSize, srcSize); + if (srcSize > 0) { + /* note : technically could be considered an assert(), since it's an invalid entry */ + RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter : src==NULL, but srcSize>0"); + } + if (srcSize < minInputSize) { + if (srcSize > 0 && format != ZSTD_f_zstd1_magicless) { + /* when receiving less than @minInputSize bytes, + * control these bytes at least correspond to a supported magic number + * in order to error out early if they don't. + **/ + size_t const toCopy = MIN(4, srcSize); + unsigned char hbuf[4]; MEM_writeLE32(hbuf, ZSTD_MAGICNUMBER); + assert(src != NULL); + ZSTD_memcpy(hbuf, src, toCopy); + if ( MEM_readLE32(hbuf) != ZSTD_MAGICNUMBER ) { + /* not a zstd frame : let's check if it's a skippable frame */ + MEM_writeLE32(hbuf, ZSTD_MAGIC_SKIPPABLE_START); + ZSTD_memcpy(hbuf, src, toCopy); + if ((MEM_readLE32(hbuf) & ZSTD_MAGIC_SKIPPABLE_MASK) != ZSTD_MAGIC_SKIPPABLE_START) { + RETURN_ERROR(prefix_unknown, + "first bytes don't correspond to any supported magic number"); + } } } + return minInputSize; + } + + ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzers may not understand that zfhPtr will be read only if return value is zero, since they are 2 different signals */ if ( (format != ZSTD_f_zstd1_magicless) && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) { if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { @@ -466,7 +515,9 @@ } switch(dictIDSizeCode) { - default: assert(0); /* impossible */ + default: + assert(0); /* impossible */ + ZSTD_FALLTHROUGH; case 0 : break; case 1 : dictID = ip[pos]; pos++; break; case 2 : dictID = MEM_readLE16(ip+pos); pos+=2; break; @@ -474,7 +525,9 @@ } switch(fcsID) { - default: assert(0); /* impossible */ + default: + assert(0); /* impossible */ + ZSTD_FALLTHROUGH; case 0 : if (singleSegment) frameContentSize = ip[pos]; break; case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break; case 2 : frameContentSize = MEM_readLE32(ip+pos); break; @@ -503,7 +556,6 @@ return ZSTD_getFrameHeader_advanced(zfhPtr, src, srcSize, ZSTD_f_zstd1); } - /** ZSTD_getFrameContentSize() : * compatible with legacy mode * @return : decompressed size of the single frame pointed to be `src` if known, otherwise @@ -537,18 +589,52 @@ sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE); RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32, frameParameter_unsupported, ""); - { - size_t const skippableSize = skippableHeaderSize + sizeU32; + { size_t const skippableSize = skippableHeaderSize + sizeU32; RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, ""); return skippableSize; } } +/*! ZSTD_readSkippableFrame() : + * Retrieves content of a skippable frame, and writes it to dst buffer. + * + * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written, + * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested + * in the magicVariant. + * + * Returns an error if destination buffer is not large enough, or if this is not a valid skippable frame. + * + * @return : number of bytes written or a ZSTD error. + */ +size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, + unsigned* magicVariant, /* optional, can be NULL */ + const void* src, size_t srcSize) +{ + RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong, ""); + + { U32 const magicNumber = MEM_readLE32(src); + size_t skippableFrameSize = readSkippableFrameSize(src, srcSize); + size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE; + + /* check input validity */ + RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, ""); + RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, ""); + RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, ""); + + /* deliver payload */ + if (skippableContentSize > 0 && dst != NULL) + ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize); + if (magicVariant != NULL) + *magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START; + return skippableContentSize; + } +} + /** ZSTD_findDecompressedSize() : - * compatible with legacy mode * `srcSize` must be the exact length of some number of ZSTD compressed and/or * skippable frames - * @return : decompressed size of the frames contained */ + * note: compatible with legacy mode + * @return : decompressed size of the frames contained */ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize) { unsigned long long totalDstSize = 0; @@ -558,9 +644,7 @@ if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { size_t const skippableSize = readSkippableFrameSize(src, srcSize); - if (ZSTD_isError(skippableSize)) { - return ZSTD_CONTENTSIZE_ERROR; - } + if (ZSTD_isError(skippableSize)) return ZSTD_CONTENTSIZE_ERROR; assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize; @@ -568,17 +652,17 @@ continue; } - { unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); - if (ret >= ZSTD_CONTENTSIZE_ERROR) return ret; + { unsigned long long const fcs = ZSTD_getFrameContentSize(src, srcSize); + if (fcs >= ZSTD_CONTENTSIZE_ERROR) return fcs; - /* check for overflow */ - if (totalDstSize + ret < totalDstSize) return ZSTD_CONTENTSIZE_ERROR; - totalDstSize += ret; + if (totalDstSize + fcs < totalDstSize) + return ZSTD_CONTENTSIZE_ERROR; /* check for overflow */ + totalDstSize += fcs; } + /* skip to next frame */ { size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); - if (ZSTD_isError(frameSrcSize)) { - return ZSTD_CONTENTSIZE_ERROR; - } + if (ZSTD_isError(frameSrcSize)) return ZSTD_CONTENTSIZE_ERROR; + assert(frameSrcSize <= srcSize); src = (const BYTE *)src + frameSrcSize; srcSize -= frameSrcSize; @@ -700,10 +784,11 @@ ip += 4; } + frameSizeInfo.nbBlocks = nbBlocks; frameSizeInfo.compressedSize = (size_t)(ip - ipstart); frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) ? zfh.frameContentSize - : nbBlocks * zfh.blockSizeMax; + : (unsigned long long)nbBlocks * zfh.blockSizeMax; return frameSizeInfo; } } @@ -743,6 +828,48 @@ return bound; } +size_t ZSTD_decompressionMargin(void const* src, size_t srcSize) +{ + size_t margin = 0; + unsigned maxBlockSize = 0; + + /* Iterate over each frame */ + while (srcSize > 0) { + ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize); + size_t const compressedSize = frameSizeInfo.compressedSize; + unsigned long long const decompressedBound = frameSizeInfo.decompressedBound; + ZSTD_frameHeader zfh; + + FORWARD_IF_ERROR(ZSTD_getFrameHeader(&zfh, src, srcSize), ""); + if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR) + return ERROR(corruption_detected); + + if (zfh.frameType == ZSTD_frame) { + /* Add the frame header to our margin */ + margin += zfh.headerSize; + /* Add the checksum to our margin */ + margin += zfh.checksumFlag ? 4 : 0; + /* Add 3 bytes per block */ + margin += 3 * frameSizeInfo.nbBlocks; + + /* Compute the max block size */ + maxBlockSize = MAX(maxBlockSize, zfh.blockSizeMax); + } else { + assert(zfh.frameType == ZSTD_skippableFrame); + /* Add the entire skippable frame size to our margin. */ + margin += compressedSize; + } + + assert(srcSize >= compressedSize); + src = (const BYTE*)src + compressedSize; + srcSize -= compressedSize; + } + + /* Add the max block size back to the margin. */ + margin += maxBlockSize; + + return margin; +} /*-************************************************************* * Frame decoding @@ -768,7 +895,7 @@ if (srcSize == 0) return 0; RETURN_ERROR(dstBuffer_null, ""); } - ZSTD_memcpy(dst, src, srcSize); + ZSTD_memmove(dst, src, srcSize); return srcSize; } @@ -846,6 +973,7 @@ /* Loop on each block */ while (1) { + BYTE* oBlockEnd = oend; size_t decodedSize; blockProperties_t blockProperties; size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties); @@ -855,16 +983,34 @@ remainingSrcSize -= ZSTD_blockHeaderSize; RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong, ""); + if (ip >= op && ip < oBlockEnd) { + /* We are decompressing in-place. Limit the output pointer so that we + * don't overwrite the block that we are currently reading. This will + * fail decompression if the input & output pointers aren't spaced + * far enough apart. + * + * This is important to set, even when the pointers are far enough + * apart, because ZSTD_decompressBlock_internal() can decide to store + * literals in the output buffer, after the block it is decompressing. + * Since we don't want anything to overwrite our input, we have to tell + * ZSTD_decompressBlock_internal to never write past ip. + * + * See ZSTD_allocateLiteralsBuffer() for reference. + */ + oBlockEnd = op + (ip - op); + } + switch(blockProperties.blockType) { case bt_compressed: - decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oend-op), ip, cBlockSize, /* frame */ 1); + decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oBlockEnd-op), ip, cBlockSize, /* frame */ 1, not_streaming); break; case bt_raw : + /* Use oend instead of oBlockEnd because this function is safe to overlap. It uses memmove. */ decodedSize = ZSTD_copyRawBlock(op, (size_t)(oend-op), ip, cBlockSize); break; case bt_rle : - decodedSize = ZSTD_setRleBlock(op, (size_t)(oend-op), *ip, blockProperties.origSize); + decodedSize = ZSTD_setRleBlock(op, (size_t)(oBlockEnd-op), *ip, blockProperties.origSize); break; case bt_reserved : default: @@ -899,6 +1045,7 @@ } ZSTD_DCtx_trace_end(dctx, (U64)(op-ostart), (U64)(ip-istart), /* streaming */ 0); /* Allow caller to get size read */ + DEBUGLOG(4, "ZSTD_decompressFrame: decompressed frame of size %zi, consuming %zi bytes of input", op-ostart, ip - (const BYTE*)*srcPtr); *srcPtr = ip; *srcSizePtr = remainingSrcSize; return (size_t)(op-ostart); @@ -945,17 +1092,18 @@ } #endif - { U32 const magicNumber = MEM_readLE32(src); - DEBUGLOG(4, "reading magic number %08X (expecting %08X)", - (unsigned)magicNumber, ZSTD_MAGICNUMBER); + if (srcSize >= 4) { + U32 const magicNumber = MEM_readLE32(src); + DEBUGLOG(5, "reading magic number %08X", (unsigned)magicNumber); if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { + /* skippable frame detected : skip it */ size_t const skippableSize = readSkippableFrameSize(src, srcSize); - FORWARD_IF_ERROR(skippableSize, "readSkippableFrameSize failed"); + FORWARD_IF_ERROR(skippableSize, "invalid skippable frame"); assert(skippableSize <= srcSize); src = (const BYTE *)src + skippableSize; srcSize -= skippableSize; - continue; + continue; /* check next frame */ } } if (ddict) { @@ -1009,7 +1157,7 @@ switch (dctx->dictUses) { default: assert(0 /* Impossible */); - /* fall-through */ + ZSTD_FALLTHROUGH; case ZSTD_dont_use: ZSTD_clearDict(dctx); return NULL; @@ -1031,7 +1179,7 @@ { #if defined(ZSTD_HEAPMODE) && (ZSTD_HEAPMODE>=1) size_t regenSize; - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); + ZSTD_DCtx* const dctx = ZSTD_createDCtx_internal(ZSTD_defaultCMem); RETURN_ERROR_IF(dctx==NULL, memory_allocation, "NULL pointer!"); regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize); ZSTD_freeDCtx(dctx); @@ -1051,8 +1199,8 @@ size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; } /** - * Similar to ZSTD_nextSrcSizeToDecompress(), but when when a block input can be streamed, - * we allow taking a partial block as the input. Currently only raw uncompressed blocks can + * Similar to ZSTD_nextSrcSizeToDecompress(), but when a block input can be streamed, we + * allow taking a partial block as the input. Currently only raw uncompressed blocks can * be streamed. * * For blocks that can be streamed, this allows us to reduce the latency until we produce @@ -1065,7 +1213,7 @@ return dctx->expected; if (dctx->bType != bt_raw) return dctx->expected; - return MIN(MAX(inputSize, 1), dctx->expected); + return BOUNDED(1, inputSize, dctx->expected); } ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx) { @@ -1073,7 +1221,9 @@ { default: /* should not happen */ assert(0); + ZSTD_FALLTHROUGH; case ZSTDds_getFrameHeaderSize: + ZSTD_FALLTHROUGH; case ZSTDds_decodeFrameHeader: return ZSTDnit_frameHeader; case ZSTDds_decodeBlockHeader: @@ -1085,6 +1235,7 @@ case ZSTDds_checkChecksum: return ZSTDnit_checksum; case ZSTDds_decodeSkippableHeader: + ZSTD_FALLTHROUGH; case ZSTDds_skipFrame: return ZSTDnit_skippableFrame; } @@ -1168,7 +1319,7 @@ { case bt_compressed: DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed"); - rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1); + rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1, is_streaming); dctx->expected = 0; /* Streaming not supported */ break; case bt_raw : @@ -1249,7 +1400,7 @@ default: assert(0); /* impossible */ - RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ + RETURN_ERROR(GENERIC, "impossible to reach"); /* some compilers require default to do something */ } } @@ -1290,11 +1441,11 @@ /* in minimal huffman, we always use X1 variants */ size_t const hSize = HUF_readDTableX1_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, - workspace, workspaceSize); + workspace, workspaceSize, /* flags */ 0); #else size_t const hSize = HUF_readDTableX2_wksp(entropy->hufTable, dictPtr, (size_t)(dictEnd - dictPtr), - workspace, workspaceSize); + workspace, workspaceSize, /* flags */ 0); #endif RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted, ""); dictPtr += hSize; @@ -1393,7 +1544,7 @@ dctx->prefixStart = NULL; dctx->virtualStart = NULL; dctx->dictEnd = NULL; - dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ + dctx->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); /* cover both little and big endian */ dctx->litEntropy = dctx->fseEntropy = 0; dctx->dictID = 0; dctx->bType = bt_reserved; @@ -1455,7 +1606,7 @@ * This could for one of the following reasons : * - The frame does not require a dictionary (most common case). * - The frame was built with dictID intentionally removed. - * Needed dictionary is a hidden information. + * Needed dictionary is a hidden piece of information. * Note : this use case also happens when using a non-conformant dictionary. * - `srcSize` is too small, and as a result, frame header could not be decoded. * Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`. @@ -1464,7 +1615,7 @@ * ZSTD_getFrameHeader(), which will provide a more precise error code. */ unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize) { - ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0 }; + ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0, 0, 0 }; size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize); if (ZSTD_isError(hError)) return 0; return zfp.dictID; @@ -1493,7 +1644,7 @@ ZSTD_DStream* ZSTD_createDStream(void) { DEBUGLOG(3, "ZSTD_createDStream"); - return ZSTD_createDStream_advanced(ZSTD_defaultCMem); + return ZSTD_createDCtx_internal(ZSTD_defaultCMem); } ZSTD_DStream* ZSTD_initStaticDStream(void *workspace, size_t workspaceSize) @@ -1503,7 +1654,7 @@ ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem) { - return ZSTD_createDCtx_advanced(customMem); + return ZSTD_createDCtx_internal(customMem); } size_t ZSTD_freeDStream(ZSTD_DStream* zds) @@ -1571,7 +1722,9 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) { DEBUGLOG(4, "ZSTD_initDStream"); - return ZSTD_initDStream_usingDDict(zds, NULL); + FORWARD_IF_ERROR(ZSTD_DCtx_reset(zds, ZSTD_reset_session_only), ""); + FORWARD_IF_ERROR(ZSTD_DCtx_refDDict(zds, NULL), ""); + return ZSTD_startingInputLength(zds->format); } /* ZSTD_initDStream_usingDDict() : @@ -1579,6 +1732,7 @@ * this function cannot fail */ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) { + DEBUGLOG(4, "ZSTD_initDStream_usingDDict"); FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) , ""); return ZSTD_startingInputLength(dctx->format); @@ -1589,6 +1743,7 @@ * this function cannot fail */ size_t ZSTD_resetDStream(ZSTD_DStream* dctx) { + DEBUGLOG(4, "ZSTD_resetDStream"); FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), ""); return ZSTD_startingInputLength(dctx->format); } @@ -1660,6 +1815,11 @@ bounds.lowerBound = (int)ZSTD_rmd_refSingleDDict; bounds.upperBound = (int)ZSTD_rmd_refMultipleDDicts; return bounds; + case ZSTD_d_disableHuffmanAssembly: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + default:; } bounds.error = ERROR(parameter_unsupported); @@ -1700,6 +1860,9 @@ case ZSTD_d_refMultipleDDicts: *value = (int)dctx->refMultipleDDicts; return 0; + case ZSTD_d_disableHuffmanAssembly: + *value = (int)dctx->disableHufAsm; + return 0; default:; } RETURN_ERROR(parameter_unsupported, ""); @@ -1733,6 +1896,10 @@ } dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value; return 0; + case ZSTD_d_disableHuffmanAssembly: + CHECK_DBOUNDS(ZSTD_d_disableHuffmanAssembly, value); + dctx->disableHufAsm = value != 0; + return 0; default:; } RETURN_ERROR(parameter_unsupported, ""); @@ -1763,7 +1930,8 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize) { size_t const blockSize = (size_t) MIN(windowSize, ZSTD_BLOCKSIZE_MAX); - unsigned long long const neededRBSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2); + /* space is needed to store the litbuffer after the output of a given block without stomping the extDict of a previous run, as well as to cover both windows against wildcopy*/ + unsigned long long const neededRBSize = windowSize + blockSize + ZSTD_BLOCKSIZE_MAX + (WILDCOPY_OVERLENGTH * 2); unsigned long long const neededSize = MIN(frameContentSize, neededRBSize); size_t const minRBSize = (size_t) neededSize; RETURN_ERROR_IF((unsigned long long)minRBSize != neededSize, @@ -1897,10 +2065,12 @@ DEBUGLOG(5, "stage zdss_init => transparent reset "); zds->streamStage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) zds->legacyVersion = 0; +#endif zds->hostageByte = 0; zds->expectedOutBuffer = *output; - /* fall-through */ + ZSTD_FALLTHROUGH; case zdss_loadHeader : DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip)); @@ -1917,7 +2087,6 @@ if (zds->refMultipleDDicts && zds->ddictSet) { ZSTD_DCtx_selectFrameDDict(zds); } - DEBUGLOG(5, "header size : %u", (U32)hSize); if (ZSTD_isError(hSize)) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart); @@ -1949,6 +2118,11 @@ zds->lhSize += remainingInput; } input->pos = input->size; + /* check first few bytes */ + FORWARD_IF_ERROR( + ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format), + "First few bytes detected incorrect" ); + /* return hint input size */ return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ } assert(ip != NULL); @@ -1966,8 +2140,9 @@ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, (size_t)(oend-op), istart, cSize, ZSTD_getDDict(zds)); if (ZSTD_isError(decompressedSize)) return decompressedSize; DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") + assert(istart != NULL); ip = istart + cSize; - op += decompressedSize; + op = op ? op + decompressedSize : op; /* can occur if frameContentSize = 0 (empty frame) */ zds->expected = 0; zds->streamStage = zdss_init; someMoreWork = 0; @@ -2038,7 +2213,7 @@ zds->outBuffSize = neededOutBuffSize; } } } zds->streamStage = zdss_read; - /* fall-through */ + ZSTD_FALLTHROUGH; case zdss_read: DEBUGLOG(5, "stage zdss_read"); @@ -2051,13 +2226,14 @@ } if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize), ""); + assert(ip != NULL); ip += neededInSize; /* Function modifies the stage so we must break */ break; } } if (ip==iend) { someMoreWork = 0; break; } /* no more input */ zds->streamStage = zdss_load; - /* fall-through */ + ZSTD_FALLTHROUGH; case zdss_load: { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds); @@ -2065,7 +2241,7 @@ int const isSkipFrame = ZSTD_isSkipFrame(zds); size_t loadedSize; /* At this point we shouldn't be decompressing a block that we can stream. */ - assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip)); + assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (size_t)(iend - ip))); if (isSkipFrame) { loadedSize = MIN(toLoad, (size_t)(iend-ip)); } else { @@ -2074,8 +2250,11 @@ "should never happen"); loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, (size_t)(iend-ip)); } - ip += loadedSize; - zds->inPos += loadedSize; + if (loadedSize != 0) { + /* ip may be NULL */ + ip += loadedSize; + zds->inPos += loadedSize; + } if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */ @@ -2085,14 +2264,17 @@ break; } case zdss_flush: - { size_t const toFlushSize = zds->outEnd - zds->outStart; + { + size_t const toFlushSize = zds->outEnd - zds->outStart; size_t const flushedSize = ZSTD_limitCopy(op, (size_t)(oend-op), zds->outBuff + zds->outStart, toFlushSize); - op += flushedSize; + + op = op ? op + flushedSize : op; + zds->outStart += flushedSize; if (flushedSize == toFlushSize) { /* flush completed */ zds->streamStage = zdss_read; if ( (zds->outBuffSize < zds->fParams.frameContentSize) - && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) { + && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) { DEBUGLOG(5, "restart filling outBuff from beginning (left:%i, needed:%u)", (int)(zds->outBuffSize - zds->outStart), (U32)zds->fParams.blockSizeMax); @@ -2106,7 +2288,7 @@ default: assert(0); /* impossible */ - RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */ + RETURN_ERROR(GENERIC, "impossible to reach"); /* some compilers require default to do something */ } } /* result */ @@ -2119,8 +2301,8 @@ if ((ip==istart) && (op==ostart)) { /* no forward progress */ zds->noForwardProgress ++; if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) { - RETURN_ERROR_IF(op==oend, dstSize_tooSmall, ""); - RETURN_ERROR_IF(ip==iend, srcSize_wrong, ""); + RETURN_ERROR_IF(op==oend, noForwardProgress_destFull, ""); + RETURN_ERROR_IF(ip==iend, noForwardProgress_inputEmpty, ""); assert(0); } } else { @@ -2157,11 +2339,17 @@ void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos) { - ZSTD_outBuffer output = { dst, dstCapacity, *dstPos }; - ZSTD_inBuffer input = { src, srcSize, *srcPos }; - /* ZSTD_compress_generic() will check validity of dstPos and srcPos */ - size_t const cErr = ZSTD_decompressStream(dctx, &output, &input); - *dstPos = output.pos; - *srcPos = input.pos; - return cErr; + ZSTD_outBuffer output; + ZSTD_inBuffer input; + output.dst = dst; + output.size = dstCapacity; + output.pos = *dstPos; + input.src = src; + input.size = srcSize; + input.pos = *srcPos; + { size_t const cErr = ZSTD_decompressStream(dctx, &output, &input); + *dstPos = output.pos; + *srcPos = input.pos; + return cErr; + } }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c index 349dcdc..09896a9 100644 --- a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c +++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,12 +20,12 @@ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY #include "../common/fse.h" -#define HUF_STATIC_LINKING_ONLY #include "../common/huf.h" #include "../common/zstd_internal.h" #include "zstd_decompress_internal.h" /* ZSTD_DCtx */ #include "zstd_ddict.h" /* ZSTD_DDictDictContent */ #include "zstd_decompress_block.h" +#include "../common/bits.h" /* ZSTD_highbit32 */ /*_******************************************************* * Macros @@ -69,15 +69,56 @@ } } +/* Allocate buffer for literals, either overlapping current dst, or split between dst and litExtraBuffer, or stored entirely within litExtraBuffer */ +static void ZSTD_allocateLiteralsBuffer(ZSTD_DCtx* dctx, void* const dst, const size_t dstCapacity, const size_t litSize, + const streaming_operation streaming, const size_t expectedWriteSize, const unsigned splitImmediately) +{ + if (streaming == not_streaming && dstCapacity > ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH + litSize + WILDCOPY_OVERLENGTH) + { + /* room for litbuffer to fit without read faulting */ + dctx->litBuffer = (BYTE*)dst + ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH; + dctx->litBufferEnd = dctx->litBuffer + litSize; + dctx->litBufferLocation = ZSTD_in_dst; + } + else if (litSize > ZSTD_LITBUFFEREXTRASIZE) + { + /* won't fit in litExtraBuffer, so it will be split between end of dst and extra buffer */ + if (splitImmediately) { + /* won't fit in litExtraBuffer, so it will be split between end of dst and extra buffer */ + dctx->litBuffer = (BYTE*)dst + expectedWriteSize - litSize + ZSTD_LITBUFFEREXTRASIZE - WILDCOPY_OVERLENGTH; + dctx->litBufferEnd = dctx->litBuffer + litSize - ZSTD_LITBUFFEREXTRASIZE; + } + else { + /* initially this will be stored entirely in dst during huffman decoding, it will partially be shifted to litExtraBuffer after */ + dctx->litBuffer = (BYTE*)dst + expectedWriteSize - litSize; + dctx->litBufferEnd = (BYTE*)dst + expectedWriteSize; + } + dctx->litBufferLocation = ZSTD_split; + } + else + { + /* fits entirely within litExtraBuffer, so no split is necessary */ + dctx->litBuffer = dctx->litExtraBuffer; + dctx->litBufferEnd = dctx->litBuffer + litSize; + dctx->litBufferLocation = ZSTD_not_in_dst; + } +} /* Hidden declaration for fullbench */ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, - const void* src, size_t srcSize); + const void* src, size_t srcSize, + void* dst, size_t dstCapacity, const streaming_operation streaming); /*! ZSTD_decodeLiteralsBlock() : + * Where it is possible to do so without being stomped by the output during decompression, the literals block will be stored + * in the dstBuffer. If there is room to do so, it will be stored in full in the excess dst space after where the current + * block will be output. Otherwise it will be stored at the end of the current dst blockspace, with a small portion being + * stored in dctx->litExtraBuffer to help keep it "ahead" of the current output write. + * * @return : nb of bytes read from src (< srcSize ) * note : symbol not declared but exposed for fullbench */ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, - const void* src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ + const void* src, size_t srcSize, /* note : srcSize < BLOCKSIZE */ + void* dst, size_t dstCapacity, const streaming_operation streaming) { DEBUGLOG(5, "ZSTD_decodeLiteralsBlock"); RETURN_ERROR_IF(srcSize < MIN_CBLOCK_SIZE, corruption_detected, ""); @@ -90,15 +131,19 @@ case set_repeat: DEBUGLOG(5, "set_repeat flag : re-using stats from previous compressed literals block"); RETURN_ERROR_IF(dctx->litEntropy==0, dictionary_corrupted, ""); - /* fall-through */ + ZSTD_FALLTHROUGH; case set_compressed: - RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3"); + RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need up to 5 for case 3"); { size_t lhSize, litSize, litCSize; U32 singleStream=0; U32 const lhlCode = (istart[0] >> 2) & 3; U32 const lhc = MEM_readLE32(istart); size_t hufSuccess; + size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity); + int const flags = 0 + | (ZSTD_DCtx_get_bmi2(dctx) ? HUF_flags_bmi2 : 0) + | (dctx->disableHufAsm ? HUF_flags_disableAsm : 0); switch(lhlCode) { case 0: case 1: default: /* note : default is impossible, since lhlCode into [0..3] */ @@ -121,8 +166,15 @@ litCSize = (lhc >> 22) + ((size_t)istart[4] << 10); break; } + RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled"); RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, ""); + if (!singleStream) + RETURN_ERROR_IF(litSize < MIN_LITERALS_FOR_4_STREAMS, literals_headerWrong, + "Not enough literals (%zu) for the 4-streams mode (min %u)", + litSize, MIN_LITERALS_FOR_4_STREAMS); RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected, ""); + RETURN_ERROR_IF(expectedWriteSize < litSize , dstSize_tooSmall, ""); + ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 0); /* prefetch huffman table if cold */ if (dctx->ddictIsCold && (litSize > 768 /* heuristic */)) { @@ -131,13 +183,14 @@ if (litEncType==set_repeat) { if (singleStream) { - hufSuccess = HUF_decompress1X_usingDTable_bmi2( + hufSuccess = HUF_decompress1X_usingDTable( dctx->litBuffer, litSize, istart+lhSize, litCSize, - dctx->HUFptr, dctx->bmi2); + dctx->HUFptr, flags); } else { - hufSuccess = HUF_decompress4X_usingDTable_bmi2( + assert(litSize >= MIN_LITERALS_FOR_4_STREAMS); + hufSuccess = HUF_decompress4X_usingDTable( dctx->litBuffer, litSize, istart+lhSize, litCSize, - dctx->HUFptr, dctx->bmi2); + dctx->HUFptr, flags); } } else { if (singleStream) { @@ -145,20 +198,27 @@ hufSuccess = HUF_decompress1X_DCtx_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, - sizeof(dctx->workspace)); + sizeof(dctx->workspace), flags); #else - hufSuccess = HUF_decompress1X1_DCtx_wksp_bmi2( + hufSuccess = HUF_decompress1X1_DCtx_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, - sizeof(dctx->workspace), dctx->bmi2); + sizeof(dctx->workspace), flags); #endif } else { - hufSuccess = HUF_decompress4X_hufOnly_wksp_bmi2( + hufSuccess = HUF_decompress4X_hufOnly_wksp( dctx->entropy.hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->workspace, - sizeof(dctx->workspace), dctx->bmi2); + sizeof(dctx->workspace), flags); } } + if (dctx->litBufferLocation == ZSTD_split) + { + ZSTD_memcpy(dctx->litExtraBuffer, dctx->litBufferEnd - ZSTD_LITBUFFEREXTRASIZE, ZSTD_LITBUFFEREXTRASIZE); + ZSTD_memmove(dctx->litBuffer + ZSTD_LITBUFFEREXTRASIZE - WILDCOPY_OVERLENGTH, dctx->litBuffer, litSize - ZSTD_LITBUFFEREXTRASIZE); + dctx->litBuffer += ZSTD_LITBUFFEREXTRASIZE - WILDCOPY_OVERLENGTH; + dctx->litBufferEnd -= WILDCOPY_OVERLENGTH; + } RETURN_ERROR_IF(HUF_isError(hufSuccess), corruption_detected, ""); @@ -166,13 +226,13 @@ dctx->litSize = litSize; dctx->litEntropy = 1; if (litEncType==set_compressed) dctx->HUFptr = dctx->entropy.hufTable; - ZSTD_memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); return litCSize + lhSize; } case set_basic: { size_t litSize, lhSize; U32 const lhlCode = ((istart[0]) >> 2) & 3; + size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity); switch(lhlCode) { case 0: case 2: default: /* note : default is impossible, since lhlCode into [0..3] */ @@ -185,27 +245,41 @@ break; case 3: lhSize = 3; + RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize = 3"); litSize = MEM_readLE24(istart) >> 4; break; } + RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF(expectedWriteSize < litSize, dstSize_tooSmall, ""); + ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 1); if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */ RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected, ""); - ZSTD_memcpy(dctx->litBuffer, istart+lhSize, litSize); + if (dctx->litBufferLocation == ZSTD_split) + { + ZSTD_memcpy(dctx->litBuffer, istart + lhSize, litSize - ZSTD_LITBUFFEREXTRASIZE); + ZSTD_memcpy(dctx->litExtraBuffer, istart + lhSize + litSize - ZSTD_LITBUFFEREXTRASIZE, ZSTD_LITBUFFEREXTRASIZE); + } + else + { + ZSTD_memcpy(dctx->litBuffer, istart + lhSize, litSize); + } dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; - ZSTD_memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); return lhSize+litSize; } /* direct reference into compressed stream */ dctx->litPtr = istart+lhSize; dctx->litSize = litSize; + dctx->litBufferEnd = dctx->litPtr + litSize; + dctx->litBufferLocation = ZSTD_not_in_dst; return lhSize+litSize; } case set_rle: { U32 const lhlCode = ((istart[0]) >> 2) & 3; size_t litSize, lhSize; + size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity); switch(lhlCode) { case 0: case 2: default: /* note : default is impossible, since lhlCode into [0..3] */ @@ -214,16 +288,28 @@ break; case 1: lhSize = 2; + RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 3"); litSize = MEM_readLE16(istart) >> 4; break; case 3: lhSize = 3; + RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 4"); litSize = MEM_readLE24(istart) >> 4; - RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4"); break; } + RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled"); RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, ""); - ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); + RETURN_ERROR_IF(expectedWriteSize < litSize, dstSize_tooSmall, ""); + ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 1); + if (dctx->litBufferLocation == ZSTD_split) + { + ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize - ZSTD_LITBUFFEREXTRASIZE); + ZSTD_memset(dctx->litExtraBuffer, istart[lhSize], ZSTD_LITBUFFEREXTRASIZE); + } + else + { + ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize); + } dctx->litPtr = dctx->litBuffer; dctx->litSize = litSize; return lhSize+1; @@ -343,7 +429,7 @@ }; /* ML_defaultDTable */ -static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, U32 baseValue, U32 nbAddBits) +static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, U32 baseValue, U8 nbAddBits) { void* ptr = dt; ZSTD_seqSymbol_header* const DTableH = (ZSTD_seqSymbol_header*)ptr; @@ -355,7 +441,7 @@ cell->nbBits = 0; cell->nextState = 0; assert(nbAddBits < 255); - cell->nbAdditionalBits = (BYTE)nbAddBits; + cell->nbAdditionalBits = nbAddBits; cell->baseValue = baseValue; } @@ -367,7 +453,7 @@ FORCE_INLINE_TEMPLATE void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, - const U32* baseValue, const U32* nbAdditionalBits, + const U32* baseValue, const U8* nbAdditionalBits, unsigned tableLog, void* wksp, size_t wkspSize) { ZSTD_seqSymbol* const tableDecode = dt+1; @@ -430,14 +516,15 @@ for (i = 8; i < n; i += 8) { MEM_write64(spread + pos + i, sv); } - pos += n; + assert(n>=0); + pos += (size_t)n; } } /* Now we spread those positions across the table. - * The benefit of doing it in two stages is that we avoid the the + * The benefit of doing it in two stages is that we avoid the * variable size inner loop, which caused lots of branch misses. * Now we can run through all the positions without any branch misses. - * We unroll the loop twice, since that is what emperically worked best. + * We unroll the loop twice, since that is what empirically worked best. */ { size_t position = 0; @@ -464,7 +551,7 @@ for (i=0; i<n; i++) { tableDecode[position].baseValue = s; position = (position + step) & tableMask; - while (position > highThreshold) position = (position + step) & tableMask; /* lowprob area */ + while (UNLIKELY(position > highThreshold)) position = (position + step) & tableMask; /* lowprob area */ } } assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ } @@ -475,10 +562,10 @@ for (u=0; u<tableSize; u++) { U32 const symbol = tableDecode[u].baseValue; U32 const nextState = symbolNext[symbol]++; - tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) ); + tableDecode[u].nbBits = (BYTE) (tableLog - ZSTD_highbit32(nextState) ); tableDecode[u].nextState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize); assert(nbAdditionalBits[symbol] < 255); - tableDecode[u].nbAdditionalBits = (BYTE)nbAdditionalBits[symbol]; + tableDecode[u].nbAdditionalBits = nbAdditionalBits[symbol]; tableDecode[u].baseValue = baseValue[symbol]; } } @@ -487,7 +574,7 @@ /* Avoids the FORCE_INLINE of the _body() function. */ static void ZSTD_buildFSETable_body_default(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, - const U32* baseValue, const U32* nbAdditionalBits, + const U32* baseValue, const U8* nbAdditionalBits, unsigned tableLog, void* wksp, size_t wkspSize) { ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue, @@ -495,9 +582,9 @@ } #if DYNAMIC_BMI2 -TARGET_ATTRIBUTE("bmi2") static void ZSTD_buildFSETable_body_bmi2(ZSTD_seqSymbol* dt, +BMI2_TARGET_ATTRIBUTE static void ZSTD_buildFSETable_body_bmi2(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, - const U32* baseValue, const U32* nbAdditionalBits, + const U32* baseValue, const U8* nbAdditionalBits, unsigned tableLog, void* wksp, size_t wkspSize) { ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue, @@ -507,7 +594,7 @@ void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, - const U32* baseValue, const U32* nbAdditionalBits, + const U32* baseValue, const U8* nbAdditionalBits, unsigned tableLog, void* wksp, size_t wkspSize, int bmi2) { #if DYNAMIC_BMI2 @@ -529,7 +616,7 @@ static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymbol** DTablePtr, symbolEncodingType_e type, unsigned max, U32 maxLog, const void* src, size_t srcSize, - const U32* baseValue, const U32* nbAdditionalBits, + const U32* baseValue, const U8* nbAdditionalBits, const ZSTD_seqSymbol* defaultTable, U32 flagRepeatTable, int ddictIsCold, int nbSeq, U32* wksp, size_t wkspSize, int bmi2) @@ -541,7 +628,7 @@ RETURN_ERROR_IF((*(const BYTE*)src) > max, corruption_detected, ""); { U32 const symbol = *(const BYTE*)src; U32 const baseline = baseValue[symbol]; - U32 const nbBits = nbAdditionalBits[symbol]; + U8 const nbBits = nbAdditionalBits[symbol]; ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits); } *DTablePtr = DTableSpace; @@ -620,7 +707,7 @@ LL_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq, dctx->workspace, sizeof(dctx->workspace), - dctx->bmi2); + ZSTD_DCtx_get_bmi2(dctx)); RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += llhSize; } @@ -632,7 +719,7 @@ OF_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq, dctx->workspace, sizeof(dctx->workspace), - dctx->bmi2); + ZSTD_DCtx_get_bmi2(dctx)); RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += ofhSize; } @@ -644,7 +731,7 @@ ML_defaultDTable, dctx->fseEntropy, dctx->ddictIsCold, nbSeq, dctx->workspace, sizeof(dctx->workspace), - dctx->bmi2); + ZSTD_DCtx_get_bmi2(dctx)); RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected, "ZSTD_buildSeqTable failed"); ip += mlhSize; } @@ -713,7 +800,7 @@ * - ZSTD_overlap_src_before_dst: The src and dst may overlap and may be any distance apart. * The src buffer must be before the dst buffer. */ -static void ZSTD_safecopy(BYTE* op, BYTE* const oend_w, BYTE const* ip, ptrdiff_t length, ZSTD_overlap_e ovtype) { +static void ZSTD_safecopy(BYTE* op, const BYTE* const oend_w, BYTE const* ip, ptrdiff_t length, ZSTD_overlap_e ovtype) { ptrdiff_t const diff = op - ip; BYTE* const oend = op + length; @@ -729,6 +816,7 @@ /* Copy 8 bytes and ensure the offset >= 8 when there can be overlap. */ assert(length >= 8); ZSTD_overlapCopy8(&op, &ip, diff); + length -= 8; assert(op - ip >= 8); assert(op <= oend); } @@ -743,12 +831,35 @@ assert(oend > oend_w); ZSTD_wildcopy(op, ip, oend_w - op, ovtype); ip += oend_w - op; - op = oend_w; + op += oend_w - op; } /* Handle the leftovers. */ while (op < oend) *op++ = *ip++; } +/* ZSTD_safecopyDstBeforeSrc(): + * This version allows overlap with dst before src, or handles the non-overlap case with dst after src + * Kept separate from more common ZSTD_safecopy case to avoid performance impact to the safecopy common case */ +static void ZSTD_safecopyDstBeforeSrc(BYTE* op, BYTE const* ip, ptrdiff_t length) { + ptrdiff_t const diff = op - ip; + BYTE* const oend = op + length; + + if (length < 8 || diff > -8) { + /* Handle short lengths, close overlaps, and dst not before src. */ + while (op < oend) *op++ = *ip++; + return; + } + + if (op <= oend - WILDCOPY_OVERLENGTH && diff < -WILDCOPY_VECLEN) { + ZSTD_wildcopy(op, ip, oend - WILDCOPY_OVERLENGTH - op, ZSTD_no_overlap); + ip += oend - WILDCOPY_OVERLENGTH - op; + op += oend - WILDCOPY_OVERLENGTH - op; + } + + /* Handle the leftovers. */ + while (op < oend) *op++ = *ip++; +} + /* ZSTD_execSequenceEnd(): * This version handles cases that are near the end of the output buffer. It requires * more careful checks to make sure there is no overflow. By separating out these hard @@ -759,9 +870,9 @@ */ FORCE_NOINLINE size_t ZSTD_execSequenceEnd(BYTE* op, - BYTE* const oend, seq_t sequence, - const BYTE** litPtr, const BYTE* const litLimit, - const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) + BYTE* const oend, seq_t sequence, + const BYTE** litPtr, const BYTE* const litLimit, + const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) { BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength; @@ -784,27 +895,76 @@ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { /* offset beyond prefix */ RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, ""); - match = dictEnd - (prefixStart-match); + match = dictEnd - (prefixStart - match); if (match + sequence.matchLength <= dictEnd) { ZSTD_memmove(oLitEnd, match, sequence.matchLength); return sequenceLength; } /* span extDict & currentPrefixSegment */ { size_t const length1 = dictEnd - match; - ZSTD_memmove(oLitEnd, match, length1); - op = oLitEnd + length1; - sequence.matchLength -= length1; - match = prefixStart; - } } + ZSTD_memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence.matchLength -= length1; + match = prefixStart; + } + } + ZSTD_safecopy(op, oend_w, match, sequence.matchLength, ZSTD_overlap_src_before_dst); + return sequenceLength; +} + +/* ZSTD_execSequenceEndSplitLitBuffer(): + * This version is intended to be used during instances where the litBuffer is still split. It is kept separate to avoid performance impact for the good case. + */ +FORCE_NOINLINE +size_t ZSTD_execSequenceEndSplitLitBuffer(BYTE* op, + BYTE* const oend, const BYTE* const oend_w, seq_t sequence, + const BYTE** litPtr, const BYTE* const litLimit, + const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) +{ + BYTE* const oLitEnd = op + sequence.litLength; + size_t const sequenceLength = sequence.litLength + sequence.matchLength; + const BYTE* const iLitEnd = *litPtr + sequence.litLength; + const BYTE* match = oLitEnd - sequence.offset; + + + /* bounds checks : careful of address space overflow in 32-bit mode */ + RETURN_ERROR_IF(sequenceLength > (size_t)(oend - op), dstSize_tooSmall, "last match must fit within dstBuffer"); + RETURN_ERROR_IF(sequence.litLength > (size_t)(litLimit - *litPtr), corruption_detected, "try to read beyond literal buffer"); + assert(op < op + sequenceLength); + assert(oLitEnd < op + sequenceLength); + + /* copy literals */ + RETURN_ERROR_IF(op > *litPtr && op < *litPtr + sequence.litLength, dstSize_tooSmall, "output should not catch up to and overwrite literal buffer"); + ZSTD_safecopyDstBeforeSrc(op, *litPtr, sequence.litLength); + op = oLitEnd; + *litPtr = iLitEnd; + + /* copy Match */ + if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { + /* offset beyond prefix */ + RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, ""); + match = dictEnd - (prefixStart - match); + if (match + sequence.matchLength <= dictEnd) { + ZSTD_memmove(oLitEnd, match, sequence.matchLength); + return sequenceLength; + } + /* span extDict & currentPrefixSegment */ + { size_t const length1 = dictEnd - match; + ZSTD_memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence.matchLength -= length1; + match = prefixStart; + } + } ZSTD_safecopy(op, oend_w, match, sequence.matchLength, ZSTD_overlap_src_before_dst); return sequenceLength; } HINT_INLINE size_t ZSTD_execSequence(BYTE* op, - BYTE* const oend, seq_t sequence, - const BYTE** litPtr, const BYTE* const litLimit, - const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) + BYTE* const oend, seq_t sequence, + const BYTE** litPtr, const BYTE* const litLimit, + const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) { BYTE* const oLitEnd = op + sequence.litLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength; @@ -815,6 +975,103 @@ assert(op != NULL /* Precondition */); assert(oend_w < oend /* No underflow */); + +#if defined(__aarch64__) + /* prefetch sequence starting from match that will be used for copy later */ + PREFETCH_L1(match); +#endif + /* Handle edge cases in a slow path: + * - Read beyond end of literals + * - Match end is within WILDCOPY_OVERLIMIT of oend + * - 32-bit mode and the match length overflows + */ + if (UNLIKELY( + iLitEnd > litLimit || + oMatchEnd > oend_w || + (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH))) + return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); + + /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */ + assert(op <= oLitEnd /* No overflow */); + assert(oLitEnd < oMatchEnd /* Non-zero match & no overflow */); + assert(oMatchEnd <= oend /* No underflow */); + assert(iLitEnd <= litLimit /* Literal length is in bounds */); + assert(oLitEnd <= oend_w /* Can wildcopy literals */); + assert(oMatchEnd <= oend_w /* Can wildcopy matches */); + + /* Copy Literals: + * Split out litLength <= 16 since it is nearly always true. +1.6% on gcc-9. + * We likely don't need the full 32-byte wildcopy. + */ + assert(WILDCOPY_OVERLENGTH >= 16); + ZSTD_copy16(op, (*litPtr)); + if (UNLIKELY(sequence.litLength > 16)) { + ZSTD_wildcopy(op + 16, (*litPtr) + 16, sequence.litLength - 16, ZSTD_no_overlap); + } + op = oLitEnd; + *litPtr = iLitEnd; /* update for next sequence */ + + /* Copy Match */ + if (sequence.offset > (size_t)(oLitEnd - prefixStart)) { + /* offset beyond prefix -> go into extDict */ + RETURN_ERROR_IF(UNLIKELY(sequence.offset > (size_t)(oLitEnd - virtualStart)), corruption_detected, ""); + match = dictEnd + (match - prefixStart); + if (match + sequence.matchLength <= dictEnd) { + ZSTD_memmove(oLitEnd, match, sequence.matchLength); + return sequenceLength; + } + /* span extDict & currentPrefixSegment */ + { size_t const length1 = dictEnd - match; + ZSTD_memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence.matchLength -= length1; + match = prefixStart; + } + } + /* Match within prefix of 1 or more bytes */ + assert(op <= oMatchEnd); + assert(oMatchEnd <= oend_w); + assert(match >= prefixStart); + assert(sequence.matchLength >= 1); + + /* Nearly all offsets are >= WILDCOPY_VECLEN bytes, which means we can use wildcopy + * without overlap checking. + */ + if (LIKELY(sequence.offset >= WILDCOPY_VECLEN)) { + /* We bet on a full wildcopy for matches, since we expect matches to be + * longer than literals (in general). In silesia, ~10% of matches are longer + * than 16 bytes. + */ + ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength, ZSTD_no_overlap); + return sequenceLength; + } + assert(sequence.offset < WILDCOPY_VECLEN); + + /* Copy 8 bytes and spread the offset to be >= 8. */ + ZSTD_overlapCopy8(&op, &match, sequence.offset); + + /* If the match length is > 8 bytes, then continue with the wildcopy. */ + if (sequence.matchLength > 8) { + assert(op < oMatchEnd); + ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8, ZSTD_overlap_src_before_dst); + } + return sequenceLength; +} + +HINT_INLINE +size_t ZSTD_execSequenceSplitLitBuffer(BYTE* op, + BYTE* const oend, const BYTE* const oend_w, seq_t sequence, + const BYTE** litPtr, const BYTE* const litLimit, + const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd) +{ + BYTE* const oLitEnd = op + sequence.litLength; + size_t const sequenceLength = sequence.litLength + sequence.matchLength; + BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ + const BYTE* const iLitEnd = *litPtr + sequence.litLength; + const BYTE* match = oLitEnd - sequence.offset; + + assert(op != NULL /* Precondition */); + assert(oend_w < oend /* No underflow */); /* Handle edge cases in a slow path: * - Read beyond end of literals * - Match end is within WILDCOPY_OVERLIMIT of oend @@ -824,7 +1081,7 @@ iLitEnd > litLimit || oMatchEnd > oend_w || (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH))) - return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); + return ZSTD_execSequenceEndSplitLitBuffer(op, oend, oend_w, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd); /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */ assert(op <= oLitEnd /* No overflow */); @@ -892,6 +1149,7 @@ return sequenceLength; } + static void ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt) { @@ -905,24 +1163,14 @@ } FORCE_INLINE_TEMPLATE void -ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD) +ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, U16 nextState, U32 nbBits) { - ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state]; - U32 const nbBits = DInfo.nbBits; size_t const lowBits = BIT_readBits(bitD, nbBits); - DStatePtr->state = DInfo.nextState + lowBits; -} - -FORCE_INLINE_TEMPLATE void -ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, ZSTD_seqSymbol const DInfo) -{ - U32 const nbBits = DInfo.nbBits; - size_t const lowBits = BIT_readBits(bitD, nbBits); - DStatePtr->state = DInfo.nextState + lowBits; + DStatePtr->state = nextState + lowBits; } /* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum - * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1) + * offset bits. But we can only read at most STREAM_ACCUMULATOR_MIN_32 * bits before reloading. This value is the maximum number of bytes we read * after reloading when we are decoding long offsets. */ @@ -937,102 +1185,113 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) { seq_t seq; - ZSTD_seqSymbol const llDInfo = seqState->stateLL.table[seqState->stateLL.state]; - ZSTD_seqSymbol const mlDInfo = seqState->stateML.table[seqState->stateML.state]; - ZSTD_seqSymbol const ofDInfo = seqState->stateOffb.table[seqState->stateOffb.state]; - U32 const llBase = llDInfo.baseValue; - U32 const mlBase = mlDInfo.baseValue; - U32 const ofBase = ofDInfo.baseValue; - BYTE const llBits = llDInfo.nbAdditionalBits; - BYTE const mlBits = mlDInfo.nbAdditionalBits; - BYTE const ofBits = ofDInfo.nbAdditionalBits; - BYTE const totalBits = llBits+mlBits+ofBits; - - /* sequence */ - { size_t offset; - if (ofBits > 1) { - ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); - ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); - assert(ofBits <= MaxOff); - if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { - U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed); - offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); - BIT_reloadDStream(&seqState->DStream); - if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); - assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */ - } else { - offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); - } - seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; - } else { - U32 const ll0 = (llBase == 0); - if (LIKELY((ofBits == 0))) { - if (LIKELY(!ll0)) - offset = seqState->prevOffset[0]; - else { - offset = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; - } - } else { - offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1); - { size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; - temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ - if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; - seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset = temp; - } } } - seq.offset = offset; - } - - seq.matchLength = mlBase; - if (mlBits > 0) - seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/); - - if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) - BIT_reloadDStream(&seqState->DStream); - if (MEM_64bits() && UNLIKELY(totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) - BIT_reloadDStream(&seqState->DStream); - /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ - ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - - seq.litLength = llBase; - if (llBits > 0) - seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/); - - if (MEM_32bits()) - BIT_reloadDStream(&seqState->DStream); - - DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", - (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); - - /* ANS state update - * gcc-9.0.0 does 2.5% worse with ZSTD_updateFseStateWithDInfo(). - * clang-9.2.0 does 7% worse with ZSTD_updateFseState(). - * Naturally it seems like ZSTD_updateFseStateWithDInfo() should be the - * better option, so it is the default for other compilers. But, if you - * measure that it is worse, please put up a pull request. + /* + * ZSTD_seqSymbol is a structure with a total of 64 bits wide. So it can be + * loaded in one operation and extracted its fields by simply shifting or + * bit-extracting on aarch64. + * GCC doesn't recognize this and generates more unnecessary ldr/ldrb/ldrh + * operations that cause performance drop. This can be avoided by using this + * ZSTD_memcpy hack. */ - { -#if defined(__GNUC__) && !defined(__clang__) - const int kUseUpdateFseState = 1; +#if defined(__aarch64__) && (defined(__GNUC__) && !defined(__clang__)) + ZSTD_seqSymbol llDInfoS, mlDInfoS, ofDInfoS; + ZSTD_seqSymbol* const llDInfo = &llDInfoS; + ZSTD_seqSymbol* const mlDInfo = &mlDInfoS; + ZSTD_seqSymbol* const ofDInfo = &ofDInfoS; + ZSTD_memcpy(llDInfo, seqState->stateLL.table + seqState->stateLL.state, sizeof(ZSTD_seqSymbol)); + ZSTD_memcpy(mlDInfo, seqState->stateML.table + seqState->stateML.state, sizeof(ZSTD_seqSymbol)); + ZSTD_memcpy(ofDInfo, seqState->stateOffb.table + seqState->stateOffb.state, sizeof(ZSTD_seqSymbol)); #else - const int kUseUpdateFseState = 0; + const ZSTD_seqSymbol* const llDInfo = seqState->stateLL.table + seqState->stateLL.state; + const ZSTD_seqSymbol* const mlDInfo = seqState->stateML.table + seqState->stateML.state; + const ZSTD_seqSymbol* const ofDInfo = seqState->stateOffb.table + seqState->stateOffb.state; #endif - if (kUseUpdateFseState) { - ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ - ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ - } else { - ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llDInfo); /* <= 9 bits */ - ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlDInfo); /* <= 9 bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ - ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofDInfo); /* <= 8 bits */ + seq.matchLength = mlDInfo->baseValue; + seq.litLength = llDInfo->baseValue; + { U32 const ofBase = ofDInfo->baseValue; + BYTE const llBits = llDInfo->nbAdditionalBits; + BYTE const mlBits = mlDInfo->nbAdditionalBits; + BYTE const ofBits = ofDInfo->nbAdditionalBits; + BYTE const totalBits = llBits+mlBits+ofBits; + + U16 const llNext = llDInfo->nextState; + U16 const mlNext = mlDInfo->nextState; + U16 const ofNext = ofDInfo->nextState; + U32 const llnbBits = llDInfo->nbBits; + U32 const mlnbBits = mlDInfo->nbBits; + U32 const ofnbBits = ofDInfo->nbBits; + + assert(llBits <= MaxLLBits); + assert(mlBits <= MaxMLBits); + assert(ofBits <= MaxOff); + /* + * As gcc has better branch and block analyzers, sometimes it is only + * valuable to mark likeliness for clang, it gives around 3-4% of + * performance. + */ + + /* sequence */ + { size_t offset; + if (ofBits > 1) { + ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); + ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); + ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 > LONG_OFFSETS_MAX_EXTRA_BITS_32); + ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 - LONG_OFFSETS_MAX_EXTRA_BITS_32 >= MaxMLBits); + if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) { + /* Always read extra bits, this keeps the logic simple, + * avoids branches, and avoids accidentally reading 0 bits. + */ + U32 const extraBits = LONG_OFFSETS_MAX_EXTRA_BITS_32; + offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + BIT_reloadDStream(&seqState->DStream); + offset += BIT_readBitsFast(&seqState->DStream, extraBits); + } else { + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + } + seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset; + } else { + U32 const ll0 = (llDInfo->baseValue == 0); + if (LIKELY((ofBits == 0))) { + offset = seqState->prevOffset[ll0]; + seqState->prevOffset[1] = seqState->prevOffset[!ll0]; + seqState->prevOffset[0] = offset; + } else { + offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1); + { size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; + temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ + if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; + seqState->prevOffset[1] = seqState->prevOffset[0]; + seqState->prevOffset[0] = offset = temp; + } } } + seq.offset = offset; } + + if (mlBits > 0) + seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/); + + if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) + BIT_reloadDStream(&seqState->DStream); + if (MEM_64bits() && UNLIKELY(totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog))) + BIT_reloadDStream(&seqState->DStream); + /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ + ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); + + if (llBits > 0) + seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/); + + if (MEM_32bits()) + BIT_reloadDStream(&seqState->DStream); + + DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u", + (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset); + + ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llNext, llnbBits); /* <= 9 bits */ + ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlNext, mlnbBits); /* <= 9 bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ + ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofNext, ofnbBits); /* <= 8 bits */ } return seq; @@ -1085,9 +1344,11 @@ #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG + + FORCE_INLINE_TEMPLATE size_t DONT_VECTORIZE -ZSTD_decompressSequences_body( ZSTD_DCtx* dctx, +ZSTD_decompressSequences_bodySplitLitBuffer( ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, const ZSTD_longOffset_e isLongOffset, @@ -1099,11 +1360,11 @@ BYTE* const oend = ostart + maxDstSize; BYTE* op = ostart; const BYTE* litPtr = dctx->litPtr; - const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* litBufferEnd = dctx->litBufferEnd; const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); const BYTE* const vBase = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); - DEBUGLOG(5, "ZSTD_decompressSequences_body"); + DEBUGLOG(5, "ZSTD_decompressSequences_bodySplitLitBuffer"); (void)frame; /* Regen sequences */ @@ -1124,55 +1385,237 @@ BIT_DStream_endOfBuffer < BIT_DStream_completed && BIT_DStream_completed < BIT_DStream_overflow); + /* decompress without overrunning litPtr begins */ + { + seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset); + /* Align the decompression loop to 32 + 16 bytes. + * + * zstd compiled with gcc-9 on an Intel i9-9900k shows 10% decompression + * speed swings based on the alignment of the decompression loop. This + * performance swing is caused by parts of the decompression loop falling + * out of the DSB. The entire decompression loop should fit in the DSB, + * when it can't we get much worse performance. You can measure if you've + * hit the good case or the bad case with this perf command for some + * compressed file test.zst: + * + * perf stat -e cycles -e instructions -e idq.all_dsb_cycles_any_uops \ + * -e idq.all_mite_cycles_any_uops -- ./zstd -tq test.zst + * + * If you see most cycles served out of the MITE you've hit the bad case. + * If you see most cycles served out of the DSB you've hit the good case. + * If it is pretty even then you may be in an okay case. + * + * This issue has been reproduced on the following CPUs: + * - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9 + * Use Instruments->Counters to get DSB/MITE cycles. + * I never got performance swings, but I was able to + * go from the good case of mostly DSB to half of the + * cycles served from MITE. + * - Coffeelake: Intel i9-9900k + * - Coffeelake: Intel i7-9700k + * + * I haven't been able to reproduce the instability or DSB misses on any + * of the following CPUS: + * - Haswell + * - Broadwell: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GH + * - Skylake + * + * Alignment is done for each of the three major decompression loops: + * - ZSTD_decompressSequences_bodySplitLitBuffer - presplit section of the literal buffer + * - ZSTD_decompressSequences_bodySplitLitBuffer - postsplit section of the literal buffer + * - ZSTD_decompressSequences_body + * Alignment choices are made to minimize large swings on bad cases and influence on performance + * from changes external to this code, rather than to overoptimize on the current commit. + * + * If you are seeing performance stability this script can help test. + * It tests on 4 commits in zstd where I saw performance change. + * + * https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4 + */ #if defined(__GNUC__) && defined(__x86_64__) - /* Align the decompression loop to 32 + 16 bytes. - * - * zstd compiled with gcc-9 on an Intel i9-9900k shows 10% decompression - * speed swings based on the alignment of the decompression loop. This - * performance swing is caused by parts of the decompression loop falling - * out of the DSB. The entire decompression loop should fit in the DSB, - * when it can't we get much worse performance. You can measure if you've - * hit the good case or the bad case with this perf command for some - * compressed file test.zst: - * - * perf stat -e cycles -e instructions -e idq.all_dsb_cycles_any_uops \ - * -e idq.all_mite_cycles_any_uops -- ./zstd -tq test.zst - * - * If you see most cycles served out of the MITE you've hit the bad case. - * If you see most cycles served out of the DSB you've hit the good case. - * If it is pretty even then you may be in an okay case. - * - * This issue has been reproduced on the following CPUs: - * - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9 - * Use Instruments->Counters to get DSB/MITE cycles. - * I never got performance swings, but I was able to - * go from the good case of mostly DSB to half of the - * cycles served from MITE. - * - Coffeelake: Intel i9-9900k - * - Coffeelake: Intel i7-9700k - * - * I haven't been able to reproduce the instability or DSB misses on any - * of the following CPUS: - * - Haswell - * - Broadwell: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GH - * - Skylake - * - * If you are seeing performance stability this script can help test. - * It tests on 4 commits in zstd where I saw performance change. - * - * https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4 - */ - __asm__(".p2align 6"); - __asm__("nop"); - __asm__(".p2align 5"); - __asm__("nop"); -# if __GNUC__ >= 9 - /* better for gcc-9 and gcc-10, worse for clang and gcc-8 */ - __asm__(".p2align 3"); -# else - __asm__(".p2align 4"); + __asm__(".p2align 6"); +# if __GNUC__ >= 7 + /* good for gcc-7, gcc-9, and gcc-11 */ + __asm__("nop"); + __asm__(".p2align 5"); + __asm__("nop"); + __asm__(".p2align 4"); +# if __GNUC__ == 8 || __GNUC__ == 10 + /* good for gcc-8 and gcc-10 */ + __asm__("nop"); + __asm__(".p2align 3"); +# endif # endif #endif + + /* Handle the initial state where litBuffer is currently split between dst and litExtraBuffer */ + for (; litPtr + sequence.litLength <= dctx->litBufferEnd; ) { + size_t const oneSeqSize = ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequence.litLength - WILDCOPY_OVERLENGTH, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd); +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase); +#endif + if (UNLIKELY(ZSTD_isError(oneSeqSize))) + return oneSeqSize; + DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); + op += oneSeqSize; + if (UNLIKELY(!--nbSeq)) + break; + BIT_reloadDStream(&(seqState.DStream)); + sequence = ZSTD_decodeSequence(&seqState, isLongOffset); + } + + /* If there are more sequences, they will need to read literals from litExtraBuffer; copy over the remainder from dst and update litPtr and litEnd */ + if (nbSeq > 0) { + const size_t leftoverLit = dctx->litBufferEnd - litPtr; + if (leftoverLit) + { + RETURN_ERROR_IF(leftoverLit > (size_t)(oend - op), dstSize_tooSmall, "remaining lit must fit within dstBuffer"); + ZSTD_safecopyDstBeforeSrc(op, litPtr, leftoverLit); + sequence.litLength -= leftoverLit; + op += leftoverLit; + } + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE; + dctx->litBufferLocation = ZSTD_not_in_dst; + { + size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd); +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase); +#endif + if (UNLIKELY(ZSTD_isError(oneSeqSize))) + return oneSeqSize; + DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); + op += oneSeqSize; + if (--nbSeq) + BIT_reloadDStream(&(seqState.DStream)); + } + } + } + + if (nbSeq > 0) /* there is remaining lit from extra buffer */ + { + +#if defined(__GNUC__) && defined(__x86_64__) + __asm__(".p2align 6"); + __asm__("nop"); +# if __GNUC__ != 7 + /* worse for gcc-7 better for gcc-8, gcc-9, and gcc-10 and clang */ + __asm__(".p2align 4"); + __asm__("nop"); + __asm__(".p2align 3"); +# elif __GNUC__ >= 11 + __asm__(".p2align 3"); +# else + __asm__(".p2align 5"); + __asm__("nop"); + __asm__(".p2align 3"); +# endif +#endif + + for (; ; ) { + seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset); + size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd); +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase); +#endif + if (UNLIKELY(ZSTD_isError(oneSeqSize))) + return oneSeqSize; + DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize); + op += oneSeqSize; + if (UNLIKELY(!--nbSeq)) + break; + BIT_reloadDStream(&(seqState.DStream)); + } + } + + /* check if reached exact end */ + DEBUGLOG(5, "ZSTD_decompressSequences_bodySplitLitBuffer: after decode loop, remaining nbSeq : %i", nbSeq); + RETURN_ERROR_IF(nbSeq, corruption_detected, ""); + RETURN_ERROR_IF(BIT_reloadDStream(&seqState.DStream) < BIT_DStream_completed, corruption_detected, ""); + /* save reps for next block */ + { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); } + } + + /* last literal segment */ + if (dctx->litBufferLocation == ZSTD_split) /* split hasn't been reached yet, first get dst then copy litExtraBuffer */ + { + size_t const lastLLSize = litBufferEnd - litPtr; + RETURN_ERROR_IF(lastLLSize > (size_t)(oend - op), dstSize_tooSmall, ""); + if (op != NULL) { + ZSTD_memmove(op, litPtr, lastLLSize); + op += lastLLSize; + } + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE; + dctx->litBufferLocation = ZSTD_not_in_dst; + } + { size_t const lastLLSize = litBufferEnd - litPtr; + RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, ""); + if (op != NULL) { + ZSTD_memcpy(op, litPtr, lastLLSize); + op += lastLLSize; + } + } + + return op-ostart; +} + +FORCE_INLINE_TEMPLATE size_t +DONT_VECTORIZE +ZSTD_decompressSequences_body(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset, + const int frame) +{ + const BYTE* ip = (const BYTE*)seqStart; + const BYTE* const iend = ip + seqSize; + BYTE* const ostart = (BYTE*)dst; + BYTE* const oend = dctx->litBufferLocation == ZSTD_not_in_dst ? ostart + maxDstSize : dctx->litBuffer; + BYTE* op = ostart; + const BYTE* litPtr = dctx->litPtr; + const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* const prefixStart = (const BYTE*)(dctx->prefixStart); + const BYTE* const vBase = (const BYTE*)(dctx->virtualStart); + const BYTE* const dictEnd = (const BYTE*)(dctx->dictEnd); + DEBUGLOG(5, "ZSTD_decompressSequences_body: nbSeq = %d", nbSeq); + (void)frame; + + /* Regen sequences */ + if (nbSeq) { + seqState_t seqState; + dctx->fseEntropy = 1; + { U32 i; for (i = 0; i < ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; } + RETURN_ERROR_IF( + ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend - ip)), + corruption_detected, ""); + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + assert(dst != NULL); + + ZSTD_STATIC_ASSERT( + BIT_DStream_unfinished < BIT_DStream_completed && + BIT_DStream_endOfBuffer < BIT_DStream_completed && + BIT_DStream_completed < BIT_DStream_overflow); + +#if defined(__GNUC__) && defined(__x86_64__) + __asm__(".p2align 6"); + __asm__("nop"); +# if __GNUC__ >= 7 + __asm__(".p2align 5"); + __asm__("nop"); + __asm__(".p2align 3"); +# else + __asm__(".p2align 4"); + __asm__("nop"); + __asm__(".p2align 3"); +# endif +#endif + for ( ; ; ) { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset); size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd); @@ -1218,6 +1661,16 @@ { return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } + +static size_t +ZSTD_decompressSequencesSplitLitBuffer_default(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset, + const int frame) +{ + return ZSTD_decompressSequences_bodySplitLitBuffer(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); +} #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT @@ -1250,10 +1703,10 @@ const BYTE* ip = (const BYTE*)seqStart; const BYTE* const iend = ip + seqSize; BYTE* const ostart = (BYTE*)dst; - BYTE* const oend = ostart + maxDstSize; + BYTE* const oend = dctx->litBufferLocation == ZSTD_in_dst ? dctx->litBuffer : ostart + maxDstSize; BYTE* op = ostart; const BYTE* litPtr = dctx->litPtr; - const BYTE* const litEnd = litPtr + dctx->litSize; + const BYTE* litBufferEnd = dctx->litBufferEnd; const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart); const BYTE* const dictStart = (const BYTE*) (dctx->virtualStart); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); @@ -1289,32 +1742,94 @@ } RETURN_ERROR_IF(seqNb<seqAdvance, corruption_detected, ""); - /* decode and decompress */ - for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) { - seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset); - size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd); -#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) - assert(!ZSTD_isError(oneSeqSize)); - if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart); -#endif - if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + /* decompress without stomping litBuffer */ + for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb < nbSeq); seqNb++) { + seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset); + size_t oneSeqSize; - prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd); - sequences[seqNb & STORED_SEQS_MASK] = sequence; - op += oneSeqSize; + if (dctx->litBufferLocation == ZSTD_split && litPtr + sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK].litLength > dctx->litBufferEnd) + { + /* lit buffer is reaching split point, empty out the first buffer and transition to litExtraBuffer */ + const size_t leftoverLit = dctx->litBufferEnd - litPtr; + if (leftoverLit) + { + RETURN_ERROR_IF(leftoverLit > (size_t)(oend - op), dstSize_tooSmall, "remaining lit must fit within dstBuffer"); + ZSTD_safecopyDstBeforeSrc(op, litPtr, leftoverLit); + sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK].litLength -= leftoverLit; + op += leftoverLit; + } + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE; + dctx->litBufferLocation = ZSTD_not_in_dst; + oneSeqSize = ZSTD_execSequence(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd); +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart); +#endif + if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + + prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd); + sequences[seqNb & STORED_SEQS_MASK] = sequence; + op += oneSeqSize; + } + else + { + /* lit buffer is either wholly contained in first or second split, or not split at all*/ + oneSeqSize = dctx->litBufferLocation == ZSTD_split ? + ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK].litLength - WILDCOPY_OVERLENGTH, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd) : + ZSTD_execSequence(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd); +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart); +#endif + if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + + prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd); + sequences[seqNb & STORED_SEQS_MASK] = sequence; + op += oneSeqSize; + } } RETURN_ERROR_IF(seqNb<nbSeq, corruption_detected, ""); /* finish queue */ seqNb -= seqAdvance; for ( ; seqNb<nbSeq ; seqNb++) { - size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[seqNb&STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd); + seq_t *sequence = &(sequences[seqNb&STORED_SEQS_MASK]); + if (dctx->litBufferLocation == ZSTD_split && litPtr + sequence->litLength > dctx->litBufferEnd) + { + const size_t leftoverLit = dctx->litBufferEnd - litPtr; + if (leftoverLit) + { + RETURN_ERROR_IF(leftoverLit > (size_t)(oend - op), dstSize_tooSmall, "remaining lit must fit within dstBuffer"); + ZSTD_safecopyDstBeforeSrc(op, litPtr, leftoverLit); + sequence->litLength -= leftoverLit; + op += leftoverLit; + } + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE; + dctx->litBufferLocation = ZSTD_not_in_dst; + { + size_t const oneSeqSize = ZSTD_execSequence(op, oend, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd); #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) - assert(!ZSTD_isError(oneSeqSize)); - if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart); + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart); #endif - if (ZSTD_isError(oneSeqSize)) return oneSeqSize; - op += oneSeqSize; + if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + op += oneSeqSize; + } + } + else + { + size_t const oneSeqSize = dctx->litBufferLocation == ZSTD_split ? + ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequence->litLength - WILDCOPY_OVERLENGTH, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd) : + ZSTD_execSequence(op, oend, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd); +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE) + assert(!ZSTD_isError(oneSeqSize)); + if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart); +#endif + if (ZSTD_isError(oneSeqSize)) return oneSeqSize; + op += oneSeqSize; + } } /* save reps for next block */ @@ -1322,10 +1837,21 @@ } /* last literal segment */ - { size_t const lastLLSize = litEnd - litPtr; + if (dctx->litBufferLocation == ZSTD_split) /* first deplete literal buffer in dst, then copy litExtraBuffer */ + { + size_t const lastLLSize = litBufferEnd - litPtr; + RETURN_ERROR_IF(lastLLSize > (size_t)(oend - op), dstSize_tooSmall, ""); + if (op != NULL) { + ZSTD_memmove(op, litPtr, lastLLSize); + op += lastLLSize; + } + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE; + } + { size_t const lastLLSize = litBufferEnd - litPtr; RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, ""); if (op != NULL) { - ZSTD_memcpy(op, litPtr, lastLLSize); + ZSTD_memmove(op, litPtr, lastLLSize); op += lastLLSize; } } @@ -1349,7 +1875,7 @@ #if DYNAMIC_BMI2 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG -static TARGET_ATTRIBUTE("bmi2") size_t +static BMI2_TARGET_ATTRIBUTE size_t DONT_VECTORIZE ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, @@ -1359,10 +1885,20 @@ { return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } +static BMI2_TARGET_ATTRIBUTE size_t +DONT_VECTORIZE +ZSTD_decompressSequencesSplitLitBuffer_bmi2(ZSTD_DCtx* dctx, + void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset, + const int frame) +{ + return ZSTD_decompressSequences_bodySplitLitBuffer(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); +} #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT -static TARGET_ATTRIBUTE("bmi2") size_t +static BMI2_TARGET_ATTRIBUTE size_t ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, const void* seqStart, size_t seqSize, int nbSeq, @@ -1391,11 +1927,25 @@ { DEBUGLOG(5, "ZSTD_decompressSequences"); #if DYNAMIC_BMI2 - if (dctx->bmi2) { + if (ZSTD_DCtx_get_bmi2(dctx)) { return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif - return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); + return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); +} +static size_t +ZSTD_decompressSequencesSplitLitBuffer(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize, + const void* seqStart, size_t seqSize, int nbSeq, + const ZSTD_longOffset_e isLongOffset, + const int frame) +{ + DEBUGLOG(5, "ZSTD_decompressSequencesSplitLitBuffer"); +#if DYNAMIC_BMI2 + if (ZSTD_DCtx_get_bmi2(dctx)) { + return ZSTD_decompressSequencesSplitLitBuffer_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); + } +#endif + return ZSTD_decompressSequencesSplitLitBuffer_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */ @@ -1415,7 +1965,7 @@ { DEBUGLOG(5, "ZSTD_decompressSequencesLong"); #if DYNAMIC_BMI2 - if (dctx->bmi2) { + if (ZSTD_DCtx_get_bmi2(dctx)) { return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame); } #endif @@ -1424,55 +1974,101 @@ #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */ +/** + * @returns The total size of the history referenceable by zstd, including + * both the prefix and the extDict. At @p op any offset larger than this + * is invalid. + */ +static size_t ZSTD_totalHistorySize(BYTE* op, BYTE const* virtualStart) +{ + return (size_t)(op - virtualStart); +} -#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ - !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) -/* ZSTD_getLongOffsetsShare() : +typedef struct { + unsigned longOffsetShare; + unsigned maxNbAdditionalBits; +} ZSTD_OffsetInfo; + +/* ZSTD_getOffsetInfo() : * condition : offTable must be valid * @return : "share" of long offsets (arbitrarily defined as > (1<<23)) - * compared to maximum possible of (1<<OffFSELog) */ -static unsigned -ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable) + * compared to maximum possible of (1<<OffFSELog), + * as well as the maximum number additional bits required. + */ +static ZSTD_OffsetInfo +ZSTD_getOffsetInfo(const ZSTD_seqSymbol* offTable, int nbSeq) { - const void* ptr = offTable; - U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog; - const ZSTD_seqSymbol* table = offTable + 1; - U32 const max = 1 << tableLog; - U32 u, total = 0; - DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog); + ZSTD_OffsetInfo info = {0, 0}; + /* If nbSeq == 0, then the offTable is uninitialized, but we have + * no sequences, so both values should be 0. + */ + if (nbSeq != 0) { + const void* ptr = offTable; + U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog; + const ZSTD_seqSymbol* table = offTable + 1; + U32 const max = 1 << tableLog; + U32 u; + DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog); - assert(max <= (1 << OffFSELog)); /* max not too large */ - for (u=0; u<max; u++) { - if (table[u].nbAdditionalBits > 22) total += 1; + assert(max <= (1 << OffFSELog)); /* max not too large */ + for (u=0; u<max; u++) { + info.maxNbAdditionalBits = MAX(info.maxNbAdditionalBits, table[u].nbAdditionalBits); + if (table[u].nbAdditionalBits > 22) info.longOffsetShare += 1; + } + + assert(tableLog <= OffFSELog); + info.longOffsetShare <<= (OffFSELog - tableLog); /* scale to OffFSELog */ } - assert(tableLog <= OffFSELog); - total <<= (OffFSELog - tableLog); /* scale to OffFSELog */ - - return total; + return info; } -#endif + +/** + * @returns The maximum offset we can decode in one read of our bitstream, without + * reloading more bits in the middle of the offset bits read. Any offsets larger + * than this must use the long offset decoder. + */ +static size_t ZSTD_maxShortOffset(void) +{ + if (MEM_64bits()) { + /* We can decode any offset without reloading bits. + * This might change if the max window size grows. + */ + ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31); + return (size_t)-1; + } else { + /* The maximum offBase is (1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1. + * This offBase would require STREAM_ACCUMULATOR_MIN extra bits. + * Then we have to subtract ZSTD_REP_NUM to get the maximum possible offset. + */ + size_t const maxOffbase = ((size_t)1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1; + size_t const maxOffset = maxOffbase - ZSTD_REP_NUM; + assert(ZSTD_highbit32((U32)maxOffbase) == STREAM_ACCUMULATOR_MIN); + return maxOffset; + } +} size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, - const void* src, size_t srcSize, const int frame) + const void* src, size_t srcSize, const int frame, const streaming_operation streaming) { /* blockType == blockCompressed */ const BYTE* ip = (const BYTE*)src; - /* isLongOffset must be true if there are long offsets. - * Offsets are long if they are larger than 2^STREAM_ACCUMULATOR_MIN. - * We don't expect that to be the case in 64-bit mode. - * In block mode, window size is not known, so we have to be conservative. - * (note: but it could be evaluated from current-lowLimit) - */ - ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || (dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN)))); DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize); - RETURN_ERROR_IF(srcSize >= ZSTD_BLOCKSIZE_MAX, srcSize_wrong, ""); + /* Note : the wording of the specification + * allows compressed block to be sized exactly ZSTD_BLOCKSIZE_MAX. + * This generally does not happen, as it makes little sense, + * since an uncompressed block would feature same size and have no decompression cost. + * Also, note that decoder from reference libzstd before < v1.5.4 + * would consider this edge case as an error. + * As a consequence, avoid generating compressed blocks of size ZSTD_BLOCKSIZE_MAX + * for broader compatibility with the deployed ecosystem of zstd decoders */ + RETURN_ERROR_IF(srcSize > ZSTD_BLOCKSIZE_MAX, srcSize_wrong, ""); /* Decode literals section */ - { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); - DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : %u", (U32)litCSize); + { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize, dst, dstCapacity, streaming); + DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : cSize=%u, nbLiterals=%zu", (U32)litCSize, dctx->litSize); if (ZSTD_isError(litCSize)) return litCSize; ip += litCSize; srcSize -= litCSize; @@ -1480,6 +2076,23 @@ /* Build Decoding Tables */ { + /* Compute the maximum block size, which must also work when !frame and fParams are unset. + * Additionally, take the min with dstCapacity to ensure that the totalHistorySize fits in a size_t. + */ + size_t const blockSizeMax = MIN(dstCapacity, (frame ? dctx->fParams.blockSizeMax : ZSTD_BLOCKSIZE_MAX)); + size_t const totalHistorySize = ZSTD_totalHistorySize((BYTE*)dst + blockSizeMax, (BYTE const*)dctx->virtualStart); + /* isLongOffset must be true if there are long offsets. + * Offsets are long if they are larger than ZSTD_maxShortOffset(). + * We don't expect that to be the case in 64-bit mode. + * + * We check here to see if our history is large enough to allow long offsets. + * If it isn't, then we can't possible have (valid) long offsets. If the offset + * is invalid, then it is okay to read it incorrectly. + * + * If isLongOffsets is true, then we will later check our decoding table to see + * if it is even possible to generate long offsets. + */ + ZSTD_longOffset_e isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (totalHistorySize > ZSTD_maxShortOffset())); /* These macros control at build-time which decompressor implementation * we use. If neither is defined, we do some inspection and dispatch at * runtime. @@ -1487,6 +2100,11 @@ #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) int usePrefetchDecoder = dctx->ddictIsCold; +#else + /* Set to 1 to avoid computing offset info if we don't need to. + * Otherwise this value is ignored. + */ + int usePrefetchDecoder = 1; #endif int nbSeq; size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize); @@ -1494,32 +2112,49 @@ ip += seqHSize; srcSize -= seqHSize; - RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF((dst == NULL || dstCapacity == 0) && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF(MEM_64bits() && sizeof(size_t) == sizeof(void*) && (size_t)(-1) - (size_t)dst < (size_t)(1 << 20), dstSize_tooSmall, + "invalid dst"); -#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ - !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) - if ( !usePrefetchDecoder - && (!frame || (dctx->fParams.windowSize > (1<<24))) - && (nbSeq>ADVANCED_SEQS) ) { /* could probably use a larger nbSeq limit */ - U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr); - U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */ - usePrefetchDecoder = (shareLongOffsets >= minShare); + /* If we could potentially have long offsets, or we might want to use the prefetch decoder, + * compute information about the share of long offsets, and the maximum nbAdditionalBits. + * NOTE: could probably use a larger nbSeq limit + */ + if (isLongOffset || (!usePrefetchDecoder && (totalHistorySize > (1u << 24)) && (nbSeq > 8))) { + ZSTD_OffsetInfo const info = ZSTD_getOffsetInfo(dctx->OFTptr, nbSeq); + if (isLongOffset && info.maxNbAdditionalBits <= STREAM_ACCUMULATOR_MIN) { + /* If isLongOffset, but the maximum number of additional bits that we see in our table is small + * enough, then we know it is impossible to have too long an offset in this block, so we can + * use the regular offset decoder. + */ + isLongOffset = ZSTD_lo_isRegularOffset; + } + if (!usePrefetchDecoder) { + U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */ + usePrefetchDecoder = (info.longOffsetShare >= minShare); + } } -#endif dctx->ddictIsCold = 0; #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) - if (usePrefetchDecoder) + if (usePrefetchDecoder) { +#else + (void)usePrefetchDecoder; + { #endif #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif + } #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG /* else */ - return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); + if (dctx->litBufferLocation == ZSTD_split) + return ZSTD_decompressSequencesSplitLitBuffer(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); + else + return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame); #endif } } @@ -1536,13 +2171,22 @@ } +size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + size_t dSize; + ZSTD_checkContinuity(dctx, dst, dstCapacity); + dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0, not_streaming); + dctx->previousDstEnd = (char*)dst + dSize; + return dSize; +} + + +/* NOTE: Must just wrap ZSTD_decompressBlock_deprecated() */ size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { - size_t dSize; - ZSTD_checkContinuity(dctx, dst, dstCapacity); - dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0); - dctx->previousDstEnd = (char*)dst + dSize; - return dSize; + return ZSTD_decompressBlock_deprecated(dctx, dst, dstCapacity, src, srcSize); }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h index 049a0cd..9d13188 100644 --- a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h +++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -33,6 +33,12 @@ */ + /* Streaming state is used to inform allocation of the literal buffer */ +typedef enum { + not_streaming = 0, + is_streaming = 1 +} streaming_operation; + /* ZSTD_decompressBlock_internal() : * decompress block, starting at `src`, * into destination buffer `dst`. @@ -41,7 +47,7 @@ */ size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, - const void* src, size_t srcSize, const int frame); + const void* src, size_t srcSize, const int frame, const streaming_operation streaming); /* ZSTD_buildFSETable() : * generate FSE decoding table for one symbol (ll, ml or off) @@ -54,9 +60,14 @@ */ void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, const short* normalizedCounter, unsigned maxSymbolValue, - const U32* baseValue, const U32* nbAdditionalBits, + const U32* baseValue, const U8* nbAdditionalBits, unsigned tableLog, void* wksp, size_t wkspSize, int bmi2); +/* Internal definition of ZSTD_decompressBlock() to avoid deprecation warnings. */ +size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + #endif /* ZSTD_DEC_BLOCK_H */
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h index ebda0c9..c2ec5d9 100644 --- a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h +++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,7 +20,7 @@ * Dependencies *********************************************************/ #include "../common/mem.h" /* BYTE, U16, U32 */ -#include "../common/zstd_internal.h" /* ZSTD_seqSymbol */ +#include "../common/zstd_internal.h" /* constants : MaxLL, MaxML, MaxOff, LLFSELog, etc. */ @@ -40,7 +40,7 @@ 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD }; -static UNUSED_ATTR const U32 OF_bits[MaxOff+1] = { +static UNUSED_ATTR const U8 OF_bits[MaxOff+1] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, @@ -75,12 +75,13 @@ #define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE (sizeof(S16) * (MaxSeq + 1) + (1u << MaxFSELog) + sizeof(U64)) #define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 ((ZSTD_BUILD_FSE_TABLE_WKSP_SIZE + sizeof(U32) - 1) / sizeof(U32)) +#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12 typedef struct { ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)]; /* Note : Space reserved for FSE Tables */ ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)]; /* is also used as temporary workspace while building hufTable during DDict creation */ ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)]; /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */ - HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ + HUF_DTable hufTable[HUF_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; /* can accommodate HUF_decompress4X */ U32 rep[ZSTD_REP_NUM]; U32 workspace[ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32]; } ZSTD_entropyDTables_t; @@ -106,6 +107,22 @@ size_t ddictPtrCount; } ZSTD_DDictHashSet; +#ifndef ZSTD_DECODER_INTERNAL_BUFFER +# define ZSTD_DECODER_INTERNAL_BUFFER (1 << 16) +#endif + +#define ZSTD_LBMIN 64 +#define ZSTD_LBMAX (128 << 10) + +/* extra buffer, compensates when dst is not large enough to store litBuffer */ +#define ZSTD_LITBUFFEREXTRASIZE BOUNDED(ZSTD_LBMIN, ZSTD_DECODER_INTERNAL_BUFFER, ZSTD_LBMAX) + +typedef enum { + ZSTD_not_in_dst = 0, /* Stored entirely within litExtraBuffer */ + ZSTD_in_dst = 1, /* Stored entirely within dst (in memory after current output write) */ + ZSTD_split = 2 /* Split between litExtraBuffer and dst */ +} ZSTD_litLocation_e; + struct ZSTD_DCtx_s { const ZSTD_seqSymbol* LLTptr; @@ -136,7 +153,9 @@ size_t litSize; size_t rleSize; size_t staticSize; +#if DYNAMIC_BMI2 != 0 int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */ +#endif /* dictionary */ ZSTD_DDict* ddictLocal; @@ -146,6 +165,7 @@ ZSTD_dictUses_e dictUses; ZSTD_DDictHashSet* ddictSet; /* Hash set for multiple ddicts */ ZSTD_refMultipleDDicts_e refMultipleDDicts; /* User specified: if == 1, will allow references to multiple DDicts. Default == 0 (disabled) */ + int disableHufAsm; /* streaming */ ZSTD_dStreamStage streamStage; @@ -158,16 +178,21 @@ size_t outStart; size_t outEnd; size_t lhSize; +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) void* legacyContext; U32 previousLegacyVersion; U32 legacyVersion; +#endif U32 hostageByte; int noForwardProgress; ZSTD_bufferMode_e outBufferMode; ZSTD_outBuffer expectedOutBuffer; /* workspace */ - BYTE litBuffer[ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH]; + BYTE* litBuffer; + const BYTE* litBufferEnd; + ZSTD_litLocation_e litBufferLocation; + BYTE litExtraBuffer[ZSTD_LITBUFFEREXTRASIZE + WILDCOPY_OVERLENGTH]; /* literal buffer can be split between storage within dst and within this scratch buffer */ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; size_t oversizedDuration; @@ -183,6 +208,14 @@ #endif }; /* typedef'd to ZSTD_DCtx within "zstd.h" */ +MEM_STATIC int ZSTD_DCtx_get_bmi2(const struct ZSTD_DCtx_s *dctx) { +#if DYNAMIC_BMI2 != 0 + return dctx->bmi2; +#else + (void)dctx; + return 0; +#endif +} /*-******************************************************* * Shared internal functions
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff.h b/Utilities/cmzstd/lib/deprecated/zbuff.h index b83ea0f..a968245 100644 --- a/Utilities/cmzstd/lib/deprecated/zbuff.h +++ b/Utilities/cmzstd/lib/deprecated/zbuff.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_common.c b/Utilities/cmzstd/lib/deprecated/zbuff_common.c index e7d01a0..5a2f2db 100644 --- a/Utilities/cmzstd/lib/deprecated/zbuff_common.c +++ b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c index 2e72267..1d86821 100644 --- a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c +++ b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -15,6 +15,7 @@ ***************************************/ #define ZBUFF_STATIC_LINKING_ONLY #include "zbuff.h" +#include "../common/error_private.h" /*-*********************************************************** @@ -73,13 +74,32 @@ ZSTD_parameters params, unsigned long long pledgedSrcSize) { if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* preserve "0 == unknown" behavior */ - return ZSTD_initCStream_advanced(zbc, dict, dictSize, params, pledgedSrcSize); -} + FORWARD_IF_ERROR(ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setPledgedSrcSize(zbc, pledgedSrcSize), ""); + FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_windowLog, params.cParams.windowLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_hashLog, params.cParams.hashLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_chainLog, params.cParams.chainLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_searchLog, params.cParams.searchLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_minMatch, params.cParams.minMatch), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_targetLength, params.cParams.targetLength), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_strategy, params.cParams.strategy), ""); + + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_contentSizeFlag, params.fParams.contentSizeFlag), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_checksumFlag, params.fParams.checksumFlag), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_dictIDFlag, params.fParams.noDictIDFlag), ""); + + FORWARD_IF_ERROR(ZSTD_CCtx_loadDictionary(zbc, dict, dictSize), ""); + return 0; +} size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, int compressionLevel) { - return ZSTD_initCStream_usingDict(zbc, dict, dictSize, compressionLevel); + FORWARD_IF_ERROR(ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_compressionLevel, compressionLevel), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_loadDictionary(zbc, dict, dictSize), ""); + return 0; } size_t ZBUFF_compressInit(ZBUFF_CCtx* zbc, int compressionLevel)
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c index d73c0f3..12a66af 100644 --- a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c +++ b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -13,6 +13,8 @@ /* ************************************* * Dependencies ***************************************/ +#define ZSTD_DISABLE_DEPRECATE_WARNINGS /* suppress warning on ZSTD_initDStream_usingDict */ +#include "../zstd.h" /* ZSTD_CStream, ZSTD_DStream, ZSTDLIB_API */ #define ZBUFF_STATIC_LINKING_ONLY #include "zbuff.h"
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.c b/Utilities/cmzstd/lib/dictBuilder/cover.c index 8364444..9e5e7d5 100644 --- a/Utilities/cmzstd/lib/dictBuilder/cover.c +++ b/Utilities/cmzstd/lib/dictBuilder/cover.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -34,12 +34,20 @@ #include "../common/pool.h" #include "../common/threading.h" #include "../common/zstd_internal.h" /* includes zstd.h */ +#include "../common/bits.h" /* ZSTD_highbit32 */ #include "../zdict.h" #include "cover.h" /*-************************************* * Constants ***************************************/ +/** +* There are 32bit indexes used to ref samples, so limit samples size to 4GB +* on 64bit builds. +* For 32bit builds we choose 1 GB. +* Most 32bit platforms have 2GB user-mode addressable space and we allocate a large +* contiguous buffer, so 1GB is already a high limit. +*/ #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB)) #define COVER_DEFAULT_SPLITPOINT 1.0 @@ -47,7 +55,7 @@ * Console display ***************************************/ #ifndef LOCALDISPLAYLEVEL -static int g_displayLevel = 2; +static int g_displayLevel = 0; #endif #undef DISPLAY #define DISPLAY(...) \ @@ -534,7 +542,7 @@ /** * Prepare a context for dictionary building. - * The context is only dependent on the parameter `d` and can used multiple + * The context is only dependent on the parameter `d` and can be used multiple * times. * Returns 0 on success or error code on error. * The context must be destroyed with `COVER_ctx_destroy()`. @@ -639,7 +647,7 @@ void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel) { - const double ratio = (double)nbDmers / maxDictSize; + const double ratio = (double)nbDmers / (double)maxDictSize; if (ratio >= 10) { return; } @@ -735,7 +743,7 @@ COVER_map_t activeDmers; parameters.splitPoint = 1.0; /* Initialize global data */ - g_displayLevel = parameters.zParams.notificationLevel; + g_displayLevel = (int)parameters.zParams.notificationLevel; /* Checks */ if (!COVER_checkParameters(parameters, dictBufferCapacity)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); @@ -943,9 +951,17 @@ } } +static COVER_dictSelection_t setDictSelection(BYTE* buf, size_t s, size_t csz) +{ + COVER_dictSelection_t ds; + ds.dictContent = buf; + ds.dictSize = s; + ds.totalCompressedSize = csz; + return ds; +} + COVER_dictSelection_t COVER_dictSelectionError(size_t error) { - COVER_dictSelection_t selection = { NULL, 0, error }; - return selection; + return setDictSelection(NULL, 0, error); } unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection) { @@ -998,9 +1014,8 @@ } if (params.shrinkDict == 0) { - COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize }; free(candidateDictBuffer); - return selection; + return setDictSelection(largestDictbuffer, dictContentSize, totalCompressedSize); } largestDict = dictContentSize; @@ -1032,20 +1047,16 @@ return COVER_dictSelectionError(totalCompressedSize); } - if (totalCompressedSize <= largestCompressed * regressionTolerance) { - COVER_dictSelection_t selection = { candidateDictBuffer, dictContentSize, totalCompressedSize }; + if ((double)totalCompressedSize <= (double)largestCompressed * regressionTolerance) { free(largestDictbuffer); - return selection; + return setDictSelection( candidateDictBuffer, dictContentSize, totalCompressedSize ); } dictContentSize *= 2; } dictContentSize = largestDict; totalCompressedSize = largestCompressed; - { - COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize }; - free(candidateDictBuffer); - return selection; - } + free(candidateDictBuffer); + return setDictSelection( largestDictbuffer, dictContentSize, totalCompressedSize ); } /**
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.h b/Utilities/cmzstd/lib/dictBuilder/cover.h index 1aacddd..252624b 100644 --- a/Utilities/cmzstd/lib/dictBuilder/cover.h +++ b/Utilities/cmzstd/lib/dictBuilder/cover.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/dictBuilder/fastcover.c b/Utilities/cmzstd/lib/dictBuilder/fastcover.c index ed789f9..46bba01 100644 --- a/Utilities/cmzstd/lib/dictBuilder/fastcover.c +++ b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -32,6 +32,13 @@ /*-************************************* * Constants ***************************************/ +/** +* There are 32bit indexes used to ref samples, so limit samples size to 4GB +* on 64bit builds. +* For 32bit builds we choose 1 GB. +* Most 32bit platforms have 2GB user-mode addressable space and we allocate a large +* contiguous buffer, so 1GB is already a high limit. +*/ #define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB)) #define FASTCOVER_MAX_F 31 #define FASTCOVER_MAX_ACCEL 10 @@ -44,7 +51,7 @@ * Console display ***************************************/ #ifndef LOCALDISPLAYLEVEL -static int g_displayLevel = 2; +static int g_displayLevel = 0; #endif #undef DISPLAY #define DISPLAY(...) \ @@ -297,7 +304,7 @@ /** * Prepare a context for dictionary building. - * The context is only dependent on the parameter `d` and can used multiple + * The context is only dependent on the parameter `d` and can be used multiple * times. * Returns 0 on success or error code on error. * The context must be destroyed with `FASTCOVER_ctx_destroy()`. @@ -549,7 +556,7 @@ ZDICT_cover_params_t coverParams; FASTCOVER_accel_t accelParams; /* Initialize global data */ - g_displayLevel = parameters.zParams.notificationLevel; + g_displayLevel = (int)parameters.zParams.notificationLevel; /* Assign splitPoint and f if not provided */ parameters.splitPoint = 1.0; parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f; @@ -632,7 +639,7 @@ const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel; const unsigned shrinkDict = 0; /* Local variables */ - const int displayLevel = parameters->zParams.notificationLevel; + const int displayLevel = (int)parameters->zParams.notificationLevel; unsigned iteration = 1; unsigned d; unsigned k; @@ -716,7 +723,7 @@ data->parameters.splitPoint = splitPoint; data->parameters.steps = kSteps; data->parameters.shrinkDict = shrinkDict; - data->parameters.zParams.notificationLevel = g_displayLevel; + data->parameters.zParams.notificationLevel = (unsigned)g_displayLevel; /* Check the parameters */ if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity, data->ctx->f, accel)) {
diff --git a/Utilities/cmzstd/lib/dictBuilder/zdict.c b/Utilities/cmzstd/lib/dictBuilder/zdict.c index 459cbe4..58290f4 100644 --- a/Utilities/cmzstd/lib/dictBuilder/zdict.c +++ b/Utilities/cmzstd/lib/dictBuilder/zdict.c
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -44,7 +44,6 @@ #ifndef ZDICT_STATIC_LINKING_ONLY # define ZDICT_STATIC_LINKING_ONLY #endif -#define HUF_STATIC_LINKING_ONLY #include "../common/mem.h" /* read */ #include "../common/fse.h" /* FSE_normalizeCount, FSE_writeNCount */ @@ -54,6 +53,7 @@ #include "../compress/zstd_compress_internal.h" /* ZSTD_loadCEntropy() */ #include "../zdict.h" #include "divsufsort.h" +#include "../common/bits.h" /* ZSTD_NbCommonBytes */ /*-************************************* @@ -130,65 +130,6 @@ /*-******************************************************** * Dictionary training functions **********************************************************/ -static unsigned ZDICT_NbCommonBytes (size_t val) -{ - if (MEM_isLittleEndian()) { - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) - unsigned long r = 0; - _BitScanForward64( &r, (U64)val ); - return (unsigned)(r>>3); -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_ctzll((U64)val) >> 3); -# else - static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - unsigned long r=0; - _BitScanForward( &r, (U32)val ); - return (unsigned)(r>>3); -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_ctz((U32)val) >> 3); -# else - static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; -# endif - } - } else { /* Big Endian CPU */ - if (MEM_64bits()) { -# if defined(_MSC_VER) && defined(_WIN64) - unsigned long r = 0; - _BitScanReverse64( &r, val ); - return (unsigned)(r>>3); -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_clzll(val) >> 3); -# else - unsigned r; - const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */ - if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -# endif - } else { /* 32 bits */ -# if defined(_MSC_VER) - unsigned long r = 0; - _BitScanReverse( &r, (unsigned long)val ); - return (unsigned)(r>>3); -# elif defined(__GNUC__) && (__GNUC__ >= 3) - return (__builtin_clz((U32)val) >> 3); -# else - unsigned r; - if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } - r += (!val); - return r; -# endif - } } -} - - /*! ZDICT_count() : Count the nb of common bytes between 2 pointers. Note : this function presumes end of buffer followed by noisy guard band. @@ -203,7 +144,7 @@ pMatch = (const char*)pMatch+sizeof(size_t); continue; } - pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff); + pIn = (const char*)pIn+ZSTD_NbCommonBytes(diff); return (size_t)((const char*)pIn - pStart); } } @@ -235,7 +176,7 @@ U32 savings[LLIMIT] = {0}; const BYTE* b = (const BYTE*)buffer; size_t maxLength = LLIMIT; - size_t pos = suffix[start]; + size_t pos = (size_t)suffix[start]; U32 end = start; dictItem solution; @@ -369,7 +310,7 @@ savings[i] = savings[i-1] + (lengthList[i] * (i-3)); DISPLAYLEVEL(4, "Selected dict at position %u, of length %u : saves %u (ratio: %.2f) \n", - (unsigned)pos, (unsigned)maxLength, (unsigned)savings[maxLength], (double)savings[maxLength] / maxLength); + (unsigned)pos, (unsigned)maxLength, (unsigned)savings[maxLength], (double)savings[maxLength] / (double)maxLength); solution.pos = (U32)pos; solution.length = (U32)maxLength; @@ -379,7 +320,7 @@ { U32 id; for (id=start; id<end; id++) { U32 p, pEnd, length; - U32 const testedPos = suffix[id]; + U32 const testedPos = (U32)suffix[id]; if (testedPos == pos) length = solution.length; else { @@ -431,7 +372,7 @@ elt = table[u]; /* sort : improve rank */ while ((u>1) && (table[u-1].savings < elt.savings)) - table[u] = table[u-1], u--; + table[u] = table[u-1], u--; table[u] = elt; return u; } } @@ -442,7 +383,7 @@ if ((table[u].pos + table[u].length >= elt.pos) && (table[u].pos < elt.pos)) { /* overlap, existing < new */ /* append */ - int const addedLength = (int)eltEnd - (table[u].pos + table[u].length); + int const addedLength = (int)eltEnd - (int)(table[u].pos + table[u].length); table[u].savings += elt.length / 8; /* rough approx bonus */ if (addedLength > 0) { /* otherwise, elt fully included into existing */ table[u].length += addedLength; @@ -582,7 +523,7 @@ if (solution.length==0) { cursor++; continue; } ZDICT_insertDictItem(dictList, dictListSize, solution, buffer); cursor += solution.length; - DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / bufferSize * 100); + DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / (double)bufferSize * 100.0); } } _cleanup: @@ -625,11 +566,11 @@ size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ - { size_t const errorCode = ZSTD_compressBegin_usingCDict(esr.zc, esr.dict); + { size_t const errorCode = ZSTD_compressBegin_usingCDict_deprecated(esr.zc, esr.dict); if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_compressBegin_usingCDict failed \n"); return; } } - cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); + cSize = ZSTD_compressBlock_deprecated(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (unsigned)srcSize); return; } if (cSize) { /* if == 0; block is not compressible */ @@ -662,8 +603,8 @@ if (nbSeq >= 2) { /* rep offsets */ const seqDef* const seq = seqStorePtr->sequencesStart; - U32 offset1 = seq[0].offset - 3; - U32 offset2 = seq[1].offset - 3; + U32 offset1 = seq[0].offBase - ZSTD_REP_NUM; + U32 offset2 = seq[1].offBase - ZSTD_REP_NUM; if (offset1 >= MAXREPOFFSET) offset1 = 0; if (offset2 >= MAXREPOFFSET) offset2 = 0; repOffsets[offset1] += 3; @@ -734,6 +675,7 @@ size_t const totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles); size_t const averageSampleSize = totalSrcSize / (nbFiles + !nbFiles); BYTE* dstPtr = (BYTE*)dstBuffer; + U32 wksp[HUF_CTABLE_WORKSPACE_SIZE_U32]; /* init */ DEBUGLOG(4, "ZDICT_analyzeEntropy"); @@ -766,8 +708,15 @@ pos += fileSizes[u]; } + if (notificationLevel >= 4) { + /* writeStats */ + DISPLAYLEVEL(4, "Offset Code Frequencies : \n"); + for (u=0; u<=offcodeMax; u++) { + DISPLAYLEVEL(4, "%2u :%7u \n", u, offcodeCount[u]); + } } + /* analyze, build stats, starting with literals */ - { size_t maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); + { size_t maxNbBits = HUF_buildCTable_wksp(hufTable, countLit, 255, huffLog, wksp, sizeof(wksp)); if (HUF_isError(maxNbBits)) { eSize = maxNbBits; DISPLAYLEVEL(1, " HUF_buildCTable error \n"); @@ -776,7 +725,7 @@ if (maxNbBits==8) { /* not compressible : will fail on HUF_writeCTable() */ DISPLAYLEVEL(2, "warning : pathological dataset : literals are not compressible : samples are noisy or too regular \n"); ZDICT_flatLit(countLit); /* replace distribution by a fake "mostly flat but still compressible" distribution, that HUF_writeCTable() can encode */ - maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog); + maxNbBits = HUF_buildCTable_wksp(hufTable, countLit, 255, huffLog, wksp, sizeof(wksp)); assert(maxNbBits==9); } huffLog = (U32)maxNbBits; @@ -817,7 +766,7 @@ llLog = (U32)errorCode; /* write result to buffer */ - { size_t const hhSize = HUF_writeCTable(dstPtr, maxDstSize, hufTable, 255, huffLog); + { size_t const hhSize = HUF_writeCTable_wksp(dstPtr, maxDstSize, hufTable, 255, huffLog, wksp, sizeof(wksp)); if (HUF_isError(hhSize)) { eSize = hhSize; DISPLAYLEVEL(1, "HUF_writeCTable error \n"); @@ -872,7 +821,7 @@ MEM_writeLE32(dstPtr+8, bestRepOffset[2].offset); #else /* at this stage, we don't use the result of "most common first offset", - as the impact of statistics is not properly evaluated */ + * as the impact of statistics is not properly evaluated */ MEM_writeLE32(dstPtr+0, repStartValue[0]); MEM_writeLE32(dstPtr+4, repStartValue[1]); MEM_writeLE32(dstPtr+8, repStartValue[2]); @@ -888,6 +837,17 @@ } +/** + * @returns the maximum repcode value + */ +static U32 ZDICT_maxRep(U32 const reps[ZSTD_REP_NUM]) +{ + U32 maxRep = reps[0]; + int r; + for (r = 1; r < ZSTD_REP_NUM; ++r) + maxRep = MAX(maxRep, reps[r]); + return maxRep; +} size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity, const void* customDictContent, size_t dictContentSize, @@ -899,11 +859,13 @@ BYTE header[HBUFFSIZE]; int const compressionLevel = (params.compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : params.compressionLevel; U32 const notificationLevel = params.notificationLevel; + /* The final dictionary content must be at least as large as the largest repcode */ + size_t const minContentSize = (size_t)ZDICT_maxRep(repStartValue); + size_t paddingSize; /* check conditions */ DEBUGLOG(4, "ZDICT_finalizeDictionary"); if (dictBufferCapacity < dictContentSize) return ERROR(dstSize_tooSmall); - if (dictContentSize < ZDICT_CONTENTSIZE_MIN) return ERROR(srcSize_wrong); if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) return ERROR(dstSize_tooSmall); /* dictionary header */ @@ -927,12 +889,43 @@ hSize += eSize; } - /* copy elements in final buffer ; note : src and dst buffer can overlap */ - if (hSize + dictContentSize > dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize; - { size_t const dictSize = hSize + dictContentSize; - char* dictEnd = (char*)dictBuffer + dictSize; - memmove(dictEnd - dictContentSize, customDictContent, dictContentSize); - memcpy(dictBuffer, header, hSize); + /* Shrink the content size if it doesn't fit in the buffer */ + if (hSize + dictContentSize > dictBufferCapacity) { + dictContentSize = dictBufferCapacity - hSize; + } + + /* Pad the dictionary content with zeros if it is too small */ + if (dictContentSize < minContentSize) { + RETURN_ERROR_IF(hSize + minContentSize > dictBufferCapacity, dstSize_tooSmall, + "dictBufferCapacity too small to fit max repcode"); + paddingSize = minContentSize - dictContentSize; + } else { + paddingSize = 0; + } + + { + size_t const dictSize = hSize + paddingSize + dictContentSize; + + /* The dictionary consists of the header, optional padding, and the content. + * The padding comes before the content because the "best" position in the + * dictionary is the last byte. + */ + BYTE* const outDictHeader = (BYTE*)dictBuffer; + BYTE* const outDictPadding = outDictHeader + hSize; + BYTE* const outDictContent = outDictPadding + paddingSize; + + assert(dictSize <= dictBufferCapacity); + assert(outDictContent + dictContentSize == (BYTE*)dictBuffer + dictSize); + + /* First copy the customDictContent into its final location. + * `customDictContent` and `dictBuffer` may overlap, so we must + * do this before any other writes into the output buffer. + * Then copy the header & padding into the output buffer. + */ + memmove(outDictContent, customDictContent, dictContentSize); + memcpy(outDictHeader, header, hSize); + memset(outDictPadding, 0, paddingSize); + return dictSize; } }
diff --git a/Utilities/cmzstd/lib/zdict.h b/Utilities/cmzstd/lib/zdict.h index 75b05db..2268f94 100644 --- a/Utilities/cmzstd/lib/zdict.h +++ b/Utilities/cmzstd/lib/zdict.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -8,32 +8,43 @@ * You may select, at your option, one of the above-listed licenses. */ -#ifndef DICTBUILDER_H_001 -#define DICTBUILDER_H_001 - #if defined (__cplusplus) extern "C" { #endif +#ifndef ZSTD_ZDICT_H +#define ZSTD_ZDICT_H /*====== Dependencies ======*/ #include <stddef.h> /* size_t */ /* ===== ZDICTLIB_API : control library symbols visibility ===== */ -#ifndef ZDICTLIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZDICTLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#ifndef ZDICTLIB_VISIBLE + /* Backwards compatibility with old macro name */ +# ifdef ZDICTLIB_VISIBILITY +# define ZDICTLIB_VISIBLE ZDICTLIB_VISIBILITY +# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZDICTLIB_VISIBLE __attribute__ ((visibility ("default"))) # else -# define ZDICTLIB_VISIBILITY +# define ZDICTLIB_VISIBLE # endif #endif + +#ifndef ZDICTLIB_HIDDEN +# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZDICTLIB_HIDDEN __attribute__ ((visibility ("hidden"))) +# else +# define ZDICTLIB_HIDDEN +# endif +#endif + #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBILITY +# define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBLE #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +# define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else -# define ZDICTLIB_API ZDICTLIB_VISIBILITY +# define ZDICTLIB_API ZDICTLIB_VISIBLE #endif /******************************************************************************* @@ -46,7 +57,7 @@ * * Zstd can use dictionaries to improve compression ratio of small data. * Traditionally small files don't compress well because there is very little - * repetion in a single sample, since it is small. But, if you are compressing + * repetition in a single sample, since it is small. But, if you are compressing * many similar files, like a bunch of JSON records that share the same * structure, you can train a dictionary on ahead of time on some samples of * these files. Then, zstd can use the dictionary to find repetitions that are @@ -110,8 +121,8 @@ * The zstd CLI defaults to a 110KB dictionary. You likely don't need a * dictionary larger than that. But, most use cases can get away with a * smaller dictionary. The advanced dictionary builders can automatically - * shrink the dictionary for you, and select a the smallest size that - * doesn't hurt compression ratio too much. See the `shrinkDict` parameter. + * shrink the dictionary for you, and select the smallest size that doesn't + * hurt compression ratio too much. See the `shrinkDict` parameter. * A smaller dictionary can save memory, and potentially speed up * compression. * @@ -132,7 +143,7 @@ * * # Benchmark levels 1-3 without a dictionary * zstd -b1e3 -r /path/to/my/files - * # Benchmark levels 1-3 with a dictioanry + * # Benchmark levels 1-3 with a dictionary * zstd -b1e3 -r /path/to/my/files -D /path/to/my/dictionary * * When should I retrain a dictionary? @@ -201,9 +212,9 @@ const size_t* samplesSizes, unsigned nbSamples); typedef struct { - int compressionLevel; /*< optimize for a specific zstd compression level; 0 means default */ - unsigned notificationLevel; /*< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ - unsigned dictID; /*< force dictID value; 0 means auto mode (32-bits random value) + int compressionLevel; /**< optimize for a specific zstd compression level; 0 means default */ + unsigned notificationLevel; /**< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ + unsigned dictID; /**< force dictID value; 0 means auto mode (32-bits random value) * NOTE: The zstd format reserves some dictionary IDs for future use. * You may use them in private settings, but be warned that they * may be used by zstd in a public dictionary registry in the future. @@ -237,7 +248,6 @@ * is presumed that the most profitable content is at the end of the dictionary, * since that is the cheapest to reference. * - * `dictContentSize` must be >= ZDICT_CONTENTSIZE_MIN bytes. * `maxDictSize` must be >= max(dictContentSize, ZSTD_DICTSIZE_MIN). * * @return: size of dictionary stored into `dstDictBuffer` (<= `maxDictSize`), @@ -261,9 +271,21 @@ ZDICTLIB_API unsigned ZDICT_isError(size_t errorCode); ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode); +#endif /* ZSTD_ZDICT_H */ +#if defined(ZDICT_STATIC_LINKING_ONLY) && !defined(ZSTD_ZDICT_H_STATIC) +#define ZSTD_ZDICT_H_STATIC -#ifdef ZDICT_STATIC_LINKING_ONLY +/* This can be overridden externally to hide static symbols. */ +#ifndef ZDICTLIB_STATIC_API +# if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZDICTLIB_STATIC_API __declspec(dllexport) ZDICTLIB_VISIBLE +# elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZDICTLIB_STATIC_API __declspec(dllimport) ZDICTLIB_VISIBLE +# else +# define ZDICTLIB_STATIC_API ZDICTLIB_VISIBLE +# endif +#endif /* ==================================================================================== * The definitions in this section are considered experimental. @@ -272,8 +294,9 @@ * Use them only in association with static linking. * ==================================================================================== */ -#define ZDICT_CONTENTSIZE_MIN 128 #define ZDICT_DICTSIZE_MIN 256 +/* Deprecated: Remove in v1.6.0 */ +#define ZDICT_CONTENTSIZE_MIN 128 /*! ZDICT_cover_params_t: * k and d are the only required parameters. @@ -318,7 +341,7 @@ * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ -ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover( +ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_cover( void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_cover_params_t parameters); @@ -340,7 +363,7 @@ * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread. */ -ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover( +ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_cover( void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_cover_params_t* parameters); @@ -361,7 +384,7 @@ * In general, it's recommended to provide a few thousands samples, though this can vary a lot. * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. */ -ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, +ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t parameters); @@ -384,7 +407,7 @@ * See ZDICT_trainFromBuffer() for details on failure modes. * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 6 * 2^f bytes of memory for each thread. */ -ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer, +ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_fastCover_params_t* parameters); @@ -409,7 +432,7 @@ * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. * Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0. */ -ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy( +ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_legacy( void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_legacy_params_t parameters); @@ -421,32 +444,31 @@ or _CRT_SECURE_NO_WARNINGS in Visual. Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */ #ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS -# define ZDICT_DEPRECATED(message) ZDICTLIB_API /* disable deprecation warnings */ +# define ZDICT_DEPRECATED(message) /* disable deprecation warnings */ #else # define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define ZDICT_DEPRECATED(message) [[deprecated(message)]] ZDICTLIB_API +# define ZDICT_DEPRECATED(message) [[deprecated(message)]] # elif defined(__clang__) || (ZDICT_GCC_VERSION >= 405) -# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message))) +# define ZDICT_DEPRECATED(message) __attribute__((deprecated(message))) # elif (ZDICT_GCC_VERSION >= 301) -# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated)) +# define ZDICT_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) -# define ZDICT_DEPRECATED(message) ZDICTLIB_API __declspec(deprecated(message)) +# define ZDICT_DEPRECATED(message) __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler") -# define ZDICT_DEPRECATED(message) ZDICTLIB_API +# define ZDICT_DEPRECATED(message) # endif #endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */ ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead") +ZDICTLIB_STATIC_API size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); -#endif /* ZDICT_STATIC_LINKING_ONLY */ +#endif /* ZSTD_ZDICT_H_STATIC */ #if defined (__cplusplus) } #endif - -#endif /* DICTBUILDER_H_001 */
diff --git a/Utilities/cmzstd/lib/zstd.h b/Utilities/cmzstd/lib/zstd.h index 4651e6c..e5c3f8b 100644 --- a/Utilities/cmzstd/lib/zstd.h +++ b/Utilities/cmzstd/lib/zstd.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -14,27 +14,61 @@ #ifndef ZSTD_H_235446 #define ZSTD_H_235446 -/* ====== Dependency ======*/ +/* ====== Dependencies ======*/ #include <limits.h> /* INT_MAX */ #include <stddef.h> /* size_t */ /* ===== ZSTDLIB_API : control library symbols visibility ===== */ -#ifndef ZSTDLIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#ifndef ZSTDLIB_VISIBLE + /* Backwards compatibility with old macro name */ +# ifdef ZSTDLIB_VISIBILITY +# define ZSTDLIB_VISIBLE ZSTDLIB_VISIBILITY +# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDLIB_VISIBLE __attribute__ ((visibility ("default"))) # else -# define ZSTDLIB_VISIBILITY +# define ZSTDLIB_VISIBLE # endif #endif -#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY -#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#else -# define ZSTDLIB_API ZSTDLIB_VISIBILITY + +#ifndef ZSTDLIB_HIDDEN +# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDLIB_HIDDEN __attribute__ ((visibility ("hidden"))) +# else +# define ZSTDLIB_HIDDEN +# endif #endif +#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBLE +#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define ZSTDLIB_API ZSTDLIB_VISIBLE +#endif + +/* Deprecation warnings : + * Should these warnings be a problem, it is generally possible to disable them, + * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual. + * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS. + */ +#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS +# define ZSTD_DEPRECATED(message) /* disable deprecation warnings */ +#else +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define ZSTD_DEPRECATED(message) [[deprecated(message)]] +# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__) +# define ZSTD_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ >= 3) +# define ZSTD_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define ZSTD_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler") +# define ZSTD_DEPRECATED(message) +# endif +#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */ + /******************************************************************************* Introduction @@ -72,7 +106,7 @@ /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 5 -#define ZSTD_VERSION_RELEASE 0 +#define ZSTD_VERSION_RELEASE 5 #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) /*! ZSTD_versionNumber() : @@ -114,7 +148,8 @@ ***************************************/ /*! ZSTD_compress() : * Compresses `src` content as a single zstd compressed frame into already allocated `dst`. - * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data. * @return : compressed size written into `dst` (<= `dstCapacity), * or an error code if it fails (which can be tested using ZSTD_isError()). */ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, @@ -163,7 +198,9 @@ * "empty", "unknown" and "error" results to the same return value (0), * while ZSTD_getFrameContentSize() gives them separate return values. * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */ -ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); +ZSTD_DEPRECATED("Replaced by ZSTD_getFrameContentSize") +ZSTDLIB_API +unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); /*! ZSTD_findFrameCompressedSize() : Requires v1.4.0+ * `src` should point to the start of a ZSTD frame or skippable frame. @@ -175,8 +212,30 @@ /*====== Helper functions ======*/ -#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ -ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */ +/* ZSTD_compressBound() : + * maximum compressed size in worst case single-pass scenario. + * When invoking `ZSTD_compress()` or any other one-pass compression function, + * it's recommended to provide @dstCapacity >= ZSTD_compressBound(srcSize) + * as it eliminates one potential failure scenario, + * aka not enough room in dst buffer to write the compressed frame. + * Note : ZSTD_compressBound() itself can fail, if @srcSize > ZSTD_MAX_INPUT_SIZE . + * In which case, ZSTD_compressBound() will return an error code + * which can be tested using ZSTD_isError(). + * + * ZSTD_COMPRESSBOUND() : + * same as ZSTD_compressBound(), but as a macro. + * It can be used to produce constants, which can be useful for static allocation, + * for example to size a static array on stack. + * Will produce constant value 0 if srcSize too large. + */ +#define ZSTD_MAX_INPUT_SIZE ((sizeof(size_t)==8) ? 0xFF00FF00FF00FF00LLU : 0xFF00FF00U) +#define ZSTD_COMPRESSBOUND(srcSize) (((size_t)(srcSize) >= ZSTD_MAX_INPUT_SIZE) ? 0 : (srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */ +ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */ +/* ZSTD_isError() : + * Most ZSTD_* functions returning a size_t value can be tested for error, + * using ZSTD_isError(). + * @return 1 if error, 0 otherwise + */ ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */ ZSTDLIB_API int ZSTD_minCLevel(void); /*!< minimum negative compression level allowed, requires v1.4.0+ */ @@ -247,7 +306,7 @@ * * It's possible to reset all parameters to "default" using ZSTD_CCtx_reset(). * - * This API supercedes all other "advanced" API entry points in the experimental section. + * This API supersedes all other "advanced" API entry points in the experimental section. * In the future, we expect to remove from experimental API entry points which are redundant with this API. */ @@ -417,8 +476,11 @@ * ZSTD_c_stableOutBuffer * ZSTD_c_blockDelimiters * ZSTD_c_validateSequences - * ZSTD_c_splitBlocks + * ZSTD_c_useBlockSplitter * ZSTD_c_useRowMatchFinder + * ZSTD_c_prefetchCDictTables + * ZSTD_c_enableSeqProducerFallback + * ZSTD_c_maxBlockSize * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. * note : never ever use experimentalParam? names directly; * also, the enums values themselves are unstable and can still change. @@ -437,7 +499,11 @@ ZSTD_c_experimentalParam12=1009, ZSTD_c_experimentalParam13=1010, ZSTD_c_experimentalParam14=1011, - ZSTD_c_experimentalParam15=1012 + ZSTD_c_experimentalParam15=1012, + ZSTD_c_experimentalParam16=1013, + ZSTD_c_experimentalParam17=1014, + ZSTD_c_experimentalParam18=1015, + ZSTD_c_experimentalParam19=1016 } ZSTD_cParameter; typedef struct { @@ -500,7 +566,7 @@ * They will be used to compress next frame. * Resetting session never fails. * - The parameters : changes all parameters back to "default". - * This removes any reference to any dictionary too. + * This also removes any reference to any dictionary or external sequence producer. * Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing) * otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError()) * - Both : similar to resetting the session, followed by resetting parameters. @@ -513,7 +579,8 @@ * Should cctx hold data from a previously unfinished frame, everything about it is forgotten. * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() * - The function is always blocking, returns when compression is completed. - * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data, though it is possible it fails for other reasons. * @return : compressed size written into `dst` (<= `dstCapacity), * or an error code if it fails (which can be tested using ZSTD_isError()). */ @@ -550,13 +617,15 @@ * ZSTD_d_stableOutBuffer * ZSTD_d_forceIgnoreChecksum * ZSTD_d_refMultipleDDicts + * ZSTD_d_disableHuffmanAssembly * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. * note : never ever use experimentalParam? names directly */ ZSTD_d_experimentalParam1=1000, ZSTD_d_experimentalParam2=1001, ZSTD_d_experimentalParam3=1002, - ZSTD_d_experimentalParam4=1003 + ZSTD_d_experimentalParam4=1003, + ZSTD_d_experimentalParam5=1004 } ZSTD_dParameter; @@ -735,8 +804,6 @@ * This following is a legacy streaming API, available since v1.0+ . * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2(). * It is redundant, but remains fully supported. - * Streaming in combination with advanced parameters and dictionary compression - * can only be used through the new API. ******************************************************************************/ /*! @@ -745,6 +812,9 @@ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any) * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * + * Note that ZSTD_initCStream() clears any previously set dictionary. Use the new API + * to compress with a dictionary. */ ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel); /*! @@ -795,13 +865,31 @@ /*===== Streaming decompression functions =====*/ -/* This function is redundant with the advanced API and equivalent to: +/*! ZSTD_initDStream() : + * Initialize/reset DStream state for new decompression operation. + * Call before new decompression operation using same DStream. * + * Note : This function is redundant with the advanced API and equivalent to: * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); * ZSTD_DCtx_refDDict(zds, NULL); */ ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds); +/*! ZSTD_decompressStream() : + * Streaming decompression function. + * Call repetitively to consume full input updating it as necessary. + * Function will update both input and output `pos` fields exposing current state via these fields: + * - `input.pos < input.size`, some input remaining and caller should provide remaining input + * on the next call. + * - `output.pos < output.size`, decoder finished and flushed all remaining buffers. + * - `output.pos == output.size`, potentially uncflushed data present in the internal buffers, + * call ZSTD_decompressStream() again to flush remaining data to output. + * Note : with no additional input, amount of data flushed <= ZSTD_BLOCKSIZE_MAX. + * + * @return : 0 when a frame is completely decoded and fully flushed, + * or an error code, which can be tested using ZSTD_isError(), + * or any other value > 0, which means there is some decoding or flushing to do to complete current frame. + */ ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input); ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input buffer */ @@ -920,7 +1008,7 @@ * If @return == 0, the dictID could not be decoded. * This could for one of the following reasons : * - The frame does not require a dictionary to be decoded (most common case). - * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. + * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden piece of information. * Note : this use case also happens when using a non-conformant dictionary. * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). * - This is not a Zstandard frame. @@ -932,9 +1020,11 @@ * Advanced dictionary and prefix API (Requires v1.4.0+) * * This API allows dictionaries to be used with ZSTD_compress2(), - * ZSTD_compressStream2(), and ZSTD_decompress(). Dictionaries are sticky, and - * only reset with the context is reset with ZSTD_reset_parameters or - * ZSTD_reset_session_and_parameters. Prefixes are single-use. + * ZSTD_compressStream2(), and ZSTD_decompressDCtx(). + * Dictionaries are sticky, they remain valid when same context is re-used, + * they only reset when the context is reset + * with ZSTD_reset_parameters or ZSTD_reset_session_and_parameters. + * In contrast, Prefixes are single-use. ******************************************************************************/ @@ -944,8 +1034,9 @@ * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary, * meaning "return to no-dictionary mode". - * Note 1 : Dictionary is sticky, it will be used for all future compressed frames. - * To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters). + * Note 1 : Dictionary is sticky, it will be used for all future compressed frames, + * until parameters are reset, a new dictionary is loaded, or the dictionary + * is explicitly invalidated by loading a NULL dictionary. * Note 2 : Loading a dictionary involves building tables. * It's also a CPU consuming operation, with non-negligible impact on latency. * Tables are dependent on compression parameters, and for this reason, @@ -954,11 +1045,15 @@ * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead. * In such a case, dictionary buffer must outlive its users. * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() - * to precisely select how dictionary content must be interpreted. */ + * to precisely select how dictionary content must be interpreted. + * Note 5 : This method does not benefit from LDM (long distance mode). + * If you want to employ LDM on some large dictionary content, + * prefer employing ZSTD_CCtx_refPrefix() described below. + */ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); /*! ZSTD_CCtx_refCDict() : Requires v1.4.0+ - * Reference a prepared dictionary, to be used for all next compressed frames. + * Reference a prepared dictionary, to be used for all future compressed frames. * Note that compression parameters are enforced from within CDict, * and supersede any compression parameter previously set within CCtx. * The parameters ignored are labelled as "superseded-by-cdict" in the ZSTD_cParameter enum docs. @@ -977,6 +1072,7 @@ * Decompression will need same prefix to properly regenerate data. * Compressing with a prefix is similar in outcome as performing a diff and compressing it, * but performs much faster, especially during decompression (compression speed is tunable with compression level). + * This method is compatible with LDM (long distance mode). * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary * Note 1 : Prefix buffer is referenced. It **must** outlive compression. @@ -993,9 +1089,9 @@ const void* prefix, size_t prefixSize); /*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+ - * Create an internal DDict from dict buffer, - * to be used to decompress next frames. - * The dictionary remains valid for all future frames, until explicitly invalidated. + * Create an internal DDict from dict buffer, to be used to decompress all future frames. + * The dictionary remains valid for all future frames, until explicitly invalidated, or + * a new dictionary is loaded. * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary, * meaning "return to no-dictionary mode". @@ -1019,9 +1115,10 @@ * The memory for the table is allocated on the first call to refDDict, and can be * freed with ZSTD_freeDCtx(). * + * If called with ZSTD_d_refMultipleDDicts disabled (the default), only one dictionary + * will be managed, and referencing a dictionary effectively "discards" any previous one. + * * @result : 0, or an error code (which can be tested with ZSTD_isError()). - * Note 1 : Currently, only one dictionary can be managed. - * Referencing a new dictionary effectively "discards" any previous one. * Special: referencing a NULL DDict means "return to no-dictionary mode". * Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx. */ @@ -1073,27 +1170,16 @@ #if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY) #define ZSTD_H_ZSTD_STATIC_LINKING_ONLY -/* Deprecation warnings : - * Should these warnings be a problem, it is generally possible to disable them, - * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual. - * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS. - */ -#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS -# define ZSTD_DEPRECATED(message) ZSTDLIB_API /* disable deprecation warnings */ -#else -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define ZSTD_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_API -# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__) -# define ZSTD_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated(message))) -# elif defined(__GNUC__) && (__GNUC__ >= 3) -# define ZSTD_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated)) -# elif defined(_MSC_VER) -# define ZSTD_DEPRECATED(message) ZSTDLIB_API __declspec(deprecated(message)) +/* This can be overridden externally to hide static symbols. */ +#ifndef ZSTDLIB_STATIC_API +# if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZSTDLIB_STATIC_API __declspec(dllexport) ZSTDLIB_VISIBLE +# elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZSTDLIB_STATIC_API __declspec(dllimport) ZSTDLIB_VISIBLE # else -# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler") -# define ZSTD_DEPRECATED(message) ZSTDLIB_API +# define ZSTDLIB_STATIC_API ZSTDLIB_VISIBLE # endif -#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */ +#endif /**************************************************************************************** * experimental API (static linking only) @@ -1129,6 +1215,7 @@ #define ZSTD_TARGETLENGTH_MIN 0 /* note : comparing this constant to an unsigned results in a tautological test */ #define ZSTD_STRATEGY_MIN ZSTD_fast #define ZSTD_STRATEGY_MAX ZSTD_btultra2 +#define ZSTD_BLOCKSIZE_MAX_MIN (1 << 10) /* The minimum valid max blocksize. Maximum blocksizes smaller than this make compressBound() inaccurate. */ #define ZSTD_OVERLAPLOG_MIN 0 @@ -1157,9 +1244,6 @@ #define ZSTD_SRCSIZEHINT_MIN 0 #define ZSTD_SRCSIZEHINT_MAX INT_MAX -/* internal */ -#define ZSTD_HASHLOG3_MAX 17 - /* --- Advanced types --- */ @@ -1302,13 +1386,17 @@ } ZSTD_literalCompressionMode_e; typedef enum { - ZSTD_urm_auto = 0, /* Automatically determine whether or not we use row matchfinder */ - ZSTD_urm_disableRowMatchFinder = 1, /* Never use row matchfinder */ - ZSTD_urm_enableRowMatchFinder = 2 /* Always use row matchfinder when applicable */ -} ZSTD_useRowMatchFinderMode_e; + /* Note: This enum controls features which are conditionally beneficial. Zstd typically will make a final + * decision on whether or not to enable the feature (ZSTD_ps_auto), but setting the switch to ZSTD_ps_enable + * or ZSTD_ps_disable allow for a force enable/disable the feature. + */ + ZSTD_ps_auto = 0, /* Let the library automatically determine whether the feature shall be enabled */ + ZSTD_ps_enable = 1, /* Force-enable the feature */ + ZSTD_ps_disable = 2 /* Do not use the feature */ +} ZSTD_paramSwitch_e; /*************************************** -* Frame size functions +* Frame header and size functions ***************************************/ /*! ZSTD_findDecompressedSize() : @@ -1332,7 +1420,7 @@ * note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to * read each contained frame header. This is fast as most of the data is skipped, * however it does mean that all frame data must be present and valid. */ -ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize); +ZSTDLIB_STATIC_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize); /*! ZSTD_decompressBound() : * `src` should point to the start of a series of ZSTD encoded and/or skippable frames @@ -1347,41 +1435,121 @@ * note 3 : when the decompressed size field isn't available, the upper-bound for that frame is calculated by: * upper-bound = # blocks * min(128 KB, Window_Size) */ -ZSTDLIB_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize); +ZSTDLIB_STATIC_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize); /*! ZSTD_frameHeaderSize() : * srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX. * @return : size of the Frame Header, * or an error code (if srcSize is too small) */ -ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); +ZSTDLIB_STATIC_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); + +typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e; +typedef struct { + unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ + unsigned long long windowSize; /* can be very large, up to <= frameContentSize */ + unsigned blockSizeMax; + ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ + unsigned headerSize; + unsigned dictID; + unsigned checksumFlag; + unsigned _reserved1; + unsigned _reserved2; +} ZSTD_frameHeader; + +/*! ZSTD_getFrameHeader() : + * decode Frame Header, or requires larger `srcSize`. + * @return : 0, `zfhPtr` is correctly filled, + * >0, `srcSize` is too small, value is wanted `srcSize` amount, + * or an error code, which can be tested using ZSTD_isError() */ +ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ +/*! ZSTD_getFrameHeader_advanced() : + * same as ZSTD_getFrameHeader(), + * with added capability to select a format (like ZSTD_f_zstd1_magicless) */ +ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format); + +/*! ZSTD_decompressionMargin() : + * Zstd supports in-place decompression, where the input and output buffers overlap. + * In this case, the output buffer must be at least (Margin + Output_Size) bytes large, + * and the input buffer must be at the end of the output buffer. + * + * _______________________ Output Buffer ________________________ + * | | + * | ____ Input Buffer ____| + * | | | + * v v v + * |---------------------------------------|-----------|----------| + * ^ ^ ^ + * |___________________ Output_Size ___________________|_ Margin _| + * + * NOTE: See also ZSTD_DECOMPRESSION_MARGIN(). + * NOTE: This applies only to single-pass decompression through ZSTD_decompress() or + * ZSTD_decompressDCtx(). + * NOTE: This function supports multi-frame input. + * + * @param src The compressed frame(s) + * @param srcSize The size of the compressed frame(s) + * @returns The decompression margin or an error that can be checked with ZSTD_isError(). + */ +ZSTDLIB_STATIC_API size_t ZSTD_decompressionMargin(const void* src, size_t srcSize); + +/*! ZSTD_DECOMPRESS_MARGIN() : + * Similar to ZSTD_decompressionMargin(), but instead of computing the margin from + * the compressed frame, compute it from the original size and the blockSizeLog. + * See ZSTD_decompressionMargin() for details. + * + * WARNING: This macro does not support multi-frame input, the input must be a single + * zstd frame. If you need that support use the function, or implement it yourself. + * + * @param originalSize The original uncompressed size of the data. + * @param blockSize The block size == MIN(windowSize, ZSTD_BLOCKSIZE_MAX). + * Unless you explicitly set the windowLog smaller than + * ZSTD_BLOCKSIZELOG_MAX you can just use ZSTD_BLOCKSIZE_MAX. + */ +#define ZSTD_DECOMPRESSION_MARGIN(originalSize, blockSize) ((size_t)( \ + ZSTD_FRAMEHEADERSIZE_MAX /* Frame header */ + \ + 4 /* checksum */ + \ + ((originalSize) == 0 ? 0 : 3 * (((originalSize) + (blockSize) - 1) / blockSize)) /* 3 bytes per block */ + \ + (blockSize) /* One block of margin */ \ + )) typedef enum { ZSTD_sf_noBlockDelimiters = 0, /* Representation of ZSTD_Sequence has no block delimiters, sequences only */ ZSTD_sf_explicitBlockDelimiters = 1 /* Representation of ZSTD_Sequence contains explicit block delimiters */ } ZSTD_sequenceFormat_e; +/*! ZSTD_sequenceBound() : + * `srcSize` : size of the input buffer + * @return : upper-bound for the number of sequences that can be generated + * from a buffer of srcSize bytes + * + * note : returns number of sequences - to get bytes, multiply by sizeof(ZSTD_Sequence). + */ +ZSTDLIB_STATIC_API size_t ZSTD_sequenceBound(size_t srcSize); + /*! ZSTD_generateSequences() : - * Generate sequences using ZSTD_compress2, given a source buffer. + * Generate sequences using ZSTD_compress2(), given a source buffer. * * Each block will end with a dummy sequence * with offset == 0, matchLength == 0, and litLength == length of last literals. * litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0) * simply acts as a block delimiter. * - * zc can be used to insert custom compression params. - * This function invokes ZSTD_compress2 + * @zc can be used to insert custom compression params. + * This function invokes ZSTD_compress2(). * * The output of this function can be fed into ZSTD_compressSequences() with CCtx * setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters * @return : number of sequences generated */ -ZSTDLIB_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs, - size_t outSeqsSize, const void* src, size_t srcSize); +ZSTDLIB_STATIC_API size_t +ZSTD_generateSequences( ZSTD_CCtx* zc, + ZSTD_Sequence* outSeqs, size_t outSeqsSize, + const void* src, size_t srcSize); /*! ZSTD_mergeBlockDelimiters() : * Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals - * by merging them into into the literals of the next sequence. + * by merging them into the literals of the next sequence. * * As such, the final generated result has no explicit representation of block boundaries, * and the final last literals segment is not represented in the sequences. @@ -1390,10 +1558,12 @@ * setting of ZSTD_c_blockDelimiters as ZSTD_sf_noBlockDelimiters * @return : number of sequences left after merging */ -ZSTDLIB_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize); +ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize); /*! ZSTD_compressSequences() : - * Compress an array of ZSTD_Sequence, generated from the original source buffer, into dst. + * Compress an array of ZSTD_Sequence, associated with @src buffer, into dst. + * @src contains the entire input (not just the literals). + * If @srcSize > sum(sequence.length), the remaining bytes are considered all literals * If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.) * The entire source is compressed into a single frame. * @@ -1418,17 +1588,18 @@ * Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused. * Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly, * and cannot emit an RLE block that disagrees with the repcode history - * @return : final compressed size or a ZSTD error. + * @return : final compressed size, or a ZSTD error code. */ -ZSTDLIB_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize, - const ZSTD_Sequence* inSeqs, size_t inSeqsSize, - const void* src, size_t srcSize); +ZSTDLIB_STATIC_API size_t +ZSTD_compressSequences( ZSTD_CCtx* cctx, void* dst, size_t dstSize, + const ZSTD_Sequence* inSeqs, size_t inSeqsSize, + const void* src, size_t srcSize); /*! ZSTD_writeSkippableFrame() : * Generates a zstd skippable frame containing data given by src, and writes it to dst buffer. * - * Skippable frames begin with a a 4-byte magic number. There are 16 possible choices of magic number, + * Skippable frames begin with a 4-byte magic number. There are 16 possible choices of magic number, * ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15. * As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so * the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant. @@ -1438,9 +1609,29 @@ * * @return : number of bytes written or a ZSTD error. */ -ZSTDLIB_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity, +ZSTDLIB_STATIC_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned magicVariant); +/*! ZSTD_readSkippableFrame() : + * Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer. + * + * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written, + * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested + * in the magicVariant. + * + * Returns an error if destination buffer is not large enough, or if the frame is not skippable. + * + * @return : number of bytes written or a ZSTD error. + */ +ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant, + const void* src, size_t srcSize); + +/*! ZSTD_isSkippableFrame() : + * Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame. + */ +ZSTDLIB_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size); + + /*************************************** * Memory management @@ -1466,13 +1657,16 @@ * and ZSTD_estimateCCtxSize_usingCCtxParams(), which can be used in tandem with ZSTD_CCtxParams_setParameter(). * Both can be used to estimate memory using custom compression parameters and arbitrary srcSize limits. * - * Note 2 : only single-threaded compression is supported. + * Note : only single-threaded compression is supported. * ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1. + * + * Note 2 : ZSTD_estimateCCtxSize* functions are not compatible with the Block-Level Sequence Producer API at this time. + * Size estimates assume that no external sequence producer is registered. */ -ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel); -ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); -ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params); -ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize(int compressionLevel); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params); +ZSTDLIB_STATIC_API size_t ZSTD_estimateDCtxSize(void); /*! ZSTD_estimateCStreamSize() : * ZSTD_estimateCStreamSize() will provide a budget large enough for any compression level up to selected one. @@ -1486,21 +1680,26 @@ * or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame(); * Note : if streaming is init with function ZSTD_init?Stream_usingDict(), * an internal ?Dict will be created, which additional size is not estimated here. - * In this case, get total size by adding ZSTD_estimate?DictSize */ -ZSTDLIB_API size_t ZSTD_estimateCStreamSize(int compressionLevel); -ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams); -ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params); -ZSTDLIB_API size_t ZSTD_estimateDStreamSize(size_t windowSize); -ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize); + * In this case, get total size by adding ZSTD_estimate?DictSize + * Note 2 : only single-threaded compression is supported. + * ZSTD_estimateCStreamSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1. + * Note 3 : ZSTD_estimateCStreamSize* functions are not compatible with the Block-Level Sequence Producer API at this time. + * Size estimates assume that no external sequence producer is registered. + */ +ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize(int compressionLevel); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params); +ZSTDLIB_STATIC_API size_t ZSTD_estimateDStreamSize(size_t windowSize); +ZSTDLIB_STATIC_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize); /*! ZSTD_estimate?DictSize() : * ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict(). * ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced(). * Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller. */ -ZSTDLIB_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel); -ZSTDLIB_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod); -ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel); +ZSTDLIB_STATIC_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod); +ZSTDLIB_STATIC_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod); /*! ZSTD_initStatic*() : * Initialize an object using a pre-allocated fixed-size buffer. @@ -1523,20 +1722,20 @@ * Limitation 2 : static cctx currently not compatible with multi-threading. * Limitation 3 : static dctx is incompatible with legacy support. */ -ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize); -ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */ +ZSTDLIB_STATIC_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize); +ZSTDLIB_STATIC_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */ -ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize); -ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */ +ZSTDLIB_STATIC_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize); +ZSTDLIB_STATIC_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */ -ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict( +ZSTDLIB_STATIC_API const ZSTD_CDict* ZSTD_initStaticCDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams); -ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict( +ZSTDLIB_STATIC_API const ZSTD_DDict* ZSTD_initStaticDDict( void* workspace, size_t workspaceSize, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, @@ -1557,44 +1756,44 @@ #endif ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */ -ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); +ZSTDLIB_STATIC_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); +ZSTDLIB_STATIC_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); +ZSTDLIB_STATIC_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); +ZSTDLIB_STATIC_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, +ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, ZSTD_compressionParameters cParams, ZSTD_customMem customMem); -/* ! Thread pool : - * These prototypes make it possible to share a thread pool among multiple compression contexts. - * This can limit resources for applications with multiple threads where each one uses - * a threaded compression mode (via ZSTD_c_nbWorkers parameter). - * ZSTD_createThreadPool creates a new thread pool with a given number of threads. - * Note that the lifetime of such pool must exist while being used. - * ZSTD_CCtx_refThreadPool assigns a thread pool to a context (use NULL argument value - * to use an internal thread pool). - * ZSTD_freeThreadPool frees a thread pool, accepts NULL pointer. +/*! Thread pool : + * These prototypes make it possible to share a thread pool among multiple compression contexts. + * This can limit resources for applications with multiple threads where each one uses + * a threaded compression mode (via ZSTD_c_nbWorkers parameter). + * ZSTD_createThreadPool creates a new thread pool with a given number of threads. + * Note that the lifetime of such pool must exist while being used. + * ZSTD_CCtx_refThreadPool assigns a thread pool to a context (use NULL argument value + * to use an internal thread pool). + * ZSTD_freeThreadPool frees a thread pool, accepts NULL pointer. */ typedef struct POOL_ctx_s ZSTD_threadPool; -ZSTDLIB_API ZSTD_threadPool* ZSTD_createThreadPool(size_t numThreads); -ZSTDLIB_API void ZSTD_freeThreadPool (ZSTD_threadPool* pool); /* accept NULL pointer */ -ZSTDLIB_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool); +ZSTDLIB_STATIC_API ZSTD_threadPool* ZSTD_createThreadPool(size_t numThreads); +ZSTDLIB_STATIC_API void ZSTD_freeThreadPool (ZSTD_threadPool* pool); /* accept NULL pointer */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool); /* * This API is temporary and is expected to change or disappear in the future! */ -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced2( +ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_advanced2( const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, const ZSTD_CCtx_params* cctxParams, ZSTD_customMem customMem); -ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced( +ZSTDLIB_STATIC_API ZSTD_DDict* ZSTD_createDDict_advanced( const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType, @@ -1611,22 +1810,22 @@ * As a consequence, `dictBuffer` **must** outlive CDict, * and its content must remain unmodified throughout the lifetime of CDict. * note: equivalent to ZSTD_createCDict_advanced(), with dictLoadMethod==ZSTD_dlm_byRef */ -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); +ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); /*! ZSTD_getCParams() : * @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize. * `estimatedSrcSize` value is optional, select 0 if not known */ -ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); +ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); /*! ZSTD_getParams() : * same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`. * All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */ -ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); +ZSTDLIB_STATIC_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); /*! ZSTD_checkCParams() : * Ensure param values remain within authorized range. * @return 0 on success, or an error code (can be checked with ZSTD_isError()) */ -ZSTDLIB_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); +ZSTDLIB_STATIC_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); /*! ZSTD_adjustCParams() : * optimize params for a given `srcSize` and `dictSize`. @@ -1634,24 +1833,47 @@ * `dictSize` must be `0` when there is no dictionary. * cPar can be invalid : all parameters will be clamped within valid range in the @return struct. * This function never fails (wide contract) */ -ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); +ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); + +/*! ZSTD_CCtx_setCParams() : + * Set all parameters provided within @p cparams into the working @p cctx. + * Note : if modifying parameters during compression (MT mode only), + * note that changes to the .windowLog parameter will be ignored. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + * On failure, no parameters are updated. + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams); + +/*! ZSTD_CCtx_setFParams() : + * Set all parameters provided within @p fparams into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams); + +/*! ZSTD_CCtx_setParams() : + * Set all parameters provided within @p params into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params); /*! ZSTD_compress_advanced() : * Note : this function is now DEPRECATED. * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters. * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_compress2") +ZSTDLIB_STATIC_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize, - ZSTD_parameters params); + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize, + ZSTD_parameters params); /*! ZSTD_compress_usingCDict_advanced() : * Note : this function is now DEPRECATED. * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters. * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary") +ZSTDLIB_STATIC_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -1662,18 +1884,18 @@ /*! ZSTD_CCtx_loadDictionary_byReference() : * Same as ZSTD_CCtx_loadDictionary(), but dictionary content is referenced, instead of being copied into CCtx. * It saves some memory, but also requires that `dict` outlives its usage within `cctx` */ -ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); /*! ZSTD_CCtx_loadDictionary_advanced() : * Same as ZSTD_CCtx_loadDictionary(), but gives finer control over * how to load the dictionary (by copy ? by reference ?) * and how to interpret it (automatic ? force raw mode ? full mode only ?) */ -ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); /*! ZSTD_CCtx_refPrefix_advanced() : * Same as ZSTD_CCtx_refPrefix(), but gives finer control over * how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */ -ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); /* === experimental parameters === */ /* these parameters can be used with ZSTD_setParameter() @@ -1712,9 +1934,15 @@ * See the comments on that enum for an explanation of the feature. */ #define ZSTD_c_forceAttachDict ZSTD_c_experimentalParam4 -/* Controls how the literals are compressed (default is auto). - * The value must be of type ZSTD_literalCompressionMode_e. - * See ZSTD_literalCompressionMode_e enum definition for details. +/* Controlled with ZSTD_paramSwitch_e enum. + * Default is ZSTD_ps_auto. + * Set to ZSTD_ps_disable to never compress literals. + * Set to ZSTD_ps_enable to always compress literals. (Note: uncompressed literals + * may still be emitted if huffman is not beneficial to use.) + * + * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use + * literals compression based on the compression parameters - specifically, + * negative compression levels do not use literal compression. */ #define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5 @@ -1777,7 +2005,7 @@ * * Note that this means that the CDict tables can no longer be copied into the * CCtx, so the dict attachment mode ZSTD_dictForceCopy will no longer be - * useable. The dictionary can only be attached or reloaded. + * usable. The dictionary can only be attached or reloaded. * * In general, you should expect compression to be faster--sometimes very much * so--and CDict creation to be slightly slower. Eventually, we will probably @@ -1789,13 +2017,16 @@ * Experimental parameter. * Default is 0 == disabled. Set to 1 to enable. * - * Tells the compressor that the ZSTD_inBuffer will ALWAYS be the same - * between calls, except for the modifications that zstd makes to pos (the - * caller must not modify pos). This is checked by the compressor, and - * compression will fail if it ever changes. This means the only flush - * mode that makes sense is ZSTD_e_end, so zstd will error if ZSTD_e_end - * is not used. The data in the ZSTD_inBuffer in the range [src, src + pos) - * MUST not be modified during compression or you will get data corruption. + * Tells the compressor that input data presented with ZSTD_inBuffer + * will ALWAYS be the same between calls. + * Technically, the @src pointer must never be changed, + * and the @pos field can only be updated by zstd. + * However, it's possible to increase the @size field, + * allowing scenarios where more data can be appended after compressions starts. + * These conditions are checked by the compressor, + * and compression will fail if they are not respected. + * Also, data in the ZSTD_inBuffer within the range [src, src + pos) + * MUST not be modified during compression or it will result in data corruption. * * When this flag is enabled zstd won't allocate an input window buffer, * because the user guarantees it can reference the ZSTD_inBuffer until @@ -1803,18 +2034,15 @@ * large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also * avoid the memcpy() from the input buffer to the input window buffer. * - * NOTE: ZSTD_compressStream2() will error if ZSTD_e_end is not used. - * That means this flag cannot be used with ZSTD_compressStream(). - * * NOTE: So long as the ZSTD_inBuffer always points to valid memory, using * this flag is ALWAYS memory safe, and will never access out-of-bounds - * memory. However, compression WILL fail if you violate the preconditions. + * memory. However, compression WILL fail if conditions are not respected. * - * WARNING: The data in the ZSTD_inBuffer in the range [dst, dst + pos) MUST - * not be modified during compression or you will get data corruption. This - * is because zstd needs to reference data in the ZSTD_inBuffer to find + * WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST + * not be modified during compression or it will result in data corruption. + * This is because zstd needs to reference data in the ZSTD_inBuffer to find * matches. Normally zstd maintains its own window buffer for this purpose, - * but passing this flag tells zstd to use the user provided buffer. + * but passing this flag tells zstd to rely on user provided buffer instead. */ #define ZSTD_c_stableInBuffer ZSTD_c_experimentalParam9 @@ -1859,30 +2087,33 @@ * Without validation, providing a sequence that does not conform to the zstd spec will cause * undefined behavior, and may produce a corrupted block. * - * With validation enabled, a if sequence is invalid (see doc/zstd_compression_format.md for + * With validation enabled, if sequence is invalid (see doc/zstd_compression_format.md for * specifics regarding offset/matchlength requirements) then the function will bail out and * return an error. * */ #define ZSTD_c_validateSequences ZSTD_c_experimentalParam12 -/* ZSTD_c_splitBlocks - * Default is 0 == disabled. Set to 1 to enable block splitting. +/* ZSTD_c_useBlockSplitter + * Controlled with ZSTD_paramSwitch_e enum. + * Default is ZSTD_ps_auto. + * Set to ZSTD_ps_disable to never use block splitter. + * Set to ZSTD_ps_enable to always use block splitter. * - * Will attempt to split blocks in order to improve compression ratio at the cost of speed. + * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use + * block splitting based on the compression parameters. */ -#define ZSTD_c_splitBlocks ZSTD_c_experimentalParam13 +#define ZSTD_c_useBlockSplitter ZSTD_c_experimentalParam13 /* ZSTD_c_useRowMatchFinder - * Default is ZSTD_urm_auto. - * Controlled with ZSTD_useRowMatchFinderMode_e enum. + * Controlled with ZSTD_paramSwitch_e enum. + * Default is ZSTD_ps_auto. + * Set to ZSTD_ps_disable to never use row-based matchfinder. + * Set to ZSTD_ps_enable to force usage of row-based matchfinder. * - * By default, in ZSTD_urm_auto, when finalizing the compression parameters, the library - * will decide at runtime whether to use the row-based matchfinder based on support for SIMD - * instructions as well as the windowLog. - * - * Set to ZSTD_urm_disableRowMatchFinder to never use row-based matchfinder. - * Set to ZSTD_urm_enableRowMatchFinder to force usage of row-based matchfinder. + * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use + * the row-based matchfinder based on support for SIMD instructions and the window log. + * Note that this only pertains to compression strategies: greedy, lazy, and lazy2 */ #define ZSTD_c_useRowMatchFinder ZSTD_c_experimentalParam14 @@ -1906,12 +2137,85 @@ */ #define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15 +/* ZSTD_c_prefetchCDictTables + * Controlled with ZSTD_paramSwitch_e enum. Default is ZSTD_ps_auto. + * + * In some situations, zstd uses CDict tables in-place rather than copying them + * into the working context. (See docs on ZSTD_dictAttachPref_e above for details). + * In such situations, compression speed is seriously impacted when CDict tables are + * "cold" (outside CPU cache). This parameter instructs zstd to prefetch CDict tables + * when they are used in-place. + * + * For sufficiently small inputs, the cost of the prefetch will outweigh the benefit. + * For sufficiently large inputs, zstd will by default memcpy() CDict tables + * into the working context, so there is no need to prefetch. This parameter is + * targeted at a middle range of input sizes, where a prefetch is cheap enough to be + * useful but memcpy() is too expensive. The exact range of input sizes where this + * makes sense is best determined by careful experimentation. + * + * Note: for this parameter, ZSTD_ps_auto is currently equivalent to ZSTD_ps_disable, + * but in the future zstd may conditionally enable this feature via an auto-detection + * heuristic for cold CDicts. + * Use ZSTD_ps_disable to opt out of prefetching under any circumstances. + */ +#define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16 + +/* ZSTD_c_enableSeqProducerFallback + * Allowed values are 0 (disable) and 1 (enable). The default setting is 0. + * + * Controls whether zstd will fall back to an internal sequence producer if an + * external sequence producer is registered and returns an error code. This fallback + * is block-by-block: the internal sequence producer will only be called for blocks + * where the external sequence producer returns an error code. Fallback parsing will + * follow any other cParam settings, such as compression level, the same as in a + * normal (fully-internal) compression operation. + * + * The user is strongly encouraged to read the full Block-Level Sequence Producer API + * documentation (below) before setting this parameter. */ +#define ZSTD_c_enableSeqProducerFallback ZSTD_c_experimentalParam17 + +/* ZSTD_c_maxBlockSize + * Allowed values are between 1KB and ZSTD_BLOCKSIZE_MAX (128KB). + * The default is ZSTD_BLOCKSIZE_MAX, and setting to 0 will set to the default. + * + * This parameter can be used to set an upper bound on the blocksize + * that overrides the default ZSTD_BLOCKSIZE_MAX. It cannot be used to set upper + * bounds greater than ZSTD_BLOCKSIZE_MAX or bounds lower than 1KB (will make + * compressBound() inaccurate). Only currently meant to be used for testing. + * + */ +#define ZSTD_c_maxBlockSize ZSTD_c_experimentalParam18 + +/* ZSTD_c_searchForExternalRepcodes + * This parameter affects how zstd parses external sequences, such as sequences + * provided through the compressSequences() API or from an external block-level + * sequence producer. + * + * If set to ZSTD_ps_enable, the library will check for repeated offsets in + * external sequences, even if those repcodes are not explicitly indicated in + * the "rep" field. Note that this is the only way to exploit repcode matches + * while using compressSequences() or an external sequence producer, since zstd + * currently ignores the "rep" field of external sequences. + * + * If set to ZSTD_ps_disable, the library will not exploit repeated offsets in + * external sequences, regardless of whether the "rep" field has been set. This + * reduces sequence compression overhead by about 25% while sacrificing some + * compression ratio. + * + * The default value is ZSTD_ps_auto, for which the library will enable/disable + * based on compression level. + * + * Note: for now, this param only has an effect if ZSTD_c_blockDelimiters is + * set to ZSTD_sf_explicitBlockDelimiters. That may change in the future. + */ +#define ZSTD_c_searchForExternalRepcodes ZSTD_c_experimentalParam19 + /*! ZSTD_CCtx_getParameter() : * Get the requested compression parameter value, selected by enum ZSTD_cParameter, * and store it into int* value. * @return : 0, or an error code (which can be tested with ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_CCtx_getParameter(const ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value); +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_getParameter(const ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value); /*! ZSTD_CCtx_params : @@ -1931,25 +2235,25 @@ * This can be used with ZSTD_estimateCCtxSize_advanced_usingCCtxParams() * for static allocation of CCtx for single-threaded compression. */ -ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void); -ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params); /* accept NULL pointer */ +ZSTDLIB_STATIC_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void); +ZSTDLIB_STATIC_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params); /* accept NULL pointer */ /*! ZSTD_CCtxParams_reset() : * Reset params to default values. */ -ZSTDLIB_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params); +ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params); /*! ZSTD_CCtxParams_init() : * Initializes the compression parameters of cctxParams according to * compression level. All other parameters are reset to their default values. */ -ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel); +ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel); /*! ZSTD_CCtxParams_init_advanced() : * Initializes the compression and frame parameters of cctxParams according to * params. All other parameters are reset to their default values. */ -ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params); +ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params); /*! ZSTD_CCtxParams_setParameter() : Requires v1.4.0+ * Similar to ZSTD_CCtx_setParameter. @@ -1959,14 +2263,14 @@ * @result : a code representing success or failure (which can be tested with * ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value); +ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value); /*! ZSTD_CCtxParams_getParameter() : * Similar to ZSTD_CCtx_getParameter. * Get the requested value of one compression parameter, selected by enum ZSTD_cParameter. * @result : 0, or an error code (which can be tested with ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_CCtxParams_getParameter(const ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value); +ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_getParameter(const ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value); /*! ZSTD_CCtx_setParametersUsingCCtxParams() : * Apply a set of ZSTD_CCtx_params to the compression context. @@ -1975,7 +2279,7 @@ * if nbWorkers>=1, new parameters will be picked up at next job, * with a few restrictions (windowLog, pledgedSrcSize, nbWorkers, jobSize, and overlapLog are not updated). */ -ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams( +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params); /*! ZSTD_compressStream2_simpleArgs() : @@ -1984,7 +2288,7 @@ * This variant might be helpful for binders from dynamic languages * which have troubles handling structures containing memory pointers. */ -ZSTDLIB_API size_t ZSTD_compressStream2_simpleArgs ( +ZSTDLIB_STATIC_API size_t ZSTD_compressStream2_simpleArgs ( ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos, @@ -2000,33 +2304,33 @@ * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. * Note 3 : Skippable Frame Identifiers are considered valid. */ -ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size); +ZSTDLIB_STATIC_API unsigned ZSTD_isFrame(const void* buffer, size_t size); /*! ZSTD_createDDict_byReference() : * Create a digested dictionary, ready to start decompression operation without startup delay. * Dictionary content is referenced, and therefore stays in dictBuffer. * It is important that dictBuffer outlives DDict, * it must remain read accessible throughout the lifetime of DDict */ -ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); +ZSTDLIB_STATIC_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); /*! ZSTD_DCtx_loadDictionary_byReference() : * Same as ZSTD_DCtx_loadDictionary(), * but references `dict` content instead of copying it into `dctx`. * This saves memory if `dict` remains around., * However, it's imperative that `dict` remains accessible (and unmodified) while being used, so it must outlive decompression. */ -ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); /*! ZSTD_DCtx_loadDictionary_advanced() : * Same as ZSTD_DCtx_loadDictionary(), * but gives direct control over * how to load the dictionary (by copy ? by reference ?) * and how to interpret it (automatic ? force raw mode ? full mode only ?). */ -ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType); /*! ZSTD_DCtx_refPrefix_advanced() : * Same as ZSTD_DCtx_refPrefix(), but gives finer control over * how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */ -ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType); /*! ZSTD_DCtx_setMaxWindowSize() : * Refuses allocating internal buffers for frames requiring a window size larger than provided limit. @@ -2035,14 +2339,14 @@ * By default, a decompression context accepts all window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) * @return : 0, or an error code (which can be tested using ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize); +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize); /*! ZSTD_DCtx_getParameter() : * Get the requested decompression parameter value, selected by enum ZSTD_dParameter, * and store it into int* value. * @return : 0, or an error code (which can be tested with ZSTD_isError()). */ -ZSTDLIB_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value); +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value); /* ZSTD_d_format * experimental parameter, @@ -2062,7 +2366,7 @@ * in the range [dst, dst + pos) MUST not be modified during decompression * or you will get data corruption. * - * When this flags is enabled zstd won't allocate an output buffer, because + * When this flag is enabled zstd won't allocate an output buffer, because * it can write directly to the ZSTD_outBuffer, but it will still allocate * an input buffer large enough to fit any compressed block. This will also * avoid the memcpy() from the internal output buffer to the ZSTD_outBuffer. @@ -2115,6 +2419,17 @@ */ #define ZSTD_d_refMultipleDDicts ZSTD_d_experimentalParam4 +/* ZSTD_d_disableHuffmanAssembly + * Set to 1 to disable the Huffman assembly implementation. + * The default value is 0, which allows zstd to use the Huffman assembly + * implementation if available. + * + * This parameter can be used to disable Huffman assembly at runtime. + * If you want to disable it at compile time you can define the macro + * ZSTD_DISABLE_ASM. + */ +#define ZSTD_d_disableHuffmanAssembly ZSTD_d_experimentalParam5 + /*! ZSTD_DCtx_setFormat() : * This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter(). @@ -2123,6 +2438,7 @@ * such ZSTD_f_zstd1_magicless for example. * @return : 0, or an error code (which can be tested using ZSTD_isError()). */ ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead") +ZSTDLIB_STATIC_API size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format); /*! ZSTD_decompressStream_simpleArgs() : @@ -2131,7 +2447,7 @@ * This can be helpful for binders from dynamic languages * which have troubles handling structures containing memory pointers. */ -ZSTDLIB_API size_t ZSTD_decompressStream_simpleArgs ( +ZSTDLIB_STATIC_API size_t ZSTD_decompressStream_simpleArgs ( ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos); @@ -2159,6 +2475,7 @@ * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); @@ -2176,17 +2493,15 @@ * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /*! ZSTD_initCStream_advanced() : - * This function is DEPRECATED, and is approximately equivalent to: + * This function is DEPRECATED, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - * // Pseudocode: Set each zstd parameter and leave the rest as-is. - * for ((param, value) : params) { - * ZSTD_CCtx_setParameter(zcs, param, value); - * } + * ZSTD_CCtx_setParams(zcs, params); * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); * @@ -2196,6 +2511,7 @@ * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, @@ -2205,20 +2521,18 @@ * This function is DEPRECATED, and equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); * ZSTD_CCtx_refCDict(zcs, cdict); - * + * * note : cdict will just be referenced, and must outlive compression session * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /*! ZSTD_initCStream_usingCDict_advanced() : - * This function is DEPRECATED, and is approximately equivalent to: + * This function is DEPRECATED, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - * // Pseudocode: Set each zstd frame parameter and leave the rest as-is. - * for ((fParam, value) : fParams) { - * ZSTD_CCtx_setParameter(zcs, fParam, value); - * } + * ZSTD_CCtx_setFParams(zcs, fParams); * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_refCDict(zcs, cdict); * @@ -2228,6 +2542,7 @@ * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, @@ -2252,6 +2567,7 @@ * This prototype will generate compilation warnings. */ ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); @@ -2270,7 +2586,7 @@ * Note : (ingested - consumed) is amount of input data buffered internally, not yet compressed. * Aggregates progression inside active worker threads. */ -ZSTDLIB_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx); +ZSTDLIB_STATIC_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx); /*! ZSTD_toFlushNow() : * Tell how many bytes are ready to be flushed immediately. @@ -2285,7 +2601,7 @@ * therefore flush speed is limited by production speed of oldest job * irrespective of the speed of concurrent (and newer) jobs. */ -ZSTDLIB_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx); +ZSTDLIB_STATIC_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx); /*===== Advanced Streaming decompression functions =====*/ @@ -2297,9 +2613,9 @@ * ZSTD_DCtx_loadDictionary(zds, dict, dictSize); * * note: no dictionary will be used if dict == NULL or dictSize < 8 - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ -ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); +ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_loadDictionary, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /*! * This function is deprecated, and is equivalent to: @@ -2308,9 +2624,9 @@ * ZSTD_DCtx_refDDict(zds, ddict); * * note : ddict is referenced, it must outlive decompression session - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ -ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); +ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_refDDict, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /*! * This function is deprecated, and is equivalent to: @@ -2318,17 +2634,185 @@ * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); * * re-use decompression parameters from previous init; saves dictionary loading - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x */ -ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); +ZSTD_DEPRECATED("use ZSTD_DCtx_reset, see zstd.h for detailed instructions") +ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); + + +/* ********************* BLOCK-LEVEL SEQUENCE PRODUCER API ********************* + * + * *** OVERVIEW *** + * The Block-Level Sequence Producer API allows users to provide their own custom + * sequence producer which libzstd invokes to process each block. The produced list + * of sequences (literals and matches) is then post-processed by libzstd to produce + * valid compressed blocks. + * + * This block-level offload API is a more granular complement of the existing + * frame-level offload API compressSequences() (introduced in v1.5.1). It offers + * an easier migration story for applications already integrated with libzstd: the + * user application continues to invoke the same compression functions + * ZSTD_compress2() or ZSTD_compressStream2() as usual, and transparently benefits + * from the specific advantages of the external sequence producer. For example, + * the sequence producer could be tuned to take advantage of known characteristics + * of the input, to offer better speed / ratio, or could leverage hardware + * acceleration not available within libzstd itself. + * + * See contrib/externalSequenceProducer for an example program employing the + * Block-Level Sequence Producer API. + * + * *** USAGE *** + * The user is responsible for implementing a function of type + * ZSTD_sequenceProducer_F. For each block, zstd will pass the following + * arguments to the user-provided function: + * + * - sequenceProducerState: a pointer to a user-managed state for the sequence + * producer. + * + * - outSeqs, outSeqsCapacity: an output buffer for the sequence producer. + * outSeqsCapacity is guaranteed >= ZSTD_sequenceBound(srcSize). The memory + * backing outSeqs is managed by the CCtx. + * + * - src, srcSize: an input buffer for the sequence producer to parse. + * srcSize is guaranteed to be <= ZSTD_BLOCKSIZE_MAX. + * + * - dict, dictSize: a history buffer, which may be empty, which the sequence + * producer may reference as it parses the src buffer. Currently, zstd will + * always pass dictSize == 0 into external sequence producers, but this will + * change in the future. + * + * - compressionLevel: a signed integer representing the zstd compression level + * set by the user for the current operation. The sequence producer may choose + * to use this information to change its compression strategy and speed/ratio + * tradeoff. Note: the compression level does not reflect zstd parameters set + * through the advanced API. + * + * - windowSize: a size_t representing the maximum allowed offset for external + * sequences. Note that sequence offsets are sometimes allowed to exceed the + * windowSize if a dictionary is present, see doc/zstd_compression_format.md + * for details. + * + * The user-provided function shall return a size_t representing the number of + * sequences written to outSeqs. This return value will be treated as an error + * code if it is greater than outSeqsCapacity. The return value must be non-zero + * if srcSize is non-zero. The ZSTD_SEQUENCE_PRODUCER_ERROR macro is provided + * for convenience, but any value greater than outSeqsCapacity will be treated as + * an error code. + * + * If the user-provided function does not return an error code, the sequences + * written to outSeqs must be a valid parse of the src buffer. Data corruption may + * occur if the parse is not valid. A parse is defined to be valid if the + * following conditions hold: + * - The sum of matchLengths and literalLengths must equal srcSize. + * - All sequences in the parse, except for the final sequence, must have + * matchLength >= ZSTD_MINMATCH_MIN. The final sequence must have + * matchLength >= ZSTD_MINMATCH_MIN or matchLength == 0. + * - All offsets must respect the windowSize parameter as specified in + * doc/zstd_compression_format.md. + * - If the final sequence has matchLength == 0, it must also have offset == 0. + * + * zstd will only validate these conditions (and fail compression if they do not + * hold) if the ZSTD_c_validateSequences cParam is enabled. Note that sequence + * validation has a performance cost. + * + * If the user-provided function returns an error, zstd will either fall back + * to an internal sequence producer or fail the compression operation. The user can + * choose between the two behaviors by setting the ZSTD_c_enableSeqProducerFallback + * cParam. Fallback compression will follow any other cParam settings, such as + * compression level, the same as in a normal compression operation. + * + * The user shall instruct zstd to use a particular ZSTD_sequenceProducer_F + * function by calling + * ZSTD_registerSequenceProducer(cctx, + * sequenceProducerState, + * sequenceProducer) + * This setting will persist until the next parameter reset of the CCtx. + * + * The sequenceProducerState must be initialized by the user before calling + * ZSTD_registerSequenceProducer(). The user is responsible for destroying the + * sequenceProducerState. + * + * *** LIMITATIONS *** + * This API is compatible with all zstd compression APIs which respect advanced parameters. + * However, there are three limitations: + * + * First, the ZSTD_c_enableLongDistanceMatching cParam is not currently supported. + * COMPRESSION WILL FAIL if it is enabled and the user tries to compress with a block-level + * external sequence producer. + * - Note that ZSTD_c_enableLongDistanceMatching is auto-enabled by default in some + * cases (see its documentation for details). Users must explicitly set + * ZSTD_c_enableLongDistanceMatching to ZSTD_ps_disable in such cases if an external + * sequence producer is registered. + * - As of this writing, ZSTD_c_enableLongDistanceMatching is disabled by default + * whenever ZSTD_c_windowLog < 128MB, but that's subject to change. Users should + * check the docs on ZSTD_c_enableLongDistanceMatching whenever the Block-Level Sequence + * Producer API is used in conjunction with advanced settings (like ZSTD_c_windowLog). + * + * Second, history buffers are not currently supported. Concretely, zstd will always pass + * dictSize == 0 to the external sequence producer (for now). This has two implications: + * - Dictionaries are not currently supported. Compression will *not* fail if the user + * references a dictionary, but the dictionary won't have any effect. + * - Stream history is not currently supported. All advanced compression APIs, including + * streaming APIs, work with external sequence producers, but each block is treated as + * an independent chunk without history from previous blocks. + * + * Third, multi-threading within a single compression is not currently supported. In other words, + * COMPRESSION WILL FAIL if ZSTD_c_nbWorkers > 0 and an external sequence producer is registered. + * Multi-threading across compressions is fine: simply create one CCtx per thread. + * + * Long-term, we plan to overcome all three limitations. There is no technical blocker to + * overcoming them. It is purely a question of engineering effort. + */ + +#define ZSTD_SEQUENCE_PRODUCER_ERROR ((size_t)(-1)) + +typedef size_t ZSTD_sequenceProducer_F ( + void* sequenceProducerState, + ZSTD_Sequence* outSeqs, size_t outSeqsCapacity, + const void* src, size_t srcSize, + const void* dict, size_t dictSize, + int compressionLevel, + size_t windowSize +); + +/*! ZSTD_registerSequenceProducer() : + * Instruct zstd to use a block-level external sequence producer function. + * + * The sequenceProducerState must be initialized by the caller, and the caller is + * responsible for managing its lifetime. This parameter is sticky across + * compressions. It will remain set until the user explicitly resets compression + * parameters. + * + * Sequence producer registration is considered to be an "advanced parameter", + * part of the "advanced API". This means it will only have an effect on compression + * APIs which respect advanced parameters, such as compress2() and compressStream2(). + * Older compression APIs such as compressCCtx(), which predate the introduction of + * "advanced parameters", will ignore any external sequence producer setting. + * + * The sequence producer can be "cleared" by registering a NULL function pointer. This + * removes all limitations described above in the "LIMITATIONS" section of the API docs. + * + * The user is strongly encouraged to read the full API documentation (above) before + * calling this function. */ +ZSTDLIB_STATIC_API void +ZSTD_registerSequenceProducer( + ZSTD_CCtx* cctx, + void* sequenceProducerState, + ZSTD_sequenceProducer_F* sequenceProducer +); /********************************************************************* -* Buffer-less and synchronous inner streaming functions +* Buffer-less and synchronous inner streaming functions (DEPRECATED) * -* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. -* But it's also a complex one, with several restrictions, documented below. -* Prefer normal streaming API for an easier experience. +* This API is deprecated, and will be removed in a future version. +* It allows streaming (de)compression with user allocated buffers. +* However, it is hard to use, and not as well tested as the rest of +* our API. +* +* Please use the normal streaming API instead: ZSTD_compressStream2, +* and ZSTD_decompressStream. +* If there is functionality that you need, but it doesn't provide, +* please open an issue on our GitHub. ********************************************************************* */ /** @@ -2340,7 +2824,6 @@ Start by initializing a context. Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression. - It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx() Then, consume your input using ZSTD_compressContinue(). There are some important considerations to keep in mind when using this advanced function : @@ -2362,18 +2845,28 @@ */ /*===== Buffer-less streaming compression functions =====*/ -ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); -ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); -ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ -ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ -ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.") +ZSTDLIB_STATIC_API +size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ + +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */ ZSTD_DEPRECATED("use advanced API to access custom parameters") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */ ZSTD_DEPRECATED("use advanced API to access custom parameters") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ /** Buffer-less streaming decompression (synchronous mode) @@ -2386,8 +2879,8 @@ Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough. Data fragment must be large enough to ensure successful decoding. `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough. - @result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled. - >0 : `srcSize` is too small, please provide at least @result bytes on next attempt. + result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled. + >0 : `srcSize` is too small, please provide at least result bytes on next attempt. errorCode, which can be tested using ZSTD_isError(). It fills a ZSTD_frameHeader structure with important information to correctly decode the frame, @@ -2406,7 +2899,7 @@ The most memory efficient way is to use a round buffer of sufficient size. Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(), - which can @return an error code if required value is too large for current system (in 32-bits mode). + which can return an error code if required value is too large for current system (in 32-bits mode). In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one, up to the moment there is not enough room left in the buffer to guarantee decoding another full block, which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`. @@ -2426,7 +2919,7 @@ ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. - @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). + result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item. It can also be an error code, which can be tested with ZSTD_isError(). @@ -2449,49 +2942,42 @@ */ /*===== Buffer-less streaming decompression functions =====*/ -typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e; -typedef struct { - unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ - unsigned long long windowSize; /* can be very large, up to <= frameContentSize */ - unsigned blockSizeMax; - ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ - unsigned headerSize; - unsigned dictID; - unsigned checksumFlag; -} ZSTD_frameHeader; -/*! ZSTD_getFrameHeader() : - * decode Frame Header, or requires larger `srcSize`. - * @return : 0, `zfhPtr` is correctly filled, - * >0, `srcSize` is too small, value is wanted `srcSize` amount, - * or an error code, which can be tested using ZSTD_isError() */ -ZSTDLIB_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ -/*! ZSTD_getFrameHeader_advanced() : - * same as ZSTD_getFrameHeader(), - * with added capability to select a format (like ZSTD_f_zstd1_magicless) */ -ZSTDLIB_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format); -ZSTDLIB_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */ +ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */ -ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); -ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); +ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); +ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); -ZSTDLIB_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); -ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); +ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* misc */ -ZSTDLIB_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); +ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.") +ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; -ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); +ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); -/* ============================ */ -/** Block level API */ -/* ============================ */ +/* ========================================= */ +/** Block level API (DEPRECATED) */ +/* ========================================= */ /*! + + This API is deprecated in favor of the regular compression API. + You can get the frame header down to 2 bytes by setting: + - ZSTD_c_format = ZSTD_f_zstd1_magicless + - ZSTD_c_contentSizeFlag = 0 + - ZSTD_c_checksumFlag = 0 + - ZSTD_c_dictIDFlag = 0 + + This API is not as well tested as our normal API, so we recommend not using it. + We will be removing it in a future version. If the normal API doesn't provide + the functionality you need, please open a GitHub issue. + Block functions produce and decode raw zstd blocks, without frame metadata. Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes. @@ -2502,7 +2988,6 @@ - It is necessary to init context before starting + compression : any ZSTD_compressBegin*() variant, including with dictionary + decompression : any ZSTD_decompressBegin*() variant, including with dictionary - + copyCCtx() and copyDCtx() can be used too - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB + If input is larger than a block size, it's necessary to split input data into multiple blocks + For inputs larger than a single block, consider using regular ZSTD_compress() instead. @@ -2519,11 +3004,14 @@ */ /*===== Raw zstd block functions =====*/ -ZSTDLIB_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx); -ZSTDLIB_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ - +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ #endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */
diff --git a/Utilities/cmzstd/lib/zstd_errors.h b/Utilities/cmzstd/lib/zstd_errors.h index fa3686b..dc75eee 100644 --- a/Utilities/cmzstd/lib/zstd_errors.h +++ b/Utilities/cmzstd/lib/zstd_errors.h
@@ -1,5 +1,5 @@ /* - * Copyright (c) Yann Collet, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the @@ -20,19 +20,31 @@ /* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */ -#ifndef ZSTDERRORLIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZSTDERRORLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#ifndef ZSTDERRORLIB_VISIBLE + /* Backwards compatibility with old macro name */ +# ifdef ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY +# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default"))) # else -# define ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_VISIBLE # endif #endif + +#ifndef ZSTDERRORLIB_HIDDEN +# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) +# define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden"))) +# else +# define ZSTDERRORLIB_HIDDEN +# endif +#endif + #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else -# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY +# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE #endif /*-********************************************* @@ -58,14 +70,17 @@ ZSTD_error_frameParameter_windowTooLarge = 16, ZSTD_error_corruption_detected = 20, ZSTD_error_checksum_wrong = 22, + ZSTD_error_literals_headerWrong = 24, ZSTD_error_dictionary_corrupted = 30, ZSTD_error_dictionary_wrong = 32, ZSTD_error_dictionaryCreation_failed = 34, ZSTD_error_parameter_unsupported = 40, + ZSTD_error_parameter_combination_unsupported = 41, ZSTD_error_parameter_outOfBound = 42, ZSTD_error_tableLog_tooLarge = 44, ZSTD_error_maxSymbolValue_tooLarge = 46, ZSTD_error_maxSymbolValue_tooSmall = 48, + ZSTD_error_stabilityCondition_notRespected = 50, ZSTD_error_stage_wrong = 60, ZSTD_error_init_missing = 62, ZSTD_error_memory_allocation = 64, @@ -73,11 +88,15 @@ ZSTD_error_dstSize_tooSmall = 70, ZSTD_error_srcSize_wrong = 72, ZSTD_error_dstBuffer_null = 74, + ZSTD_error_noForwardProgress_destFull = 80, + ZSTD_error_noForwardProgress_inputEmpty = 82, /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */ ZSTD_error_frameIndex_tooLarge = 100, ZSTD_error_seekableIO = 102, ZSTD_error_dstBuffer_wrong = 104, ZSTD_error_srcBuffer_wrong = 105, + ZSTD_error_sequenceProducer_failed = 106, + ZSTD_error_externalSequences_invalid = 107, ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */ } ZSTD_ErrorCode;
diff --git a/bootstrap b/bootstrap index 01fcfae..88878fe 100755 --- a/bootstrap +++ b/bootstrap
@@ -353,6 +353,7 @@ cmExprParserHelper \ cmExternalMakefileProjectGenerator \ cmFileCommand \ + cmFileCommand_ReadMacho \ cmFileCopier \ cmFileInstaller \ cmFileSet \ @@ -381,6 +382,14 @@ cmGeneratorExpressionNode \ cmGeneratorExpressionParser \ cmGeneratorTarget \ + cmGeneratorTarget_CompatibleInterface \ + cmGeneratorTarget_IncludeDirectories \ + cmGeneratorTarget_Link \ + cmGeneratorTarget_LinkDirectories \ + cmGeneratorTarget_Options \ + cmGeneratorTarget_Sources \ + cmGeneratorTarget_TargetPropertyEntry \ + cmGeneratorTarget_TransitiveProperty \ cmGetCMakePropertyCommand \ cmGetDirectoryPropertyCommand \ cmGetFilenameComponentCommand \ @@ -492,6 +501,7 @@ cmTargetPropCommandBase \ cmTargetPropertyComputer \ cmTargetSourcesCommand \ + cmTargetTraceDependencies \ cmTest \ cmTestGenerator \ cmTimestamp \ @@ -512,6 +522,12 @@ cm_fileno \ " +if ${cmake_system_darwin}; then + CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\ + cmMachO \ + " +fi + if ${cmake_system_mingw}; then CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\ cmGlobalMSYSMakefileGenerator \ @@ -684,8 +700,8 @@ (default) --system-cppdap use system-installed cppdap library --no-system-cppdap use cmake-provided cppdap library (default) - --system-curl use system-installed curl library - --no-system-curl use cmake-provided curl library (default) + --system-curl use system-installed curl library (default on macOS) + --no-system-curl use cmake-provided curl library (default elsewhere) --system-expat use system-installed expat library --no-system-expat use cmake-provided expat library (default) --system-jsoncpp use system-installed jsoncpp library @@ -1660,6 +1676,10 @@ cmake_report cmConfigure.h${_tmp} "#define CMAKE_BOOTSTRAP_MAKEFILES" fi +if ${cmake_system_darwin}; then + cmake_report cmConfigure.h${_tmp} "#define CMake_USE_MACH_PARSER" +fi + if ${cmake_system_mingw}; then cmake_report cmConfigure.h${_tmp} "#if defined(_WIN32) && !defined(NOMINMAX)" cmake_report cmConfigure.h${_tmp} "# define NOMINMAX" @@ -1787,20 +1807,26 @@ else if test `which pkg-config`; then use_uv_flags="`pkg-config --cflags libuv`" + use_uv_ldflags="`pkg-config --libs libuv`" cmake_c_flags="${cmake_c_flags} ${use_uv_flags}" cmake_cxx_flags="${cmake_cxx_flags} ${use_uv_flags}" + else + use_uv_ldflags="-luv" fi - libs="${libs} -luv" + libs="${libs} ${use_uv_ldflags}" fi librhash_c_flags="-DNO_IMPORT_EXPORT" if test "x${bootstrap_system_librhash}" != "x"; then if test `which pkg-config`; then use_librhash_flags="`pkg-config --cflags librhash`" + use_librhash_ldflags="`pkg-config --libs librhash`" cmake_c_flags="${cmake_c_flags} ${use_librhash_flags}" cmake_cxx_flags="${cmake_cxx_flags} ${use_librhash_flags}" + else + use_librhash_ldflags="-lrhash" fi - libs="${libs} -lrhash" + libs="${libs} ${use_librhash_ldflags}" fi jsoncpp_cxx_flags= @@ -1809,9 +1835,12 @@ else if test `which pkg-config`; then use_jsoncpp_flags="`pkg-config --cflags jsoncpp`" + use_jsoncpp_ldflags="`pkg-config --libs jsoncpp`" cmake_cxx_flags="${cmake_cxx_flags} ${use_jsoncpp_flags}" + else + use_jsoncpp_ldflags="-ljsoncpp" fi - libs="${libs} -ljsoncpp" + libs="${libs} ${use_jsoncpp_ldflags}" fi if test "x${cmake_ansi_cxx_flags}" != "x"; then