diff options
1381 files changed, 22750 insertions, 14229 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 768a902..829e34f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -114,6 +114,7 @@ l:sphinx-fedora37: extends: - .fedora37_sphinx - .cmake_build_linux + - .cmake_sphinx_artifacts - .linux_x86_64_tags - .run_automatically variables: @@ -909,6 +910,34 @@ t:macos-arm64-xcode: variables: CMAKE_CI_NO_MR: "true" +t:macos-x86_64-ninja-ub: + extends: + - .macos_x86_64_ninja_ub + - .cmake_test_macos_external + - .macos_x86_64_tags_ext + - .cmake_junit_artifacts + - .run_dependent + dependencies: + - t:macos-x86_64-ninja + needs: + - t:macos-x86_64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + +t:macos-x86_64-xcode-ub: + extends: + - .macos_x86_64_xcode_ub + - .cmake_test_macos_external + - .macos_x86_64_tags_ext + - .cmake_junit_artifacts + - .run_dependent + dependencies: + - t:macos-x86_64-ninja + needs: + - t:macos-x86_64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + b:macos-package: extends: - .macos_package diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index 41f24ed..6c4cc0d 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -91,6 +91,15 @@ junit: - build/junit.xml +.cmake_sphinx_artifacts: + artifacts: + expire_in: 1d + when: always + paths: + # Take the sphinx logs. + - build/build-*.log + - build/linkcheck/output.* + .cmake_test_artifacts: artifacts: expire_in: 1d diff --git a/.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake b/.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake new file mode 100644 index 0000000..1b976d2 --- /dev/null +++ b/.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake b/.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake new file mode 100644 index 0000000..1b976d2 --- /dev/null +++ b/.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_sphinx.cmake b/.gitlab/ci/configure_sphinx.cmake index 3750309..9f3f0be 100644 --- a/.gitlab/ci/configure_sphinx.cmake +++ b/.gitlab/ci/configure_sphinx.cmake @@ -4,3 +4,6 @@ set(SPHINX_HTML ON CACHE BOOL "") set(SPHINX_SINGLEHTML ON CACHE BOOL "") set(SPHINX_QTHELP ON CACHE BOOL "") set(SPHINX_TEXT ON CACHE BOOL "") +if(NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(SPHINX_LINKCHECK ON CACHE BOOL "") +endif() diff --git a/.gitlab/ci/configure_windows_vs_common.cmake b/.gitlab/ci/configure_windows_vs_common.cmake index 962f03d..daf9aaa 100644 --- a/.gitlab/ci/configure_windows_vs_common.cmake +++ b/.gitlab/ci/configure_windows_vs_common.cmake @@ -4,6 +4,7 @@ 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_FindOpenMP_Fortran "OFF" CACHE BOOL "") +set(CMake_TEST_FindPatch "ON" CACHE BOOL "") set(CMake_TEST_Java OFF CACHE BOOL "") set(CMake_TEST_MFC "ON" CACHE BOOL "") diff --git a/.gitlab/ci/configure_windows_vs_common_ninja.cmake b/.gitlab/ci/configure_windows_vs_common_ninja.cmake index 1ae1a66..9f7acc3 100644 --- a/.gitlab/ci/configure_windows_vs_common_ninja.cmake +++ b/.gitlab/ci/configure_windows_vs_common_ninja.cmake @@ -6,6 +6,7 @@ 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_FindOpenMP_Fortran "OFF" CACHE BOOL "") +set(CMake_TEST_FindPatch "ON" CACHE BOOL "") set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "") set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "") set(CMake_TEST_MFC "ON" CACHE BOOL "") diff --git a/.gitlab/ci/env_macos_x86_64_ninja_ub.cmake b/.gitlab/ci/env_macos_x86_64_ninja_ub.cmake new file mode 100644 index 0000000..4b5c401 --- /dev/null +++ b/.gitlab/ci/env_macos_x86_64_ninja_ub.cmake @@ -0,0 +1 @@ +set(ENV{CMAKE_OSX_ARCHITECTURES} "x86_64;arm64") diff --git a/.gitlab/ci/env_macos_x86_64_xcode_ub.cmake b/.gitlab/ci/env_macos_x86_64_xcode_ub.cmake new file mode 100644 index 0000000..4b5c401 --- /dev/null +++ b/.gitlab/ci/env_macos_x86_64_xcode_ub.cmake @@ -0,0 +1 @@ +set(ENV{CMAKE_OSX_ARCHITECTURES} "x86_64;arm64") diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md new file mode 100644 index 0000000..3b083d2 --- /dev/null +++ b/.gitlab/issue_templates/Default.md @@ -0,0 +1,9 @@ +<!-- +This issue tracker is for CMake upstream development: + +* If you are having trouble building a specific third-party project + that uses CMake, ask for help in that project's forums first. + +* If you have a coding or usage question, please ask for help + on the CMake discourse forums: https://discourse.cmake.org/ +--> diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 652a67a..6f0bea5 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -80,6 +80,14 @@ CMAKE_GENERATOR: Xcode CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" +.macos_x86_64_xcode_ub: + extends: .macos + + variables: + CMAKE_CONFIGURATION: macos_x86_64_xcode_ub + CMAKE_GENERATOR: Xcode + CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" + .macos_x86_64_ninja_multi: extends: .macos @@ -88,6 +96,13 @@ CMAKE_GENERATOR: "Ninja Multi-Config" CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" +.macos_x86_64_ninja_ub: + extends: .macos + + variables: + CMAKE_CONFIGURATION: macos_x86_64_ninja_ub + CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" + ## Tags .macos_x86_64_tags: diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index 5e936c2..f303bd4 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -459,6 +459,7 @@ syn keyword cmakeVariable contained \ BUILD_SHARED_LIBS \ CACHE \ CMAKE_ABSOLUTE_DESTINATION_FILES + \ CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY \ CMAKE_AIX_EXPORT_ALL_SYMBOLS \ CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS \ CMAKE_ANDROID_API @@ -2095,6 +2096,7 @@ syn keyword cmakeKWadd_custom_command contained \ COMMENT \ CROSSCOMPILING_EMULATOR \ DEPENDS + \ DEPENDS_EXPLICIT_ONLY \ DEPFILE \ GENERATED \ IMPLICIT_DEPENDS diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ce42fb..6322aa6 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.24 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.25 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) diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt index bd55e24..21236fa 100644 --- a/Help/command/FIND_XXX.txt +++ b/Help/command/FIND_XXX.txt @@ -140,21 +140,40 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: |prefix_XXX_SUBDIR| for each ``<prefix>`` in :variable:`CMAKE_SYSTEM_PREFIX_PATH` -1. .. versionadded:: 3.12 - If called from within a find module or any other script loaded by a call to - :command:`find_package(<PackageName>)`, search prefixes unique to the - current package being found. Specifically, look in the - :variable:`<PackageName>_ROOT` CMake variable and the - :envvar:`<PackageName>_ROOT` environment variable. - The package root variables are maintained as a stack, so if called from - nested find modules or config packages, root paths from the parent's find - module or config package will be searched after paths from the current - module or package. In other words, the search order would be - ``<CurrentPackage>_ROOT``, ``ENV{<CurrentPackage>_ROOT}``, - ``<ParentPackage>_ROOT``, ``ENV{<ParentPackage>_ROOT}``, etc. - This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting - the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``. - See policy :policy:`CMP0074`. +1. If called from within a find module or any other script loaded by a call to + :command:`find_package(<PackageName>)`, search prefixes unique to the + current package being found. See policy :policy:`CMP0074`. + + .. versionadded:: 3.12 + + Specifically, search paths specified by the following variables, in order: + + a. :variable:`<PackageName>_ROOT` CMake variable, + where ``<PackageName>`` is the case-preserved package name. + + b. :variable:`<PACKAGENAME>_ROOT` CMake variable, + where ``<PACKAGENAME>`` is the upper-cased package name. + See policy :policy:`CMP0144`. + + .. versionadded:: 3.27 + + c. :envvar:`<PackageName>_ROOT` environment variable, + where ``<PackageName>`` is the case-preserved package name. + + d. :envvar:`<PACKAGENAME>_ROOT` environment variable, + where ``<PACKAGENAME>`` is the upper-cased package name. + See policy :policy:`CMP0144`. + + .. versionadded:: 3.27 + + The package root variables are maintained as a stack, so if called from + nested find modules or config packages, root paths from the parent's find + module or config package will be searched after paths from the current + module or package. In other words, the search order would be + ``<CurrentPackage>_ROOT``, ``ENV{<CurrentPackage>_ROOT}``, + ``<ParentPackage>_ROOT``, ``ENV{<ParentPackage>_ROOT}``, etc. + This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting + the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``. * |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX| diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 293d3f0..1ccd434 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -25,7 +25,8 @@ The first signature is for adding a custom command to produce an output: [DEPFILE depfile] [JOB_POOL job_pool] [VERBATIM] [APPEND] [USES_TERMINAL] - [COMMAND_EXPAND_LISTS]) + [COMMAND_EXPAND_LISTS] + [DEPENDS_EXPLICIT_ONLY]) This defines a command to generate specified ``OUTPUT`` file(s). A target created in the same directory (``CMakeLists.txt`` file) @@ -357,6 +358,24 @@ The options are: :ref:`Makefile Generators`, :ref:`Visual Studio Generators`, and the :generator:`Xcode` generator. +``DEPENDS_EXPLICIT_ONLY`` + + .. versionadded:: 3.27 + + Indicate that the command's ``DEPENDS`` argument represents all files + required by the command and implicit dependencies are not required. + + Without this option, if any target uses the output of the custom command, + CMake will consider that target's dependencies as implicit dependencies for + the custom command in case this custom command requires files implicitly + created by those targets. + + This option can be enabled on all custom commands by setting + :variable:`CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY` to ``ON``. + + Only the :ref:`Ninja Generators` actually use this information to remove + unnecessary implicit dependencies. + Examples: Generating Files ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst index 53555a4..02dd3986 100644 --- a/Help/command/add_test.rst +++ b/Help/command/add_test.rst @@ -12,13 +12,24 @@ Add a test to the project to be run by :manual:`ctest(1)`. Adds a test called ``<name>``. The test name may contain arbitrary characters, expressed as a :ref:`Quoted Argument` or :ref:`Bracket Argument` -if necessary. See policy :policy:`CMP0110`. The options are: +if necessary. See policy :policy:`CMP0110`. + +CMake only generates tests if the :command:`enable_testing` command has been +invoked. The :module:`CTest` module invokes ``enable_testing`` automatically +unless ``BUILD_TESTING`` is set to ``OFF``. + +Tests added with the ``add_test(NAME)`` signature support using +:manual:`generator expressions <cmake-generator-expressions(7)>` +in test properties set by :command:`set_property(TEST)` or +:command:`set_tests_properties`. Test properties may only be set in the +directory the test is created in. + +``add_test`` options are: ``COMMAND`` - Specify the test command-line. If ``<command>`` specifies an - executable target (created by :command:`add_executable`) it will - automatically be replaced by the location of the executable created - at build time. + Specify the test command-line. If ``<command>`` specifies an executable + target created by :command:`add_executable`, it will automatically be + replaced by the location of the executable created at build time. The command may be specified using :manual:`generator expressions <cmake-generator-expressions(7)>`. @@ -27,38 +38,29 @@ if necessary. See policy :policy:`CMP0110`. The options are: Restrict execution of the test only to the named configurations. ``WORKING_DIRECTORY`` - Set the :prop_test:`WORKING_DIRECTORY` test property to - specify the working directory in which to execute the test. - If not specified the test will be run with the current working - directory set to the build directory corresponding to the - current source directory. - - The working directory may be specified using - :manual:`generator expressions <cmake-generator-expressions(7)>`. + Set the test property :prop_test:`WORKING_DIRECTORY` in which to execute the + test. If not specified, the test will be run in + :variable:`CMAKE_CURRENT_BINARY_DIR`. The working directory may be specified + using :manual:`generator expressions <cmake-generator-expressions(7)>`. ``COMMAND_EXPAND_LISTS`` .. versionadded:: 3.16 - Lists in ``COMMAND`` arguments will be expanded, including those - created with + Lists in ``COMMAND`` arguments will be expanded, including those created with :manual:`generator expressions <cmake-generator-expressions(7)>`. -The given test command is expected to exit with code ``0`` to pass and -non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test -property is set. Any output written to stdout or stderr will be -captured by :manual:`ctest(1)` but does not affect the pass/fail status -unless the :prop_test:`PASS_REGULAR_EXPRESSION`, -:prop_test:`FAIL_REGULAR_EXPRESSION` or -:prop_test:`SKIP_REGULAR_EXPRESSION` test property is used. +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 +written to stdout or stderr is captured by :manual:`ctest(1)` and only +affects the pass/fail status via the :prop_test:`PASS_REGULAR_EXPRESSION`, +:prop_test:`FAIL_REGULAR_EXPRESSION`, or :prop_test:`SKIP_REGULAR_EXPRESSION` +test properties. .. versionadded:: 3.16 Added :prop_test:`SKIP_REGULAR_EXPRESSION` property. -Tests added with the ``add_test(NAME)`` signature support using -:manual:`generator expressions <cmake-generator-expressions(7)>` -in test properties set by :command:`set_property(TEST)` or -:command:`set_tests_properties`. - Example usage: .. code-block:: cmake @@ -71,16 +73,9 @@ This creates a test ``mytest`` whose command runs a ``testDriver`` tool passing the configuration name and the full path to the executable file produced by target ``myexe``. -.. note:: - - CMake will generate tests only if the :command:`enable_testing` - command has been invoked. The :module:`CTest` module invokes the - command automatically unless the ``BUILD_TESTING`` option is turned - ``OFF``. - --------------------------------------------------------------------- -This command also supports a simpler, but less flexible, signature: +The command syntax above is recommended over the older, less flexible form: .. code-block:: cmake diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst index 8801a9f..707568c 100644 --- a/Help/command/cmake_language.rst +++ b/Help/command/cmake_language.rst @@ -27,163 +27,155 @@ those created via the :command:`macro` or :command:`function` commands. Calling Commands ^^^^^^^^^^^^^^^^ -.. _CALL: - -.. code-block:: cmake - +.. signature:: cmake_language(CALL <command> [<arg>...]) -Calls the named ``<command>`` with the given arguments (if any). -For example, the code: + Calls the named ``<command>`` with the given arguments (if any). + For example, the code: -.. code-block:: cmake + .. code-block:: cmake - set(message_command "message") - cmake_language(CALL ${message_command} STATUS "Hello World!") + set(message_command "message") + cmake_language(CALL ${message_command} STATUS "Hello World!") -is equivalent to + is equivalent to -.. code-block:: cmake + .. code-block:: cmake - message(STATUS "Hello World!") + message(STATUS "Hello World!") -.. note:: - To ensure consistency of the code, the following commands are not allowed: + .. note:: + To ensure consistency of the code, the following commands are not allowed: - * ``if`` / ``elseif`` / ``else`` / ``endif`` - * ``block`` / ``endblock`` - * ``while`` / ``endwhile`` - * ``foreach`` / ``endforeach`` - * ``function`` / ``endfunction`` - * ``macro`` / ``endmacro`` + * ``if`` / ``elseif`` / ``else`` / ``endif`` + * ``block`` / ``endblock`` + * ``while`` / ``endwhile`` + * ``foreach`` / ``endforeach`` + * ``function`` / ``endfunction`` + * ``macro`` / ``endmacro`` Evaluating Code ^^^^^^^^^^^^^^^ -.. _EVAL: - -.. code-block:: cmake - +.. signature:: cmake_language(EVAL CODE <code>...) + :target: EVAL -Evaluates the ``<code>...`` as CMake code. + Evaluates the ``<code>...`` as CMake code. -For example, the code: + For example, the code: -.. code-block:: cmake + .. code-block:: cmake - set(A TRUE) - set(B TRUE) - set(C TRUE) - set(condition "(A AND B) OR C") + set(A TRUE) + set(B TRUE) + set(C TRUE) + set(condition "(A AND B) OR C") - cmake_language(EVAL CODE " - if (${condition}) - message(STATUS TRUE) - else() - message(STATUS FALSE) - endif()" - ) + cmake_language(EVAL CODE " + if (${condition}) + message(STATUS TRUE) + else() + message(STATUS FALSE) + endif()" + ) -is equivalent to + is equivalent to -.. code-block:: cmake + .. code-block:: cmake - set(A TRUE) - set(B TRUE) - set(C TRUE) - set(condition "(A AND B) OR C") - - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake " - if (${condition}) - message(STATUS TRUE) - else() - message(STATUS FALSE) - endif()" - ) + set(A TRUE) + set(B TRUE) + set(C TRUE) + set(condition "(A AND B) OR C") - include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake " + if (${condition}) + message(STATUS TRUE) + else() + message(STATUS FALSE) + endif()" + ) + + include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake) Deferring Calls ^^^^^^^^^^^^^^^ .. versionadded:: 3.19 -.. _DEFER: - -.. code-block:: cmake - +.. signature:: cmake_language(DEFER <options>... CALL <command> [<arg>...]) -Schedules a call to the named ``<command>`` with the given arguments (if any) -to occur at a later time. By default, deferred calls are executed as if -written at the end of the current directory's ``CMakeLists.txt`` file, -except that they run even after a :command:`return` call. Variable -references in arguments are evaluated at the time the deferred call is -executed. + Schedules a call to the named ``<command>`` with the given arguments (if any) + to occur at a later time. By default, deferred calls are executed as if + written at the end of the current directory's ``CMakeLists.txt`` file, + except that they run even after a :command:`return` call. Variable + references in arguments are evaluated at the time the deferred call is + executed. -The options are: + The options are: -``DIRECTORY <dir>`` - Schedule the call for the end of the given directory instead of the - current directory. The ``<dir>`` may reference either a source - directory or its corresponding binary directory. Relative paths are - treated as relative to the current source directory. + ``DIRECTORY <dir>`` + Schedule the call for the end of the given directory instead of the + current directory. The ``<dir>`` may reference either a source + directory or its corresponding binary directory. Relative paths are + treated as relative to the current source directory. - The given directory must be known to CMake, being either the top-level - directory or one added by :command:`add_subdirectory`. Furthermore, - the given directory must not yet be finished processing. This means - it can be the current directory or one of its ancestors. + The given directory must be known to CMake, being either the top-level + directory or one added by :command:`add_subdirectory`. Furthermore, + the given directory must not yet be finished processing. This means + it can be the current directory or one of its ancestors. -``ID <id>`` - Specify an identification for the deferred call. - The ``<id>`` may not be empty and may not begin with a capital letter ``A-Z``. - The ``<id>`` may begin with an underscore (``_``) only if it was generated - automatically by an earlier call that used ``ID_VAR`` to get the id. + ``ID <id>`` + Specify an identification for the deferred call. + The ``<id>`` may not be empty and may not begin with a capital letter ``A-Z``. + The ``<id>`` may begin with an underscore (``_``) only if it was generated + automatically by an earlier call that used ``ID_VAR`` to get the id. -``ID_VAR <var>`` - Specify a variable in which to store the identification for the - deferred call. If ``ID <id>`` is not given, a new identification - will be generated and the generated id will start with an underscore (``_``). + ``ID_VAR <var>`` + Specify a variable in which to store the identification for the + deferred call. If ``ID <id>`` is not given, a new identification + will be generated and the generated id will start with an underscore (``_``). -The currently scheduled list of deferred calls may be retrieved: + The currently scheduled list of deferred calls may be retrieved: -.. code-block:: cmake + .. code-block:: cmake - cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>) + cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>) -This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language -Lists>` of deferred call ids. The ids are for the directory scope in which -the calls have been deferred to (i.e. where they will be executed), which can -be different to the scope in which they were created. The ``DIRECTORY`` -option can be used to specify the scope for which to retrieve the call ids. -If that option is not given, the call ids for the current directory scope will -be returned. + This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language + Lists>` of deferred call ids. The ids are for the directory scope in which + the calls have been deferred to (i.e. where they will be executed), which can + be different to the scope in which they were created. The ``DIRECTORY`` + option can be used to specify the scope for which to retrieve the call ids. + If that option is not given, the call ids for the current directory scope + will be returned. -Details of a specific call may be retrieved from its id: + Details of a specific call may be retrieved from its id: -.. code-block:: cmake + .. code-block:: cmake - cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>) + cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>) -This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language -Lists>` in which the first element is the name of the command to be -called, and the remaining elements are its unevaluated arguments (any -contained ``;`` characters are included literally and cannot be distinguished -from multiple arguments). If multiple calls are scheduled with the same id, -this retrieves the first one. If no call is scheduled with the given id in -the specified ``DIRECTORY`` scope (or the current directory scope if no -``DIRECTORY`` option is given), this stores an empty string in the variable. + This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language + Lists>` in which the first element is the name of the command to be + called, and the remaining elements are its unevaluated arguments (any + contained ``;`` characters are included literally and cannot be distinguished + from multiple arguments). If multiple calls are scheduled with the same id, + this retrieves the first one. If no call is scheduled with the given id in + the specified ``DIRECTORY`` scope (or the current directory scope if no + ``DIRECTORY`` option is given), this stores an empty string in the variable. -Deferred calls may be canceled by their id: + Deferred calls may be canceled by their id: -.. code-block:: cmake + .. code-block:: cmake - cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...) + cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...) -This cancels all deferred calls matching any of the given ids in the specified -``DIRECTORY`` scope (or the current directory scope if no ``DIRECTORY`` option -is given). Unknown ids are silently ignored. + This cancels all deferred calls matching any of the given ids in the specified + ``DIRECTORY`` scope (or the current directory scope if no ``DIRECTORY`` option + is given). Unknown ids are silently ignored. Deferred Call Examples """""""""""""""""""""" @@ -229,8 +221,6 @@ also prints:: Deferred Message 1 Deferred Message 2 - -.. _SET_DEPENDENCY_PROVIDER: .. _dependency_providers: Dependency Providers @@ -241,51 +231,50 @@ Dependency Providers .. note:: A high-level introduction to this feature can be found in the :ref:`Using Dependencies Guide <dependency_providers_overview>`. -.. code-block:: cmake - +.. signature:: cmake_language(SET_DEPENDENCY_PROVIDER <command> SUPPORTED_METHODS <methods>...) -When a call is made to :command:`find_package` or -:command:`FetchContent_MakeAvailable`, the call may be forwarded to a -dependency provider which then has the opportunity to fulfill the request. -If the request is for one of the ``<methods>`` specified when the provider -was set, CMake calls the provider's ``<command>`` with a set of -method-specific arguments. If the provider does not fulfill the request, -or if the provider doesn't support the request's method, or no provider -is set, the built-in :command:`find_package` or -:command:`FetchContent_MakeAvailable` implementation is used to fulfill -the request in the usual way. - -One or more of the following values can be specified for the ``<methods>`` -when setting the provider: - -``FIND_PACKAGE`` - The provider command accepts :command:`find_package` requests. - -``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` - The provider command accepts :command:`FetchContent_MakeAvailable` - requests. It expects each dependency to be fed to the provider command - one at a time, not the whole list in one go. - -Only one provider can be set at any point in time. If a provider is already -set when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called, the new -provider replaces the previously set one. The specified ``<command>`` must -already exist when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called. -As a special case, providing an empty string for the ``<command>`` and no -``<methods>`` will discard any previously set provider. - -The dependency provider can only be set while processing one of the files -specified by the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable. -Thus, dependency providers can only be set as part of the first call to -:command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)`` -outside of that context will result in an error. - -.. 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 - list in their :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable, but - the use of such a file should always be the user's choice. + When a call is made to :command:`find_package` or + :command:`FetchContent_MakeAvailable`, the call may be forwarded to a + dependency provider which then has the opportunity to fulfill the request. + If the request is for one of the ``<methods>`` specified when the provider + was set, CMake calls the provider's ``<command>`` with a set of + method-specific arguments. If the provider does not fulfill the request, + or if the provider doesn't support the request's method, or no provider + is set, the built-in :command:`find_package` or + :command:`FetchContent_MakeAvailable` implementation is used to fulfill + the request in the usual way. + + One or more of the following values can be specified for the ``<methods>`` + when setting the provider: + + ``FIND_PACKAGE`` + The provider command accepts :command:`find_package` requests. + + ``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` + The provider command accepts :command:`FetchContent_MakeAvailable` + requests. It expects each dependency to be fed to the provider command + one at a time, not the whole list in one go. + + Only one provider can be set at any point in time. If a provider is already + set when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called, the new + provider replaces the previously set one. The specified ``<command>`` must + already exist when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called. + As a special case, providing an empty string for the ``<command>`` and no + ``<methods>`` will discard any previously set provider. + + The dependency provider can only be set while processing one of the files + specified by the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable. + Thus, dependency providers can only be set as part of the first call to + :command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)`` + outside of that context will result in an error. + + .. 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 + list in their :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable, but + the use of such a file should always be the user's choice. Provider commands """"""""""""""""" @@ -499,23 +488,21 @@ Getting current message log level .. versionadded:: 3.25 -.. _GET_MESSAGE_LOG_LEVEL: .. _query_message_log_level: -.. code-block:: cmake - +.. signature:: cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>) -Writes the current :command:`message` logging level -into the given ``<output_variable>``. + Writes the current :command:`message` logging level + into the given ``<output_variable>``. -See :command:`message` for the possible logging levels. + See :command:`message` for the possible logging levels. -The current message logging level can be set either using the -:option:`--log-level <cmake --log-level>` -command line option of the :manual:`cmake(1)` program or using -the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable. + The current message logging level can be set either using the + :option:`--log-level <cmake --log-level>` + command line option of the :manual:`cmake(1)` program or using + the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable. -If both the command line option and the variable are set, the command line -option takes precedence. If neither are set, the default logging level -is returned. + If both the command line option and the variable are set, the command line + option takes precedence. If neither are set, the default logging level + is returned. diff --git a/Help/command/file.rst b/Help/command/file.rst index df895d0..6ab7421 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -1077,7 +1077,14 @@ Options to both ``DOWNLOAD`` and ``UPLOAD`` are: ``HTTPHEADER <HTTP-header>`` .. versionadded:: 3.7 - HTTP header for operation. Suboption can be repeated several times. + HTTP header for ``DOWNLOAD`` and ``UPLOAD`` operations. ``HTTPHEADER`` can be + repeated for multiple options: + + .. code-block:: cmake + + file(DOWNLOAD <url> + HTTPHEADER "Authorization: Bearer <auth-token>" + HTTPHEADER "UserAgent: Mozilla/5.0") ``NETRC <level>`` .. versionadded:: 3.11 diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index de4cb88..b82088e 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst @@ -367,17 +367,37 @@ The set of installation prefixes is constructed using the following steps. If ``NO_DEFAULT_PATH`` is specified all ``NO_*`` options are enabled. -1. .. versionadded:: 3.12 - Search paths specified in the :variable:`<PackageName>_ROOT` CMake - variable and the :envvar:`<PackageName>_ROOT` environment variable, - where ``<PackageName>`` is the package to be found - (the case-preserved first argument to ``find_package``). - The package root variables are maintained as a stack so if - called from within a find module, root paths from the parent's find - module will also be searched after paths for the current package. - This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting - the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``. - See policy :policy:`CMP0074`. +1. Search prefixes unique to the current ``<PackageName>`` being found. + See policy :policy:`CMP0074`. + + .. versionadded:: 3.12 + + Specifically, search prefixes specified by the following variables, + in order: + + a. :variable:`<PackageName>_ROOT` CMake variable, + where ``<PackageName>`` is the case-preserved package name. + + b. :variable:`<PACKAGENAME>_ROOT` CMake variable, + where ``<PACKAGENAME>`` is the upper-cased package name. + See policy :policy:`CMP0144`. + + .. versionadded:: 3.27 + + c. :envvar:`<PackageName>_ROOT` environment variable, + where ``<PackageName>`` is the case-preserved package name. + + d. :envvar:`<PACKAGENAME>_ROOT` environment variable, + where ``<PACKAGENAME>`` is the upper-cased package name. + See policy :policy:`CMP0144`. + + .. versionadded:: 3.27 + + The package root variables are maintained as a stack so if + called from within a find module, root paths from the parent's find + module will also be searched after paths for the current package. + This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting + the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``. 2. Search paths specified in cmake-specific cache variables. These are intended to be used on the command line with a :option:`-DVAR=VALUE <cmake -D>`. diff --git a/Help/command/install.rst b/Help/command/install.rst index 126888a..d5092ae 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -158,6 +158,9 @@ that may be installed: ``.lib``, in contrast to the ``.dll`` libraries that go to ``RUNTIME``); * On AIX, the *linker import file* created for executables with :prop_tgt:`ENABLE_EXPORTS` enabled. + * On macOS, the *linker import file* created for shared libraries with + :prop_tgt:`ENABLE_EXPORTS` enabled (except when marked as ``FRAMEWORK``, + see below). ``LIBRARY`` Target artifacts of this kind include: @@ -308,6 +311,11 @@ the following additional arguments: value of ``COMPONENT``. It is an error to use this parameter outside of a ``LIBRARY`` block. + .. versionchanged:: 3.27 + This parameter is also usable for an ``ARCHIVE`` block to manage + the linker import file created, on macOS, for shared libraries with + :prop_tgt:`ENABLE_EXPORTS` enabled. + Consider the following example: .. code-block:: cmake @@ -342,6 +350,11 @@ the following additional arguments: option installs nothing. It is an error to use this parameter outside of a ``LIBRARY`` block. + .. versionchanged:: 3.27 + This parameter is also usable for an ``ARCHIVE`` block to manage + the linker import file created, on macOS, for shared libraries with + :prop_tgt:`ENABLE_EXPORTS` enabled. + When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or ``COMPONENT`` may be used to specify the installation component of the namelink, but ``COMPONENT`` should generally be preferred. @@ -355,6 +368,11 @@ the following additional arguments: installs the library. It is an error to use this parameter outside of a ``LIBRARY`` block. + .. versionchanged:: 3.27 + This parameter is also usable for an ``ARCHIVE`` block to manage + the linker import file created, on macOS, for shared libraries with + :prop_tgt:`ENABLE_EXPORTS` enabled. + If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It is not recommended to use ``NAMELINK_SKIP`` in conjunction with ``NAMELINK_COMPONENT``. diff --git a/Help/command/math.rst b/Help/command/math.rst index 8386aab..6989ebc 100644 --- a/Help/command/math.rst +++ b/Help/command/math.rst @@ -9,7 +9,8 @@ Evaluate a mathematical expression. Evaluates a mathematical ``<expression>`` and sets ``<variable>`` to the resulting value. The result of the expression must be representable as a -64-bit signed integer. +64-bit signed integer. Floating point inputs are invalid e.g. ``1.1 * 10``. +Non-integer results e.g. ``3 / 2`` are truncated. The mathematical expression must be given as a string (i.e. enclosed in double quotation marks). An example is ``"5 * (10 + 13)"``. diff --git a/Help/command/separate_arguments.rst b/Help/command/separate_arguments.rst index f66af35..4f0b25e 100644 --- a/Help/command/separate_arguments.rst +++ b/Help/command/separate_arguments.rst @@ -69,7 +69,7 @@ be one of the following keywords: The contents of ``out`` will be: ``/path/to/cc;-c;main.c`` -.. _`Parsing C Command-Line Arguments`: https://msdn.microsoft.com/library/a1y7w461.aspx +.. _`Parsing C Command-Line Arguments`: https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments .. code-block:: cmake diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst index d446a2d..8dd4b94 100644 --- a/Help/command/set_property.rst +++ b/Help/command/set_property.rst @@ -82,15 +82,15 @@ It must be one of the following: to the installation prefix. ``TEST`` - Scope may name zero or more existing tests. - See also the :command:`set_tests_properties` command. + Scope is limited to the directory the command is called in. It may name zero + or more existing tests. See also command :command:`set_tests_properties`. Test property values may be specified using :manual:`generator expressions <cmake-generator-expressions(7)>` for tests created by the :command:`add_test(NAME)` signature. ``CACHE`` - Scope must name zero or more cache existing entries. + Scope must name zero or more existing cache entries. The required ``PROPERTY`` option is immediately followed by the name of the property to set. Remaining arguments are used to compose the diff --git a/Help/command/string.rst b/Help/command/string.rst index c24b9bc..b47fa09 100644 --- a/Help/command/string.rst +++ b/Help/command/string.rst @@ -45,16 +45,16 @@ Synopsis `JSON`_ string(JSON <out-var> [ERROR_VARIABLE <error-var>] - {:ref:`GET <JSON_GET>` | :ref:`TYPE <JSON_TYPE>` | :ref:`LENGTH <JSON_LENGTH>` | :ref:`REMOVE <JSON_REMOVE>`} + {`GET <JSON GET_>`_ | `TYPE <JSON TYPE_>`_ | `LENGTH <JSON LENGTH_>`_ | `REMOVE <JSON REMOVE_>`_} <json-string> <member|index> [<member|index> ...]) string(JSON <out-var> [ERROR_VARIABLE <error-var>] - :ref:`MEMBER <JSON_MEMBER>` <json-string> + `MEMBER <JSON MEMBER_>`_ <json-string> [<member|index> ...] <index>) string(JSON <out-var> [ERROR_VARIABLE <error-var>] - :ref:`SET <JSON_SET>` <json-string> + `SET <JSON SET_>`_ <json-string> <member|index> [<member|index> ...] <value>) string(JSON <out-var> [ERROR_VARIABLE <error-var>] - :ref:`EQUAL <JSON_EQUAL>` <json-string1> <json-string2>) + `EQUAL <JSON EQUAL_>`_ <json-string1> <json-string2>) Search and Replace ^^^^^^^^^^^^^^^^^^ @@ -62,75 +62,60 @@ Search and Replace Search and Replace With Plain Strings """"""""""""""""""""""""""""""""""""" -.. _FIND: - -.. code-block:: cmake - +.. signature:: string(FIND <string> <substring> <output_variable> [REVERSE]) -Return the position where the given ``<substring>`` was found in -the supplied ``<string>``. If the ``REVERSE`` flag was used, the command will -search for the position of the last occurrence of the specified -``<substring>``. If the ``<substring>`` is not found, a position of -1 is -returned. - -The ``string(FIND)`` subcommand treats all strings as ASCII-only characters. -The index stored in ``<output_variable>`` will also be counted in bytes, -so strings containing multi-byte characters may lead to unexpected results. + Return the position where the given ``<substring>`` was found in + the supplied ``<string>``. If the ``REVERSE`` flag was used, the command + will search for the position of the last occurrence of the specified + ``<substring>``. If the ``<substring>`` is not found, a position of -1 is + returned. -.. _REPLACE: - -.. code-block:: cmake + The ``string(FIND)`` subcommand treats all strings as ASCII-only characters. + The index stored in ``<output_variable>`` will also be counted in bytes, + so strings containing multi-byte characters may lead to unexpected results. +.. signature:: string(REPLACE <match_string> <replace_string> <output_variable> <input> [<input>...]) -Replace all occurrences of ``<match_string>`` in the ``<input>`` -with ``<replace_string>`` and store the result in the ``<output_variable>``. + Replace all occurrences of ``<match_string>`` in the ``<input>`` + with ``<replace_string>`` and store the result in the ``<output_variable>``. Search and Replace With Regular Expressions """"""""""""""""""""""""""""""""""""""""""" -.. _`REGEX MATCH`: - -.. code-block:: cmake - +.. signature:: string(REGEX MATCH <regular_expression> <output_variable> <input> [<input>...]) -Match the ``<regular_expression>`` once and store the match in the -``<output_variable>``. -All ``<input>`` arguments are concatenated before matching. -Regular expressions are specified in the subsection just below. - -.. _`REGEX MATCHALL`: - -.. code-block:: cmake + Match the ``<regular_expression>`` once and store the match in the + ``<output_variable>``. + All ``<input>`` arguments are concatenated before matching. + Regular expressions are specified in the subsection just below. +.. signature:: string(REGEX MATCHALL <regular_expression> <output_variable> <input> [<input>...]) -Match the ``<regular_expression>`` as many times as possible and store the -matches in the ``<output_variable>`` as a list. -All ``<input>`` arguments are concatenated before matching. - -.. _`REGEX REPLACE`: - -.. code-block:: cmake + Match the ``<regular_expression>`` as many times as possible and store the + matches in the ``<output_variable>`` as a list. + All ``<input>`` arguments are concatenated before matching. +.. signature:: string(REGEX REPLACE <regular_expression> <replacement_expression> <output_variable> <input> [<input>...]) -Match the ``<regular_expression>`` as many times as possible and substitute -the ``<replacement_expression>`` for the match in the output. -All ``<input>`` arguments are concatenated before matching. + Match the ``<regular_expression>`` as many times as possible and substitute + the ``<replacement_expression>`` for the match in the output. + All ``<input>`` arguments are concatenated before matching. -The ``<replacement_expression>`` may refer to parenthesis-delimited -subexpressions of the match using ``\1``, ``\2``, ..., ``\9``. Note that -two backslashes (``\\1``) are required in CMake code to get a backslash -through argument parsing. + The ``<replacement_expression>`` may refer to parenthesis-delimited + subexpressions of the match using ``\1``, ``\2``, ..., ``\9``. Note that + two backslashes (``\\1``) are required in CMake code to get a backslash + through argument parsing. .. _`Regex Specification`: @@ -201,130 +186,100 @@ newlines, and backslashes (respectively) to pass in a regex. For example: Manipulation ^^^^^^^^^^^^ -.. _APPEND: - -.. code-block:: cmake - +.. signature:: string(APPEND <string_variable> [<input>...]) -.. versionadded:: 3.4 - -Append all the ``<input>`` arguments to the string. + .. versionadded:: 3.4 -.. _PREPEND: - -.. code-block:: cmake + Append all the ``<input>`` arguments to the string. +.. signature:: string(PREPEND <string_variable> [<input>...]) -.. versionadded:: 3.10 - -Prepend all the ``<input>`` arguments to the string. - -.. _CONCAT: + .. versionadded:: 3.10 -.. code-block:: cmake + Prepend all the ``<input>`` arguments to the string. +.. signature:: string(CONCAT <output_variable> [<input>...]) -Concatenate all the ``<input>`` arguments together and store -the result in the named ``<output_variable>``. - -.. _JOIN: - -.. code-block:: cmake + Concatenate all the ``<input>`` arguments together and store + the result in the named ``<output_variable>``. +.. signature:: string(JOIN <glue> <output_variable> [<input>...]) -.. versionadded:: 3.12 - -Join all the ``<input>`` arguments together using the ``<glue>`` -string and store the result in the named ``<output_variable>``. - -To join a list's elements, prefer to use the ``JOIN`` operator -from the :command:`list` command. This allows for the elements to have -special characters like ``;`` in them. + .. versionadded:: 3.12 -.. _TOLOWER: + Join all the ``<input>`` arguments together using the ``<glue>`` + string and store the result in the named ``<output_variable>``. -.. code-block:: cmake + To join a list's elements, prefer to use the ``JOIN`` operator + from the :command:`list` command. This allows for the elements to have + special characters like ``;`` in them. +.. signature:: string(TOLOWER <string> <output_variable>) -Convert ``<string>`` to lower characters. - -.. _TOUPPER: - -.. code-block:: cmake + Convert ``<string>`` to lower characters. +.. signature:: string(TOUPPER <string> <output_variable>) -Convert ``<string>`` to upper characters. - -.. _LENGTH: - -.. code-block:: cmake + Convert ``<string>`` to upper characters. +.. signature:: string(LENGTH <string> <output_variable>) -Store in an ``<output_variable>`` a given string's length in bytes. -Note that this means if ``<string>`` contains multi-byte characters, the -result stored in ``<output_variable>`` will *not* be the number of characters. - -.. _SUBSTRING: - -.. code-block:: cmake + Store in an ``<output_variable>`` a given string's length in bytes. + Note that this means if ``<string>`` contains multi-byte characters, + the result stored in ``<output_variable>`` will *not* be + the number of characters. +.. signature:: string(SUBSTRING <string> <begin> <length> <output_variable>) -Store in an ``<output_variable>`` a substring of a given ``<string>``. If -``<length>`` is ``-1`` the remainder of the string starting at ``<begin>`` -will be returned. - -.. versionchanged:: 3.2 - If ``<string>`` is shorter than ``<length>`` then the end of the string - is used instead. Previous versions of CMake reported an error in this case. - -Both ``<begin>`` and ``<length>`` are counted in bytes, so care must -be exercised if ``<string>`` could contain multi-byte characters. + Store in an ``<output_variable>`` a substring of a given ``<string>``. If + ``<length>`` is ``-1`` the remainder of the string starting at ``<begin>`` + will be returned. -.. _STRIP: + .. versionchanged:: 3.2 + If ``<string>`` is shorter than ``<length>`` + then the end of the string is used instead. + Previous versions of CMake reported an error in this case. -.. code-block:: cmake + Both ``<begin>`` and ``<length>`` are counted in bytes, so care must + be exercised if ``<string>`` could contain multi-byte characters. +.. signature:: string(STRIP <string> <output_variable>) -Store in an ``<output_variable>`` a substring of a given ``<string>`` with -leading and trailing spaces removed. - -.. _GENEX_STRIP: - -.. code-block:: cmake + Store in an ``<output_variable>`` a substring of a given ``<string>`` + with leading and trailing spaces removed. +.. signature:: string(GENEX_STRIP <string> <output_variable>) -.. versionadded:: 3.1 - -Strip any :manual:`generator expressions <cmake-generator-expressions(7)>` -from the input ``<string>`` and store the result in the ``<output_variable>``. - -.. _REPEAT: + .. versionadded:: 3.1 -.. code-block:: cmake + Strip any :manual:`generator expressions <cmake-generator-expressions(7)>` + from the input ``<string>`` and store the result + in the ``<output_variable>``. +.. signature:: string(REPEAT <string> <count> <output_variable>) -.. versionadded:: 3.15 + .. versionadded:: 3.15 -Produce the output string as the input ``<string>`` repeated ``<count>`` times. + Produce the output string as the input ``<string>`` + repeated ``<count>`` times. Comparison ^^^^^^^^^^ .. _COMPARE: -.. code-block:: cmake - +.. signature:: string(COMPARE LESS <string1> <string2> <output_variable>) string(COMPARE GREATER <string1> <string2> <output_variable>) string(COMPARE EQUAL <string1> <string2> <output_variable>) @@ -332,240 +287,217 @@ Comparison string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>) string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>) -Compare the strings and store true or false in the ``<output_variable>``. + Compare the strings and store true or false in the ``<output_variable>``. -.. versionadded:: 3.7 - Added the ``LESS_EQUAL`` and ``GREATER_EQUAL`` options. + .. versionadded:: 3.7 + Added the ``LESS_EQUAL`` and ``GREATER_EQUAL`` options. .. _`Supported Hash Algorithms`: Hashing ^^^^^^^ -.. _`HASH`: - -.. code-block:: cmake - +.. signature:: string(<HASH> <output_variable> <input>) + :target: HASH + + Compute a cryptographic hash of the ``<input>`` string. + The supported ``<HASH>`` algorithm names are: + + ``MD5`` + Message-Digest Algorithm 5, RFC 1321. + ``SHA1`` + US Secure Hash Algorithm 1, RFC 3174. + ``SHA224`` + US Secure Hash Algorithms, RFC 4634. + ``SHA256`` + US Secure Hash Algorithms, RFC 4634. + ``SHA384`` + US Secure Hash Algorithms, RFC 4634. + ``SHA512`` + US Secure Hash Algorithms, RFC 4634. + ``SHA3_224`` + Keccak SHA-3. + ``SHA3_256`` + Keccak SHA-3. + ``SHA3_384`` + Keccak SHA-3. + ``SHA3_512`` + Keccak SHA-3. -Compute a cryptographic hash of the ``<input>`` string. -The supported ``<HASH>`` algorithm names are: - -``MD5`` - Message-Digest Algorithm 5, RFC 1321. -``SHA1`` - US Secure Hash Algorithm 1, RFC 3174. -``SHA224`` - US Secure Hash Algorithms, RFC 4634. -``SHA256`` - US Secure Hash Algorithms, RFC 4634. -``SHA384`` - US Secure Hash Algorithms, RFC 4634. -``SHA512`` - US Secure Hash Algorithms, RFC 4634. -``SHA3_224`` - Keccak SHA-3. -``SHA3_256`` - Keccak SHA-3. -``SHA3_384`` - Keccak SHA-3. -``SHA3_512`` - Keccak SHA-3. - -.. versionadded:: 3.8 - Added the ``SHA3_*`` hash algorithms. + .. versionadded:: 3.8 + Added the ``SHA3_*`` hash algorithms. Generation ^^^^^^^^^^ -.. _ASCII: - -.. code-block:: cmake - +.. signature:: string(ASCII <number> [<number> ...] <output_variable>) -Convert all numbers into corresponding ASCII characters. - -.. _HEX: - -.. code-block:: cmake + Convert all numbers into corresponding ASCII characters. +.. signature:: string(HEX <string> <output_variable>) -.. versionadded:: 3.18 - -Convert each byte in the input ``<string>`` to its hexadecimal representation -and store the concatenated hex digits in the ``<output_variable>``. Letters in -the output (``a`` through ``f``) are in lowercase. + .. versionadded:: 3.18 -.. _CONFIGURE: - -.. code-block:: cmake + Convert each byte in the input ``<string>`` to its hexadecimal representation + and store the concatenated hex digits in the ``<output_variable>``. + Letters in the output (``a`` through ``f``) are in lowercase. +.. signature:: string(CONFIGURE <string> <output_variable> [@ONLY] [ESCAPE_QUOTES]) -Transform a ``<string>`` like :command:`configure_file` transforms a file. - -.. _MAKE_C_IDENTIFIER: - -.. code-block:: cmake + Transform a ``<string>`` like :command:`configure_file` transforms a file. +.. signature:: string(MAKE_C_IDENTIFIER <string> <output_variable>) -Convert each non-alphanumeric character in the input ``<string>`` to an -underscore and store the result in the ``<output_variable>``. If the first -character of the ``<string>`` is a digit, an underscore will also be prepended -to the result. - -.. _RANDOM: - -.. code-block:: cmake + Convert each non-alphanumeric character in the input ``<string>`` to an + underscore and store the result in the ``<output_variable>``. If the first + character of the ``<string>`` is a digit, an underscore will also be + prepended to the result. +.. signature:: string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>] [RANDOM_SEED <seed>] <output_variable>) -Return a random string of given ``<length>`` consisting of -characters from the given ``<alphabet>``. Default length is 5 characters -and default alphabet is all numbers and upper and lower case letters. -If an integer ``RANDOM_SEED`` is given, its value will be used to seed the -random number generator. - -.. _TIMESTAMP: - -.. code-block:: cmake + Return a random string of given ``<length>`` consisting of + characters from the given ``<alphabet>``. Default length is 5 characters + and default alphabet is all numbers and upper and lower case letters. + If an integer ``RANDOM_SEED`` is given, its value will be used to seed the + random number generator. +.. signature:: string(TIMESTAMP <output_variable> [<format_string>] [UTC]) -Write a string representation of the current date -and/or time to the ``<output_variable>``. - -If the command is unable to obtain a timestamp, the ``<output_variable>`` -will be set to the empty string ``""``. + Write a string representation of the current date + and/or time to the ``<output_variable>``. -The optional ``UTC`` flag requests the current date/time representation to -be in Coordinated Universal Time (UTC) rather than local time. + If the command is unable to obtain a timestamp, the ``<output_variable>`` + will be set to the empty string ``""``. -The optional ``<format_string>`` may contain the following format -specifiers: - -``%%`` - .. versionadded:: 3.8 + The optional ``UTC`` flag requests the current date/time representation to + be in Coordinated Universal Time (UTC) rather than local time. - A literal percent sign (%). + The optional ``<format_string>`` may contain the following format + specifiers: -``%d`` - The day of the current month (01-31). + ``%%`` + .. versionadded:: 3.8 -``%H`` - The hour on a 24-hour clock (00-23). + A literal percent sign (%). -``%I`` - The hour on a 12-hour clock (01-12). + ``%d`` + The day of the current month (01-31). -``%j`` - The day of the current year (001-366). + ``%H`` + The hour on a 24-hour clock (00-23). -``%m`` - The month of the current year (01-12). + ``%I`` + The hour on a 12-hour clock (01-12). -``%b`` - .. versionadded:: 3.7 + ``%j`` + The day of the current year (001-366). - Abbreviated month name (e.g. Oct). + ``%m`` + The month of the current year (01-12). -``%B`` - .. versionadded:: 3.10 + ``%b`` + .. versionadded:: 3.7 - Full month name (e.g. October). + Abbreviated month name (e.g. Oct). -``%M`` - The minute of the current hour (00-59). + ``%B`` + .. versionadded:: 3.10 -``%s`` - .. versionadded:: 3.6 + Full month name (e.g. October). - Seconds since midnight (UTC) 1-Jan-1970 (UNIX time). + ``%M`` + The minute of the current hour (00-59). -``%S`` - The second of the current minute. 60 represents a leap second. (00-60) + ``%s`` + .. versionadded:: 3.6 -``%f`` - .. versionadded:: 3.23 + Seconds since midnight (UTC) 1-Jan-1970 (UNIX time). - The microsecond of the current second (000000-999999). + ``%S`` + The second of the current minute. 60 represents a leap second. (00-60) -``%U`` - The week number of the current year (00-53). + ``%f`` + .. versionadded:: 3.23 -``%V`` - .. versionadded:: 3.22 + The microsecond of the current second (000000-999999). - The ISO 8601 week number of the current year (01-53). + ``%U`` + The week number of the current year (00-53). -``%w`` - The day of the current week. 0 is Sunday. (0-6) + ``%V`` + .. versionadded:: 3.22 -``%a`` - .. versionadded:: 3.7 + The ISO 8601 week number of the current year (01-53). - Abbreviated weekday name (e.g. Fri). + ``%w`` + The day of the current week. 0 is Sunday. (0-6) -``%A`` - .. versionadded:: 3.10 + ``%a`` + .. versionadded:: 3.7 - Full weekday name (e.g. Friday). + Abbreviated weekday name (e.g. Fri). -``%y`` - The last two digits of the current year (00-99). + ``%A`` + .. versionadded:: 3.10 -``%Y`` - The current year. + Full weekday name (e.g. Friday). -``%z`` - .. versionadded:: 3.26 + ``%y`` + The last two digits of the current year (00-99). - The offset of the time zone from UTC, in hours and minutes, - with format ``+hhmm`` or ``-hhmm``. + ``%Y`` + The current year. -``%Z`` - .. versionadded:: 3.26 + ``%z`` + .. versionadded:: 3.26 - The time zone name. + The offset of the time zone from UTC, in hours and minutes, + with format ``+hhmm`` or ``-hhmm``. -Unknown format specifiers will be ignored and copied to the output -as-is. + ``%Z`` + .. versionadded:: 3.26 -If no explicit ``<format_string>`` is given, it will default to: + The time zone name. -:: + Unknown format specifiers will be ignored and copied to the output + as-is. - %Y-%m-%dT%H:%M:%S for local time. - %Y-%m-%dT%H:%M:%SZ for UTC. + If no explicit ``<format_string>`` is given, it will default to: -.. versionadded:: 3.8 - If the ``SOURCE_DATE_EPOCH`` environment variable is set, - its value will be used instead of the current time. - See https://reproducible-builds.org/specs/source-date-epoch/ for details. + :: -.. _UUID: + %Y-%m-%dT%H:%M:%S for local time. + %Y-%m-%dT%H:%M:%SZ for UTC. -.. code-block:: cmake + .. versionadded:: 3.8 + If the ``SOURCE_DATE_EPOCH`` environment variable is set, + its value will be used instead of the current time. + See https://reproducible-builds.org/specs/source-date-epoch/ for details. +.. signature:: string(UUID <output_variable> NAMESPACE <namespace> NAME <name> TYPE <MD5|SHA1> [UPPER]) -.. versionadded:: 3.1 + .. versionadded:: 3.1 -Create a universally unique identifier (aka GUID) as per RFC4122 -based on the hash of the combined values of ``<namespace>`` -(which itself has to be a valid UUID) and ``<name>``. -The hash algorithm can be either ``MD5`` (Version 3 UUID) or -``SHA1`` (Version 5 UUID). -A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`` -where each ``x`` represents a lower case hexadecimal character. -Where required, an uppercase representation can be requested -with the optional ``UPPER`` flag. + Create a universally unique identifier (aka GUID) as per RFC4122 + based on the hash of the combined values of ``<namespace>`` + (which itself has to be a valid UUID) and ``<name>``. + The hash algorithm can be either ``MD5`` (Version 3 UUID) or + ``SHA1`` (Version 5 UUID). + A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`` + where each ``x`` represents a lower case hexadecimal character. + Where required, an uppercase representation can be requested + with the optional ``UPPER`` flag. .. _JSON: @@ -586,78 +518,72 @@ Functionality for querying a JSON string. option is not present, a fatal error message is generated. If no error occurs, the ``<error-variable>`` will be set to ``NOTFOUND``. -.. _JSON_GET: -.. code-block:: cmake - +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-variable>] GET <json-string> <member|index> [<member|index> ...]) + :target: JSON GET -Get an element from ``<json-string>`` at the location given -by the list of ``<member|index>`` arguments. -Array and object elements will be returned as a JSON string. -Boolean elements will be returned as ``ON`` or ``OFF``. -Null elements will be returned as an empty string. -Number and string types will be returned as strings. - -.. _JSON_TYPE: -.. code-block:: cmake + Get an element from ``<json-string>`` at the location given + by the list of ``<member|index>`` arguments. + Array and object elements will be returned as a JSON string. + Boolean elements will be returned as ``ON`` or ``OFF``. + Null elements will be returned as an empty string. + Number and string types will be returned as strings. +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-variable>] TYPE <json-string> <member|index> [<member|index> ...]) + :target: JSON TYPE -Get the type of an element in ``<json-string>`` at the location -given by the list of ``<member|index>`` arguments. The ``<out-var>`` -will be set to one of ``NULL``, ``NUMBER``, ``STRING``, ``BOOLEAN``, -``ARRAY``, or ``OBJECT``. - -.. _JSON_MEMBER: -.. code-block:: cmake + Get the type of an element in ``<json-string>`` at the location + given by the list of ``<member|index>`` arguments. The ``<out-var>`` + will be set to one of ``NULL``, ``NUMBER``, ``STRING``, ``BOOLEAN``, + ``ARRAY``, or ``OBJECT``. +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-var>] MEMBER <json-string> [<member|index> ...] <index>) + :target: JSON MEMBER -Get the name of the ``<index>``-th member in ``<json-string>`` at the location -given by the list of ``<member|index>`` arguments. -Requires an element of object type. - -.. _JSON_LENGTH: -.. code-block:: cmake + Get the name of the ``<index>``-th member in ``<json-string>`` + at the location given by the list of ``<member|index>`` arguments. + Requires an element of object type. +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-variable>] LENGTH <json-string> [<member|index> ...]) + :target: JSON LENGTH -Get the length of an element in ``<json-string>`` at the location -given by the list of ``<member|index>`` arguments. -Requires an element of array or object type. - -.. _JSON_REMOVE: -.. code-block:: cmake + Get the length of an element in ``<json-string>`` at the location + given by the list of ``<member|index>`` arguments. + Requires an element of array or object type. +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-variable>] REMOVE <json-string> <member|index> [<member|index> ...]) + :target: JSON REMOVE -Remove an element from ``<json-string>`` at the location -given by the list of ``<member|index>`` arguments. The JSON string -without the removed element will be stored in ``<out-var>``. - -.. _JSON_SET: -.. code-block:: cmake + Remove an element from ``<json-string>`` at the location + given by the list of ``<member|index>`` arguments. The JSON string + without the removed element will be stored in ``<out-var>``. +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-variable>] SET <json-string> <member|index> [<member|index> ...] <value>) + :target: JSON SET -Set an element in ``<json-string>`` at the location -given by the list of ``<member|index>`` arguments to ``<value>``. -The contents of ``<value>`` should be valid JSON. - -.. _JSON_EQUAL: -.. code-block:: cmake + Set an element in ``<json-string>`` at the location + given by the list of ``<member|index>`` arguments to ``<value>``. + The contents of ``<value>`` should be valid JSON. +.. signature:: string(JSON <out-var> [ERROR_VARIABLE <error-var>] EQUAL <json-string1> <json-string2>) + :target: JSON EQUAL -Compare the two JSON objects given by ``<json-string1>`` and ``<json-string2>`` -for equality. The contents of ``<json-string1>`` and ``<json-string2>`` -should be valid JSON. The ``<out-var>`` will be set to a true value if the -JSON objects are considered equal, or a false value otherwise. + Compare the two JSON objects given by ``<json-string1>`` + and ``<json-string2>`` for equality. The contents of ``<json-string1>`` + and ``<json-string2>`` should be valid JSON. The ``<out-var>`` + will be set to a true value if the JSON objects are considered equal, + or a false value otherwise. diff --git a/Help/cpack_gen/nuget.rst b/Help/cpack_gen/nuget.rst index 1f2e762..8ee2816 100644 --- a/Help/cpack_gen/nuget.rst +++ b/Help/cpack_gen/nuget.rst @@ -258,7 +258,7 @@ List of CPack NuGet generator specific variables: .. _nuget.org: https://www.nuget.org .. _version specification: https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges -.. _SPDX license identifier: https://spdx.github.io/spdx-spec/SPDX-license-list -.. _SPDX specification: https://spdx.github.io/spdx-spec/SPDX-license-expressions +.. _SPDX license identifier: https://spdx.org/licenses +.. _SPDX specification: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions .. 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 c880049..af01252 100644 --- a/Help/cpack_gen/wix.rst +++ b/Help/cpack_gen/wix.rst @@ -111,7 +111,7 @@ Windows using WiX. simply provide the name of the culture. If you specify more than one culture identifier in a comma or semicolon delimited list, the first one that is found will be used. You can find a list of supported languages at: - https://wixtoolset.org//documentation/manual/v3/wixui/wixui_localization.html + https://wixtoolset.org/docs/v3/wixui/wixui_localization/ .. variable:: CPACK_WIX_TEMPLATE @@ -319,7 +319,7 @@ Windows using WiX. for using WiX extensions. Each declaration should be in the form name=url, where name is the plain namespace without the usual xmlns: prefix and url is an unquoted namespace url. A list of commonly known WiX schemata can be found here: - https://wixtoolset.org/documentation/manual/v3/xsd/ + https://wixtoolset.org/docs/v3/xsd/ .. variable:: CPACK_WIX_SKIP_WIX_UI_EXTENSION diff --git a/Help/dev/documentation.rst b/Help/dev/documentation.rst index db92022..a340739 100644 --- a/Help/dev/documentation.rst +++ b/Help/dev/documentation.rst @@ -168,46 +168,154 @@ documentation: See the `cmake-variables(7)`_ manual and the `set()`_ command. -Documentation objects in the CMake Domain come from two sources. -First, the CMake extension to Sphinx transforms every document named -with the form ``Help/<type>/<file-name>.rst`` to a domain object with -type ``<type>``. The object name is extracted from the document title, -which is expected to be of the form:: - - <object-name> - ------------- - -and to appear at or near the top of the ``.rst`` file before any other -lines starting in a letter, digit, ``<``, or ``$``. If no such title appears -literally in the ``.rst`` file, the object name is the ``<file-name>``. -If a title does appear, it is expected that ``<file-name>`` is equal -to ``<object-name>`` with any ``<`` and ``>`` characters removed, -or in the case of a ``$<genex-name>`` or ``$<genex-name:...>``, the -``genex-name``. - -Second, the CMake Domain provides directives to define objects inside -other documents: +Documentation objects in the CMake Domain come from two sources: + +1. The CMake extension to Sphinx transforms every document named + with the form ``Help/<type>/<file-name>.rst`` to a domain object with + type ``<type>``. The object name is extracted from the document title, + which is expected to be of the form:: + + <object-name> + ------------- + + and to appear at or near the top of the ``.rst`` file before any other lines + starting in a letter, digit, ``<``, or ``$``. If no such title appears + literally in the ``.rst`` file, the object name is the ``<file-name>``. + If a title does appear, it is expected that ``<file-name>`` is equal + to ``<object-name>`` with any ``<`` and ``>`` characters removed, + or in the case of a ``$<genex-name>`` or ``$<genex-name:...>``, the + ``genex-name``. + +2. `CMake Domain directives`_ may be used in documents to explicitly define + some object types: + + * `command directive`_ + * `envvar directive`_ + * `genex directive`_ + * `variable directive`_ + + Object types for which no directive is available must be defined using + the document transform above. + +CMake Domain Directives +----------------------- + +The CMake Domain provides the following directives. + +``command`` directive +^^^^^^^^^^^^^^^^^^^^^ + +Document a "command" object: .. code-block:: rst - .. command:: <command-name> + .. command:: <command-name> + + This indented block documents <command-name>. - This indented block documents <command-name>. +The directive requires a single argument, the command name. - .. envvar:: <envvar-name> +``envvar`` directive +^^^^^^^^^^^^^^^^^^^^ + +Document an "envvar" object: + +.. code-block:: rst - This indented block documents <envvar-name>. + .. envvar:: <envvar-name> + + This indented block documents <envvar-name>. + +The directive requires a single argument, the environment variable name. + +``genex`` directive +^^^^^^^^^^^^^^^^^^^ + +Document a "genex" object: + +.. code-block:: rst .. genex:: <genex-name> This indented block documents <genex-name>. +The directive requires a single argument, the generator expression name. + +``signature`` directive +^^^^^^^^^^^^^^^^^^^^^^^ + +Document `CMake Command Signatures <Style: CMake Command Signatures_>`_ +within a ``Help/command/<command-name>.rst`` document. + +.. code-block:: rst + + .. signature:: <command-name>(<signature>) + + This indented block documents one or more signatures of a CMake command. + +The ``signature`` directive requires one argument, the signature summary: + +* One or more signatures must immediately follow the ``::``. + The first signature may optionally be placed on the same line. + A blank line following the ``signature`` directive will result in a + documentation generation error: ``1 argument(s) required, 0 supplied``. + +* Signatures may be split across multiple lines, but the final ``)`` of each + signature must be the last character on its line. + +* Blank lines between signatures are not allowed. (Content after a blank line + is treated as part of the description.) + +* Whitespace in signatures is not preserved. To document a complex signature, + abbreviate it in the ``signature`` directive argument and specify the full + signature in a ``code-block`` in the description. + +The ``signature`` directive generates a document-local hyperlink target +for each signature: + +* Default target names are automatically extracted from leading "keyword" + arguments in the signatures, where a keyword is any sequence of + non-space starting with a letter. For example, the signature + ``string(REGEX REPLACE <match-regex> ...)`` generates the target + ``REGEX REPLACE``, similar to ``.. _`REGEX REPLACE`:``. + +* Custom target names may be specified using a ``:target:`` option. + For example: + + .. code-block:: rst + + .. signature:: + cmake_path(GET <path-var> ROOT_NAME <out-var>) + cmake_path(GET <path-var> ROOT_PATH <out-var>) + :target: + GET ROOT_NAME + GET ROOT_PATH + + Provide a custom target name for each signature, one per line. + The first target may optionally be placed on the same line as ``:target:``. + +* If a target name is already in use earlier in the document, no hyperlink + target will be generated. + +* The targets may be referenced from within the same document using + ```REF`_`` or ```TEXT <REF_>`_`` syntax. Like reStructuredText section + headers, the targets do not work with Sphinx ``:ref:`` syntax. + +The directive treats its content as the documentation of the signature(s). +Indent the signature documentation accordingly. + +``variable`` directive +^^^^^^^^^^^^^^^^^^^^^^ + +Document a "variable" object: + +.. code-block:: rst + .. variable:: <variable-name> This indented block documents <variable-name>. -Object types for which no directive is available must be defined using -the first approach above. +The directive requires a single argument, the variable name. .. _`Sphinx Domain`: http://sphinx-doc.org/domains.html .. _`cmake(1)`: https://cmake.org/cmake/help/latest/manual/cmake.1.html @@ -329,11 +437,11 @@ paragraph. Style: CMake Command Signatures ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Command signatures should be marked up as plain literal blocks, not as -cmake ``code-blocks``. - -Signatures are separated from preceding content by a section header. -That is, use: +A ``Help/command/<command-name>.rst`` document defines one ``command`` +object in the `CMake Domain`_, but some commands have multiple signatures. +Use the CMake Domain's `signature directive`_ to document each signature. +Separate signatures from preceding content by a section header. +For example: .. code-block:: rst @@ -342,17 +450,23 @@ That is, use: Normal Libraries ^^^^^^^^^^^^^^^^ - :: - + .. signature:: add_library(<lib> ...) - This signature is used for ... + This signature is used for ... + +Use the following conventions in command signature documentation: + +* Use an angle-bracket ``<placeholder>`` for arguments to be specified + by the caller. Refer to them in prose using + `inline literal <Style: Inline Literals_>`_ syntax. + +* Wrap optional parts with square brackets. + +* Mark repeatable parts with a trailing ellipsis (``...``). -Signatures of commands should wrap optional parts with square brackets, -and should mark list of optional arguments with an ellipsis (``...``). -Elements of the signature which are specified by the user should be -specified with angle brackets, and may be referred to in prose using -``inline-literal`` syntax. +The ``signature`` directive may be used multiple times for different +signatures of the same command. Style: Boolean Constants ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/envvar/PackageName_ROOT.rst b/Help/envvar/PackageName_ROOT.rst index fa8c385..6e9c744 100644 --- a/Help/envvar/PackageName_ROOT.rst +++ b/Help/envvar/PackageName_ROOT.rst @@ -17,3 +17,17 @@ by ``:`` on UNIX or ``;`` on Windows (the same as the ``PATH`` environment variable convention on those platforms). See also the :variable:`<PackageName>_ROOT` CMake variable. + +.. envvar:: <PACKAGENAME>_ROOT + + .. versionadded:: 3.27 + + Calls to :command:`find_package(<PackageName>)` will also search in + prefixes specified by the upper-case ``<PACKAGENAME>_ROOT`` environment + variable. See policy :policy:`CMP0144`. + +.. note:: + + Note that the ``<PackageName>_ROOT`` and ``<PACKAGENAME>_ROOT`` + environment variables are distinct only on platforms that have + case-sensitive environments. diff --git a/Help/generator/CodeBlocks.rst b/Help/generator/CodeBlocks.rst index 85da715..5c48bc9 100644 --- a/Help/generator/CodeBlocks.rst +++ b/Help/generator/CodeBlocks.rst @@ -1,6 +1,12 @@ CodeBlocks ---------- +.. deprecated:: 3.27 + + Support for :ref:`Extra Generators` is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + Generates CodeBlocks project files. Project files for CodeBlocks will be created in the top directory and diff --git a/Help/generator/CodeLite.rst b/Help/generator/CodeLite.rst index 4f276df..faec9b9 100644 --- a/Help/generator/CodeLite.rst +++ b/Help/generator/CodeLite.rst @@ -1,6 +1,12 @@ CodeLite ---------- +.. deprecated:: 3.27 + + Support for :ref:`Extra Generators` is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + Generates CodeLite project files. Project files for CodeLite will be created in the top directory and diff --git a/Help/generator/Eclipse CDT4.rst b/Help/generator/Eclipse CDT4.rst index 634e2b6..9c1610d 100644 --- a/Help/generator/Eclipse CDT4.rst +++ b/Help/generator/Eclipse CDT4.rst @@ -1,6 +1,12 @@ Eclipse CDT4 ------------ +.. deprecated:: 3.27 + + Support for :ref:`Extra Generators` is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + Generates Eclipse CDT 4.0 project files. Project files for Eclipse will be created in the top directory. In diff --git a/Help/generator/Kate.rst b/Help/generator/Kate.rst index 129bf63..11354f2 100644 --- a/Help/generator/Kate.rst +++ b/Help/generator/Kate.rst @@ -1,6 +1,12 @@ Kate ---- +.. deprecated:: 3.27 + + Support for :ref:`Extra Generators` is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + Generates Kate project files. A project file for Kate will be created in the top directory in the top level @@ -22,5 +28,8 @@ This "extra" generator may be specified as: ``Kate - Ninja`` Generate with :generator:`Ninja`. +``Kate - Ninja Multi-Config`` + Generate with :generator:`Ninja Multi-Config`. + ``Kate - Unix Makefiles`` Generate with :generator:`Unix Makefiles`. diff --git a/Help/generator/Sublime Text 2.rst b/Help/generator/Sublime Text 2.rst index 0a07ea9..a45ab08 100644 --- a/Help/generator/Sublime Text 2.rst +++ b/Help/generator/Sublime Text 2.rst @@ -1,6 +1,12 @@ Sublime Text 2 -------------- +.. deprecated:: 3.27 + + Support for :ref:`Extra Generators` is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + Generates Sublime Text 2 project files. Project files for Sublime Text 2 will be created in the top directory diff --git a/Help/generator/Visual Studio 9 2008.rst b/Help/generator/Visual Studio 9 2008.rst index 3434956..816969d 100644 --- a/Help/generator/Visual Studio 9 2008.rst +++ b/Help/generator/Visual Studio 9 2008.rst @@ -1,7 +1,15 @@ Visual Studio 9 2008 -------------------- -Generates Visual Studio 9 2008 project files. +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 12 2013` 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 ^^^^^^^^^^^^^^^^^^ diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst index e198789..3a11a34 100644 --- a/Help/guide/ide-integration/index.rst +++ b/Help/guide/ide-integration/index.rst @@ -145,7 +145,7 @@ The following IDEs support CMake natively: * `VSCode`_ (via a plugin) .. _CLion: https://www.jetbrains.com/clion/ -.. _KDevelop: https://www.kdevelop.org/ +.. _KDevelop: https://kdevelop.org/ .. _QtCreator: https://www.qt.io/product/development-tools .. _Vim: https://www.vim.org/ .. _Visual Studio: https://visualstudio.microsoft.com/ diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst index 3dac68a..37b0668 100644 --- a/Help/guide/tutorial/A Basic Starting Point.rst +++ b/Help/guide/tutorial/A Basic Starting Point.rst @@ -160,7 +160,7 @@ The last command to call for a basic project is :name: CMakeLists.txt-add_executable :language: cmake :start-after: # add the executable - :end-before: # TODO 9: + :end-before: # TODO 3: .. raw:: html @@ -240,7 +240,7 @@ the following: :name: tutorial.cxx-cxx11 :language: c++ :start-after: // convert input to double - :end-before: // TODO 12: + :end-before: // TODO 6: .. raw:: html @@ -265,7 +265,7 @@ add the :variable:`CMAKE_CXX_STANDARD` declarations above the call to :name: CMakeLists.txt-CXX_STANDARD :language: cmake :start-after: # specify the C++ standard - :end-before: # TODO 7: + :end-before: # configure a header file .. raw:: html @@ -375,7 +375,7 @@ specified CMake variables replaced: :name: CMakeLists.txt-configure_file :language: cmake :start-after: # to the source code - :end-before: # TODO 8: + :end-before: # TODO 2: .. raw:: html @@ -420,7 +420,6 @@ be replaced with the corresponding version numbers from the project in :caption: TODO 10: TutorialConfig.h.in :name: TutorialConfig.h.in :language: c++ - :end-before: // TODO 13: .. raw:: html diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst index 6f9714e..08f3eea 100644 --- a/Help/guide/tutorial/Adding Generator Expressions.rst +++ b/Help/guide/tutorial/Adding Generator Expressions.rst @@ -299,7 +299,7 @@ condition. The resulting full code looks like the following: :name: CMakeLists.txt-target_compile_options-genex :language: cmake :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") - :end-before: # should we use our own math functions + :end-before: # configure a header file to pass some of the CMake settings .. raw:: html diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst index b69abd2..b314773 100644 --- a/Help/guide/tutorial/Adding System Introspection.rst +++ b/Help/guide/tutorial/Adding System Introspection.rst @@ -119,7 +119,7 @@ our source code can tell what resources are available. If both ``log`` and :name: MathFunctions/CMakeLists.txt-target_compile_definitions :language: cmake :start-after: # add compile definitions - :end-before: # install libs + :end-before: # state .. raw:: html @@ -136,7 +136,8 @@ Since we may be using ``log`` and ``exp``, we need to modify :caption: TODO 4: MathFunctions/mysqrt.cxx :name: MathFunctions/mysqrt.cxx-include-cmath :language: c++ - :end-before: #include <iostream> + :start-after: #include "mysqrt.h" + :end-before: include <iostream> .. raw:: html diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst index 4aef050..74b7496 100644 --- a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst +++ b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst @@ -100,7 +100,7 @@ follows: :name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE :language: cmake :start-after: # to find MathFunctions.h - :end-before: # TODO 3: Link to + :end-before: option .. raw:: html @@ -119,7 +119,7 @@ safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level :name: CMakeLists.txt-remove-EXTRA_INCLUDES :language: cmake :start-after: # add the MathFunctions library - :end-before: # add the executable + :end-before: # TODO 2: Link to tutorial_compiler_flags .. raw:: html diff --git a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst index b1f9840..9e6311e 100644 --- a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst +++ b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst @@ -23,15 +23,25 @@ The next step is to add the appropriate commands to the then run it as part of the build process. A few commands are needed to accomplish this. -First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for -``MakeTable`` is added as any other executable would be added. +First, in the ``USE_MYMATH`` section of ``MathFunctions/CMakeLists.txt``, +we add an executable for ``MakeTable``. .. literalinclude:: Step9/MathFunctions/CMakeLists.txt :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-add_executable-MakeTable :language: cmake :start-after: # first we add the executable that generates the table - :end-before: # add the command to generate the source code + :end-before: target_link_libraries + +After creating the executable, we add the ``tutorial_compiler_flags`` to our +executable using :command:`target_link_libraries`. + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-link-tutorial-compiler-flags + :language: cmake + :start-after: add_executable + :end-before: # add the command to generate Then we add a custom command that specifies how to produce ``Table.h`` by running MakeTable. @@ -41,7 +51,7 @@ by running MakeTable. :name: MathFunctions/CMakeLists.txt-add_custom_command-Table.h :language: cmake :start-after: # add the command to generate the source code - :end-before: # add the main library + :end-before: # library that just does sqrt Next we have to let CMake know that ``mysqrt.cxx`` depends on the generated file ``Table.h``. This is done by adding the generated ``Table.h`` to the list @@ -51,8 +61,8 @@ of sources for the library MathFunctions. :caption: MathFunctions/CMakeLists.txt :name: MathFunctions/CMakeLists.txt-add_library-Table.h :language: cmake - :start-after: # add the main library - :end-before: # state that anybody linking + :start-after: # library that just does sqrt + :end-before: # state that we depend on We also have to add the current binary directory to the list of include directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``. @@ -62,7 +72,19 @@ directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``. :name: MathFunctions/CMakeLists.txt-target_include_directories-Table.h :language: cmake :start-after: # state that we depend on our bin - :end-before: # install libs + :end-before: target_link_libraries + +As the last thing in our ``USE_MYMATH`` section, we need to link the our +flags onto ``SqrtLibrary`` and then link ``SqrtLibrary`` onto +``MathFunctions``. This makes the resulting ``USE_MYMATH`` section look like +the following: + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-full_USE_MYMATH-section + :language: cmake + :start-after: if (USE_MYMATH) + :end-before: endif() Now let's use the generated table. First, modify ``mysqrt.cxx`` to include ``Table.h``. Next, we can rewrite the ``mysqrt`` function to use the table: diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst index a56c327..2dd731f 100644 --- a/Help/guide/tutorial/Adding a Library.rst +++ b/Help/guide/tutorial/Adding a Library.rst @@ -51,11 +51,13 @@ then use this library instead of the standard square root function provided by the compiler. For this tutorial we will put the library into a subdirectory called -``MathFunctions``. This directory already contains a header file, -``MathFunctions.h``, and a source file ``mysqrt.cxx``. We will not need to -modify either of these files. The source file has one function called +``MathFunctions``. This directory already contains the header files +``MathFunctions.h`` and ``mysqrt.h``. Their respective source files +``MathFunctions.cxx`` and ``mysqrt.cxx`` are also provided. We will not need +to modify any of these files. ``mysqrt.cxx`` has one function called ``mysqrt`` that provides similar functionality to the compiler's ``sqrt`` -function. +function. ``MathFunctions.cxx`` contains one function ``sqrt`` which serves +to hide the implementation details of ``sqrt``. From the ``Help/guide/tutorial/Step2`` directory, start with ``TODO 1`` and complete through ``TODO 6``. @@ -91,10 +93,10 @@ Solution In the ``CMakeLists.txt`` file in the ``MathFunctions`` directory, we create a library target called ``MathFunctions`` with :command:`add_library`. The -source file for the library is passed as an argument to +source files for the library are passed as an argument to :command:`add_library`. This looks like the following line: -.. raw:: html +.. raw:: html/ <details><summary>TODO 1: Click to show/hide answer</summary> @@ -171,36 +173,40 @@ Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``: <details><summary>TODO 5: Click to show/hide answer</summary> -.. code-block:: c++ - :caption: TODO 5 : tutorial.cxx - :name: tutorial.cxx-include_MathFunctions.h - - #include "MathFunctions.h" +.. literalinclude:: Step3/tutorial.cxx + :caption: TODO 5: tutorial.cxx + :name: CMakeLists.txt-include-MathFunctions.h + :language: cmake + :start-after: #include <string> + :end-before: #include "TutorialConfig.h" .. raw:: html </details> -Lastly, replace ``sqrt`` with our library function ``mysqrt``. +Lastly, replace ``sqrt`` with our library function ``mathfunctions::mysqrt``. .. raw:: html <details><summary>TODO 6: Click to show/hide answer</summary> -.. code-block:: c++ - :caption: TODO 6 : tutorial.cxx - :name: tutorial.cxx-call_mysqrt - - const double outputValue = mysqrt(inputValue); +.. literalinclude:: Step3/tutorial.cxx + :caption: TODO 7: tutorial.cxx + :name: CMakeLists.txt-option + :language: cmake + :start-after: const double inputValue = std::stod(argv[1]); + :end-before: std::cout .. raw:: html </details> -Exercise 2 - Making Our Library Optional -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Exercise 2 - Adding an Option +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Now let us make the MathFunctions library optional. While for the tutorial +Now let us add an option in the MathFunctions library to allow developers to +select either the custom square root implementation or the built in standard +implementation. While for the tutorial there really isn't any need to do so, for larger projects this is a common occurrence. @@ -219,28 +225,26 @@ Helpful Resources ----------------- * :command:`if` -* :command:`list` * :command:`option` -* :command:`cmakedefine <configure_file>` +* :command:`target_compile_definitions` Files to Edit ------------- -* ``CMakeLists.txt`` -* ``tutorial.cxx`` -* ``TutorialConfig.h.in`` +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.cxx`` Getting Started --------------- Start with the resulting files from Exercise 1. Complete ``TODO 7`` through -``TODO 13``. +``TODO 9``. First create a variable ``USE_MYMATH`` using the :command:`option` command -in the top-level ``CMakeLists.txt`` file. In that same file, use that option -to determine whether to build and use the ``MathFunctions`` library. +in ``MathFunctions/CMakeLists.txt``. In that same file, use that option +to pass a compile definition to the ``MathFunctions`` library. -Then, update ``tutorial.cxx`` and ``TutorialConfig.h.in`` to use +Then, update ``MathFunctions.cxx`` to redirect compilation based on ``USE_MYMATH``. Build and Run @@ -279,7 +283,7 @@ or ``mysqrt``? Solution -------- -The first step is to add an option to the top-level ``CMakeLists.txt`` file. +The first step is to add an option to ``MathFunctions/CMakeLists.txt``. This option will be displayed in the :manual:`cmake-gui <cmake-gui(1)>` and :manual:`ccmake <ccmake(1)>` with a default value of ``ON`` that can be changed by the user. @@ -288,172 +292,95 @@ changed by the user. <details><summary>TODO 7: Click to show/hide answer</summary> -.. literalinclude:: Step3/CMakeLists.txt - :caption: TODO 7: CMakeLists.txt - :name: CMakeLists.txt-option +.. literalinclude:: Step3/MathFunctions/CMakeLists.txt + :caption: TODO 7: MathFunctions/CMakeLists.txt + :name: CMakeLists.txt-option-library-level :language: cmake :start-after: # should we use our own math functions - :end-before: # configure a header file to pass some of the CMake settings + :end-before: if (USE_MYMATH) .. raw:: html </details> -Next, make building and linking the ``MathFunctions`` library -conditional. - -Start by creating a :command:`list` of the optional library targets for our -project. At the moment, it is just ``MathFunctions``. Let's name our list -``EXTRA_LIBS``. +Next, make building and linking our library with ``mysqrt`` function +conditional using this new option. -Similarly, we need to make a :command:`list` for the optional includes which -we will call ``EXTRA_INCLUDES``. In this list, we will ``APPEND`` the path of -the header file needed for our library. - -Next, create an :command:`if` statement which checks the value of +Create an :command:`if` statement which checks the value of ``USE_MYMATH``. Inside the :command:`if` block, put the -:command:`add_subdirectory` command from Exercise 1 with the additional -:command:`list` commands. - -When ``USE_MYMATH`` is ``ON``, the lists will be generated and will be added to -our project. When ``USE_MYMATH`` is ``OFF``, the lists stay empty. With this -strategy, we allow users to toggle ``USE_MYMATH`` to manipulate what library is -used in the build. - -The top-level CMakeLists.txt file will now look like the following: +:command:`target_compile_definitions` command with the compile +definition ``USE_MYMATH``. .. raw:: html <details><summary>TODO 8: Click to show/hide answer</summary> -.. literalinclude:: Step3/CMakeLists.txt - :caption: TODO 8: CMakeLists.txt +.. literalinclude:: Step3/MathFunctions/CMakeLists.txt + :caption: TODO 8: MathFunctions/CMakeLists.txt :name: CMakeLists.txt-USE_MYMATH :language: cmake - :start-after: # add the MathFunctions library - :end-before: # add the executable + :start-after: USE_MYMATH "Use tutorial provided math implementation" ON) .. raw:: html </details> -Now that we have these two lists, we need to update -:command:`target_link_libraries` and :command:`target_include_directories` to -use them. Changing them is fairly straightforward. - -For :command:`target_link_libraries`, we replace the written out -library names with ``EXTRA_LIBS``. This looks like the following: +The corresponding changes to the source code are fairly straightforward. +In ``MathFunctions.cxx``, we make ``USE_MYMATH`` control which square root +function is used: .. raw:: html <details><summary>TODO 9: Click to show/hide answer</summary> -.. literalinclude:: Step3/CMakeLists.txt - :caption: TODO 9: CMakeLists.txt - :name: CMakeLists.txt-target_link_libraries-EXTRA_LIBS - :language: cmake - :start-after: add_executable(Tutorial tutorial.cxx) - :end-before: # TODO 3 +.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx + :caption: TODO 9: MathFunctions/MathFunctions.cxx + :name: MathFunctions-USE_MYMATH-if + :language: c++ + :start-after: which square root function should we use? + :end-before: } .. raw:: html </details> -Then, we do the same thing with :command:`target_include_directories` and -``EXTRA_INCLUDES``. +Next, we need to include ``mysqrt.h`` if ``USE_MYMATH`` is defined. .. raw:: html <details><summary>TODO 10: Click to show/hide answer</summary> -.. literalinclude:: Step3/CMakeLists.txt - :caption: TODO 10 : CMakeLists.txt - :name: CMakeLists.txt-target_link_libraries-EXTRA_INCLUDES - :language: cmake - :start-after: # so that we will find TutorialConfig.h - -.. raw:: html - - </details> - -Note that this is a classic approach when dealing with many components. We -will cover the modern approach in the Step 3 of the tutorial. - -The corresponding changes to the source code are fairly straightforward. -First, in ``tutorial.cxx``, we include the ``MathFunctions.h`` header if -``USE_MYMATH`` is defined. - -.. raw:: html - - <details><summary>TODO 11: Click to show/hide answer</summary> - -.. literalinclude:: Step3/tutorial.cxx - :caption: TODO 11 : tutorial.cxx - :name: tutorial.cxx-ifdef-include +.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx + :caption: TODO 10: MathFunctions/MathFunctions.cxx + :name: MathFunctions-USE_MYMATH-if-include :language: c++ - :start-after: // should we include the MathFunctions header - :end-before: int main + :start-after: include <cmath> + :end-before: namespace mathfunctions .. raw:: html </details> -Then, in the same file, we make ``USE_MYMATH`` control which square root -function is used: - -.. raw:: html - - <details><summary>TODO 12: Click to show/hide answer</summary> - -.. literalinclude:: Step3/tutorial.cxx - :caption: TODO 12 : tutorial.cxx - :name: tutorial.cxx-ifdef-const - :language: c++ - :start-after: // which square root function should we use? - :end-before: std::cout << "The square root of +Finally, we need to include ``cmath`` now that we are using ``std::sqrt``. .. raw:: html - </details> - -Since the source code now requires ``USE_MYMATH`` we can add it to -``TutorialConfig.h.in`` with the following line: - -.. raw:: html + <details><summary>TODO 11: Click to show/hide answer</summary> - <details><summary>TODO 13: Click to show/hide answer</summary> +.. code-block:: c++ + :caption: TODO 11 : MathFunctions/MathFunctions.cxx + :name: tutorial.cxx-include_cmath -.. literalinclude:: Step3/TutorialConfig.h.in - :caption: TODO 13 : TutorialConfig.h.in - :name: TutorialConfig.h.in-cmakedefine - :language: c++ - :lines: 4 + #include <cmath> .. raw:: html </details> -With these changes, our library is now completely optional to whoever is -building and using it. - -Bonus Question --------------- - -Why is it important that we configure ``TutorialConfig.h.in`` -after the option for ``USE_MYMATH``? What would happen if we inverted the two? - -Answer ------- +When ``USE_MYMATH`` is ``ON``, the compile definition ``USE_MYMATH`` will +be set. We can then use this compile definition to enable or disable +sections of our source code. With this strategy, we allow users to +toggle ``USE_MYMATH`` to manipulate what library is used in the build. -.. raw:: html - - <details><summary>Click to show/hide answer</summary> - -We configure after because ``TutorialConfig.h.in`` uses the value of -``USE_MYMATH``. If we configure the file before -calling :command:`option`, we won't be using the expected value of -``USE_MYMATH``. - -.. raw:: html - - </details> +With these changes, the ``mysqrt`` function is now completely optional to +whoever is building and using the ``MathFunctions`` library. diff --git a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx index 0145300..c0991b9 100644 --- a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx @@ -10,6 +10,7 @@ namespace mathfunctions { double sqrt(double x) { +// which square root function should we use? #ifdef USE_MYMATH return detail::mysqrt(x); #else diff --git a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst index 7befe1d..504e42f 100644 --- a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst +++ b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst @@ -10,49 +10,22 @@ To accomplish this we need to add :variable:`BUILD_SHARED_LIBS` to the top-level ``CMakeLists.txt``. We use the :command:`option` command as it allows users to optionally select if the value should be ``ON`` or ``OFF``. -Next we are going to refactor ``MathFunctions`` to become a real library that -encapsulates using ``mysqrt`` or ``sqrt``, instead of requiring the calling -code to do this logic. This will also mean that ``USE_MYMATH`` will not control -building ``MathFunctions``, but instead will control the behavior of this -library. - -The first step is to update the starting section of the top-level -``CMakeLists.txt`` to look like: - .. literalinclude:: Step11/CMakeLists.txt :caption: CMakeLists.txt :name: CMakeLists.txt-option-BUILD_SHARED_LIBS :language: cmake - :end-before: # add the binary tree - -Now that we have made ``MathFunctions`` always be used, we will need to update -the logic of that library. So, in ``MathFunctions/CMakeLists.txt`` we need to -create a SqrtLibrary that will conditionally be built and installed when -``USE_MYMATH`` is enabled. Now, since this is a tutorial, we are going to -explicitly require that SqrtLibrary is built statically. + :start-after: set(CMAKE_RUNTIME_OUTPUT_DIRECTORY + :end-before: # configure a header file to pass the version number only -The end result is that ``MathFunctions/CMakeLists.txt`` should look like: +Next, we need to specify output directories for our static and shared +libraries. -.. literalinclude:: Step11/MathFunctions/CMakeLists.txt - :caption: MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-add_library-STATIC +.. literalinclude:: Step11/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-cmake-output-directories :language: cmake - :lines: 1-36,42- - -Next, update ``MathFunctions/mysqrt.cxx`` to use the ``mathfunctions`` and -``detail`` namespaces: - -.. literalinclude:: Step11/MathFunctions/mysqrt.cxx - :caption: MathFunctions/mysqrt.cxx - :name: MathFunctions/mysqrt.cxx-namespace - :language: c++ - -We also need to make some changes in ``tutorial.cxx``, so that it no longer -uses ``USE_MYMATH``: - -#. Always include ``MathFunctions.h`` -#. Always use ``mathfunctions::sqrt`` -#. Don't include ``cmath`` + :start-after: # we don't need to tinker with the path to run the executable + :end-before: # configure a header file to pass the version number only Finally, update ``MathFunctions/MathFunctions.h`` to use dll export defines: diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt index 5c661aa..2dd6db5 100644 --- a/Help/guide/tutorial/Step10/CMakeLists.txt +++ b/Help/guide/tutorial/Step10/CMakeLists.txt @@ -16,22 +16,15 @@ target_compile_options(tutorial_compiler_flags INTERFACE "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" ) -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - -# configure a header file to pass some of the CMake settings -# to the source code +# configure a header file to pass the version number only configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags) +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt index fa73321..6f6c430 100644 --- a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt @@ -1,32 +1,51 @@ -# first we add the executable that generates the table -add_executable(MakeTable MakeTable.cxx) - -# add the command to generate the source code -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h - COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h - DEPENDS MakeTable - ) - -# add the main library -add_library(MathFunctions - mysqrt.cxx - ${CMAKE_CURRENT_BINARY_DIR}/Table.h - ) +# add the library that runs +add_library(MathFunctions MathFunctions.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. -# state that we depend on our binary dir to find Table.h target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_CURRENT_BINARY_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) -# link our compiler flags interface library -target_link_libraries(MathFunctions tutorial_compiler_flags) +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if(USE_MYMATH) + + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() install(TARGETS ${installable_libs} DESTINATION lib) # install include headers install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx index 0145300..c0991b9 100644 --- a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx @@ -10,6 +10,7 @@ namespace mathfunctions { double sqrt(double x) { +// which square root function should we use? #ifdef USE_MYMATH return detail::mysqrt(x); #else diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h index cd36bcc..1e916e1 100644 --- a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h @@ -1 +1,3 @@ -double mysqrt(double x); +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx index 7d80ee9..8153f18 100644 --- a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx @@ -5,6 +5,8 @@ // include the generated table #include "Table.h" +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -31,3 +33,5 @@ double mysqrt(double x) return result; } +} +} diff --git a/Help/guide/tutorial/Step10/TutorialConfig.h.in b/Help/guide/tutorial/Step10/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step10/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step10/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step10/tutorial.cxx b/Help/guide/tutorial/Step10/tutorial.cxx index b3c6a4f..37a0333 100644 --- a/Help/guide/tutorial/Step10/tutorial.cxx +++ b/Help/guide/tutorial/Step10/tutorial.cxx @@ -1,15 +1,11 @@ // A simple program that computes the square root of a number -#include <cmath> #include <iostream> +#include <sstream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx index 0145300..c0991b9 100644 --- a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx @@ -10,6 +10,7 @@ namespace mathfunctions { double sqrt(double x) { +// which square root function should we use? #ifdef USE_MYMATH return detail::mysqrt(x); #else diff --git a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx index 0145300..c0991b9 100644 --- a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx @@ -10,6 +10,7 @@ namespace mathfunctions { double sqrt(double x) { +// which square root function should we use? #ifdef USE_MYMATH return detail::mysqrt(x); #else diff --git a/Help/guide/tutorial/Step2/CMakeLists.txt b/Help/guide/tutorial/Step2/CMakeLists.txt index 2b96128..0a06ed7 100644 --- a/Help/guide/tutorial/Step2/CMakeLists.txt +++ b/Help/guide/tutorial/Step2/CMakeLists.txt @@ -7,36 +7,20 @@ project(Tutorial VERSION 1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) -# TODO 7: Create a variable USE_MYMATH using option and set default to ON - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) -# TODO 8: Use list() and APPEND to create a list of optional libraries -# called EXTRA_LIBS and a list of optional include directories called -# EXTRA_INCLUDES. Add the MathFunctions library and source directory to -# the appropriate lists. -# -# Only call add_subdirectory and only add MathFunctions specific values -# to EXTRA_LIBS and EXTRA_INCLUDES if USE_MYMATH is true. - # TODO 2: Use add_subdirectory() to add MathFunctions to this project # add the executable add_executable(Tutorial tutorial.cxx) -# TODO 9: Use EXTRA_LIBS instead of the MathFunctions specific values -# in target_link_libraries. - # TODO 3: Use target_link_libraries to link the library to our executable # TODO 4: Add MathFunctions to Tutorial's target_include_directories() # Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder! -# TODO 10: Use EXTRA_INCLUDES instead of the MathFunctions specific values -# in target_include_directories. - # add the binary tree to the search path for include files # so that we will find TutorialConfig.h target_include_directories(Tutorial PUBLIC diff --git a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt index b7779b7..c468b0e 100644 --- a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt @@ -1,2 +1,7 @@ # TODO 1: Add a library called MathFunctions # Hint: You will need the add_library command + +# TODO 7: Create a variable USE_MYMATH using option and set default to ON + +# TODO 8: If USE_MYMATH is ON, use target_compile_definitions to pass +# USE_MYMATH as a precompiled definition to our source files diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..781d0ec --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx @@ -0,0 +1,15 @@ +#include "MathFunctions.h" + +// TODO 11: include cmath + +// TODO 10: Wrap the mysqrt include in a precompiled ifdef based on USE_MYMATH +#include "mysqrt.h" + +namespace mathfunctions { +double sqrt(double x) +{ + // TODO 9: If USE_MYMATH is defined, use detail::mysqrt. + // Otherwise, use std::sqrt. + return detail::mysqrt(x); +} +} diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx index 1e4d97a..ba0ac64 100644 --- a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx @@ -1,5 +1,9 @@ +#include "mysqrt.h" + #include <iostream> +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -20,3 +24,5 @@ double mysqrt(double x) } return result; } +} +} diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step2/TutorialConfig.h.in b/Help/guide/tutorial/Step2/TutorialConfig.h.in index 6c09e1a..7e4d7fa 100644 --- a/Help/guide/tutorial/Step2/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step2/TutorialConfig.h.in @@ -1,5 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ - -// TODO 13: use cmakedefine to define USE_MYMATH diff --git a/Help/guide/tutorial/Step2/tutorial.cxx b/Help/guide/tutorial/Step2/tutorial.cxx index 87f5e0f..7a2a595 100644 --- a/Help/guide/tutorial/Step2/tutorial.cxx +++ b/Help/guide/tutorial/Step2/tutorial.cxx @@ -3,11 +3,8 @@ #include <iostream> #include <string> -#include "TutorialConfig.h" - -// TODO 11: Only include MathFunctions if USE_MYMATH is defined - // TODO 5: Include MathFunctions.h +#include "TutorialConfig.h" int main(int argc, char* argv[]) { @@ -22,9 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // TODO 12: Use mysqrt if USE_MYMATH is defined and sqrt otherwise - - // TODO 6: Replace sqrt with mysqrt + // TODO 6: Replace sqrt with mathfunctions::sqrt // calculate square root const double outputValue = sqrt(inputValue); diff --git a/Help/guide/tutorial/Step3/CMakeLists.txt b/Help/guide/tutorial/Step3/CMakeLists.txt index 007770a..f051826 100644 --- a/Help/guide/tutorial/Step3/CMakeLists.txt +++ b/Help/guide/tutorial/Step3/CMakeLists.txt @@ -7,9 +7,6 @@ project(Tutorial VERSION 1.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) @@ -17,16 +14,13 @@ configure_file(TutorialConfig.h.in TutorialConfig.h) # TODO 2: Remove EXTRA_INCLUDES list # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) - list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions") -endif() +add_subdirectory(MathFunctions) +list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions") # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS}) +target_link_libraries(Tutorial PUBLIC MathFunctions) # TODO 3: Remove use of EXTRA_INCLUDES diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt index 7bf05e0..90d6c24 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt @@ -1,5 +1,11 @@ -add_library(MathFunctions mysqrt.cxx) +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) # TODO 1: State that anybody linking to MathFunctions needs to include the # current source directory, while MathFunctions itself doesn't. # Hint: Use target_include_directories with the INTERFACE keyword + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx index abe767d..ba0ac64 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx @@ -1,7 +1,9 @@ -#include <iostream> +#include "mysqrt.h" -#include "MathFunctions.h" +#include <iostream> +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -22,3 +24,5 @@ double mysqrt(double x) } return result; } +} +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step3/TutorialConfig.h.in b/Help/guide/tutorial/Step3/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step3/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step3/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step3/tutorial.cxx b/Help/guide/tutorial/Step3/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step3/tutorial.cxx +++ b/Help/guide/tutorial/Step3/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step4/CMakeLists.txt b/Help/guide/tutorial/Step4/CMakeLists.txt index fa4aab2..dcda135 100644 --- a/Help/guide/tutorial/Step4/CMakeLists.txt +++ b/Help/guide/tutorial/Step4/CMakeLists.txt @@ -31,25 +31,19 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # build-tree # Hint: Use BUILD_INTERFACE -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) # TODO 2: Link to tutorial_compiler_flags -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS}) +target_link_libraries(Tutorial PUBLIC MathFunctions) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt index 5f7369c..cc71d86 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt @@ -1,9 +1,15 @@ -add_library(MathFunctions mysqrt.cxx) +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() # TODO 3: Link to tutorial_compiler_flags diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx index abe767d..ba0ac64 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx @@ -1,7 +1,9 @@ -#include <iostream> +#include "mysqrt.h" -#include "MathFunctions.h" +#include <iostream> +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -22,3 +24,5 @@ double mysqrt(double x) } return result; } +} +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step4/TutorialConfig.h.in b/Help/guide/tutorial/Step4/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step4/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step4/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step4/tutorial.cxx b/Help/guide/tutorial/Step4/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step4/tutorial.cxx +++ b/Help/guide/tutorial/Step4/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step5/CMakeLists.txt b/Help/guide/tutorial/Step5/CMakeLists.txt index 279ddf9..ad814f6 100644 --- a/Help/guide/tutorial/Step5/CMakeLists.txt +++ b/Help/guide/tutorial/Step5/CMakeLists.txt @@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" ) -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt index 6cd88d7..000a786 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt @@ -1,13 +1,19 @@ -add_library(MathFunctions mysqrt.cxx) +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() # link our compiler flags interface library -target_link_libraries(MathFunctions tutorial_compiler_flags) +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # TODO 1: Create a variable called installable_libs that is a list of all # libraries we want to install (e.g. MathFunctions and tutorial_compiler_flags) diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx index abe767d..ba0ac64 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx @@ -1,7 +1,9 @@ -#include <iostream> +#include "mysqrt.h" -#include "MathFunctions.h" +#include <iostream> +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -22,3 +24,5 @@ double mysqrt(double x) } return result; } +} +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step5/TutorialConfig.h.in b/Help/guide/tutorial/Step5/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step5/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step5/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step5/tutorial.cxx b/Help/guide/tutorial/Step5/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step5/tutorial.cxx +++ b/Help/guide/tutorial/Step5/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt index c11e307..a86d60a 100644 --- a/Help/guide/tutorial/Step6/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/CMakeLists.txt @@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" ) -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt index b4724c4..623cb74 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt @@ -1,13 +1,19 @@ -add_library(MathFunctions mysqrt.cxx) +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() # link our compiler flags interface library -target_link_libraries(MathFunctions tutorial_compiler_flags) +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs set(installable_libs MathFunctions tutorial_compiler_flags) diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx index abe767d..ba0ac64 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx @@ -1,7 +1,9 @@ -#include <iostream> +#include "mysqrt.h" -#include "MathFunctions.h" +#include <iostream> +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -22,3 +24,5 @@ double mysqrt(double x) } return result; } +} +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step6/TutorialConfig.h.in b/Help/guide/tutorial/Step6/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step6/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step6/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step6/tutorial.cxx b/Help/guide/tutorial/Step6/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step6/tutorial.cxx +++ b/Help/guide/tutorial/Step6/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt index d26a90c..97ec6aa 100644 --- a/Help/guide/tutorial/Step7/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/CMakeLists.txt @@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" ) -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt index e5bdc4d..c0d1e72 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt @@ -1,33 +1,39 @@ -add_library(MathFunctions mysqrt.cxx) +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) -# link our compiler flags interface library -target_link_libraries(MathFunctions tutorial_compiler_flags) +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # TODO 1: Include CheckCXXSourceCompiles -# TODO 1: Include CheckCXXSourceCompiles + # TODO 2: Use check_cxx_source_compiles with simple C++ code to verify + # availability of: + # * std::log + # * std::exp + # Store the results in HAVE_LOG and HAVE_EXP respectively. -# TODO 2: Use check_cxx_source_compiles with simple C++ code to verify -# availability of: -# * std::log -# * std::exp -# Store the results in HAVE_LOG and HAVE_EXP respectively. + # Hint: Sample C++ code which uses log: + # #include <cmath> + # int main() { + # std::log(1.0); + # return 0; + # } -# Hint: Sample C++ code which uses log: -# #include <cmath> -# int main() { -# std::log(1.0); -# return 0; -# } + # TODO 3: Conditionally on HAVE_LOG and HAVE_EXP, add private compile + # definitions "HAVE_LOG" and "HAVE_EXP" to the MathFunctions target. -# TODO 3: Conditionally on HAVE_LOG and HAVE_EXP, add private compile -# definitions "HAVE_LOG" and "HAVE_EXP" to the MathFunctions target. + # Hint: Use target_compile_definitions() +endif() -#Hint: Use target_compile_definitions() +# link our compiler flags interface library +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs set(installable_libs MathFunctions tutorial_compiler_flags) diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx index 3d2492a..9963cff 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx @@ -1,8 +1,9 @@ -#include <iostream> +#include "mysqrt.h" -// TODO 4: include cmath -#include "MathFunctions.h" +#include <iostream> +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -32,3 +33,5 @@ double mysqrt(double x) return result; } +} +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step7/TutorialConfig.h.in b/Help/guide/tutorial/Step7/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step7/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step7/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step7/tutorial.cxx b/Help/guide/tutorial/Step7/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step7/tutorial.cxx +++ b/Help/guide/tutorial/Step7/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt index cb87281..97ec6aa 100644 --- a/Help/guide/tutorial/Step8/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/CMakeLists.txt @@ -16,23 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" ) - -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt index f81b563..861014d 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt @@ -1,36 +1,43 @@ -add_library(MathFunctions mysqrt.cxx) +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # does this system provide the log and exp functions? + include(CheckCXXSourceCompiles) + check_cxx_source_compiles(" + #include <cmath> + int main() { + std::log(1.0); + return 0; + } + " HAVE_LOG) + check_cxx_source_compiles(" + #include <cmath> + int main() { + std::exp(1.0); + return 0; + } + " HAVE_EXP) + + # add compile definitions + if(HAVE_LOG AND HAVE_EXP) + target_compile_definitions(MathFunctions + PRIVATE "HAVE_LOG" "HAVE_EXP" + ) + endif() +endif() # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) # link our compiler flags interface library -target_link_libraries(MathFunctions tutorial_compiler_flags) - -# does this system provide the log and exp functions? -include(CheckCXXSourceCompiles) -check_cxx_source_compiles(" - #include <cmath> - int main() { - std::log(1.0); - return 0; - } -" HAVE_LOG) -check_cxx_source_compiles(" - #include <cmath> - int main() { - std::exp(1.0); - return 0; - } -" HAVE_EXP) - -# add compile definitions -if(HAVE_LOG AND HAVE_EXP) - target_compile_definitions(MathFunctions - PRIVATE "HAVE_LOG" "HAVE_EXP") -endif() +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs set(installable_libs MathFunctions tutorial_compiler_flags) diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx index 7eecd26..28ab042 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx @@ -1,8 +1,10 @@ +#include "mysqrt.h" + #include <cmath> #include <iostream> -#include "MathFunctions.h" - +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -30,3 +32,5 @@ double mysqrt(double x) #endif return result; } +} +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step8/TutorialConfig.h.in b/Help/guide/tutorial/Step8/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step8/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step8/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step8/tutorial.cxx b/Help/guide/tutorial/Step8/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step8/tutorial.cxx +++ b/Help/guide/tutorial/Step8/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt index d26a90c..97ec6aa 100644 --- a/Help/guide/tutorial/Step9/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/CMakeLists.txt @@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" ) -# should we use our own math functions -option(USE_MYMATH "Use tutorial provided math implementation" ON) - # configure a header file to pass some of the CMake settings # to the source code configure_file(TutorialConfig.h.in TutorialConfig.h) # add the MathFunctions library -if(USE_MYMATH) - add_subdirectory(MathFunctions) - list(APPEND EXTRA_LIBS MathFunctions) -endif() +add_subdirectory(MathFunctions) # add the executable add_executable(Tutorial tutorial.cxx) -target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt index 8e04f97..05c8616 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt @@ -1,34 +1,49 @@ -# first we add the executable that generates the table -add_executable(MakeTable MakeTable.cxx) - -# add the command to generate the source code -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h - COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h - DEPENDS MakeTable - ) - -# add the main library -add_library(MathFunctions - mysqrt.cxx - ${CMAKE_CURRENT_BINARY_DIR}/Table.h - ) +add_library(MathFunctions MathFunctions.cxx) # state that anybody linking to us needs to include the current source dir # to find MathFunctions.h, while we don't. -# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the -# TutorialConfig.h include is an implementation detail -# state that we depend on our binary dir to find Table.h target_include_directories(MathFunctions - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_CURRENT_BINARY_DIR} - ) + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) -# link our compiler flags interface library -target_link_libraries(MathFunctions tutorial_compiler_flags) +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() install(TARGETS ${installable_libs} DESTINATION lib) # install include headers install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h index cd36bcc..d5c2f22 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h @@ -1 +1,5 @@ -double mysqrt(double x); +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx index 7d80ee9..477d715 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx +++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx @@ -1,10 +1,12 @@ -#include <iostream> +#include "mysqrt.h" -#include "MathFunctions.h" +#include <iostream> // include the generated table #include "Table.h" +namespace mathfunctions { +namespace detail { // a hack square root calculation using simple operations double mysqrt(double x) { @@ -31,3 +33,5 @@ double mysqrt(double x) return result; } +} +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step9/TutorialConfig.h.in b/Help/guide/tutorial/Step9/TutorialConfig.h.in index e23f521..7e4d7fa 100644 --- a/Help/guide/tutorial/Step9/TutorialConfig.h.in +++ b/Help/guide/tutorial/Step9/TutorialConfig.h.in @@ -1,4 +1,3 @@ // the configured options and settings for Tutorial #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ -#cmakedefine USE_MYMATH diff --git a/Help/guide/tutorial/Step9/tutorial.cxx b/Help/guide/tutorial/Step9/tutorial.cxx index b3c6a4f..a3a2bdc 100644 --- a/Help/guide/tutorial/Step9/tutorial.cxx +++ b/Help/guide/tutorial/Step9/tutorial.cxx @@ -3,13 +3,9 @@ #include <iostream> #include <string> +#include "MathFunctions.h" #include "TutorialConfig.h" -// should we include the MathFunctions header? -#ifdef USE_MYMATH -# include "MathFunctions.h" -#endif - int main(int argc, char* argv[]) { if (argc < 2) { @@ -23,12 +19,7 @@ int main(int argc, char* argv[]) // convert input to double const double inputValue = std::stod(argv[1]); - // which square root function should we use? -#ifdef USE_MYMATH - const double outputValue = mysqrt(inputValue); -#else - const double outputValue = sqrt(inputValue); -#endif + const double outputValue = mathfunctions::sqrt(inputValue); std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; diff --git a/Help/manual/ccmake.1.rst b/Help/manual/ccmake.1.rst index a09857b..5b118d1 100644 --- a/Help/manual/ccmake.1.rst +++ b/Help/manual/ccmake.1.rst @@ -8,6 +8,7 @@ Synopsis .. parsed-literal:: + ccmake [<options>] -B <path-to-build> [-S <path-to-source>] ccmake [<options>] <path-to-source | path-to-existing-build> Description diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst index b9d621b..b88b864 100644 --- a/Help/manual/cmake-buildsystem.7.rst +++ b/Help/manual/cmake-buildsystem.7.rst @@ -37,6 +37,8 @@ is defined as an executable formed by compiling and linking ``zipapp.cpp``. When linking the ``zipapp`` executable, the ``archive`` static library is linked in. +.. _`Binary Executables`: + Binary Executables ------------------ @@ -797,6 +799,10 @@ An *archive* output artifact of a buildsystem target may be: created by the :command:`add_executable` command when its :prop_tgt:`ENABLE_EXPORTS` target property is set. +* On macOS: the linker import file (e.g. ``.tbd``) of a shared library target + created by the :command:`add_library` command with the ``SHARED`` option and + when its :prop_tgt:`ENABLE_EXPORTS` target property is set. + The :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY` and :prop_tgt:`ARCHIVE_OUTPUT_NAME` target properties may be used to control archive output artifact locations and names in the build tree. diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index c3e87d7..a640c13 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -446,16 +446,20 @@ command. All paths are expected to be in cmake-style format. components from a path. See :ref:`Path Structure And Terminology` for the meaning of each path component. + .. versionchanged:: 3.27 + All operations now accept a list of paths as argument. When a list of paths + is specified, the operation will be applied to each path. + :: - $<PATH:GET_ROOT_NAME,path> - $<PATH:GET_ROOT_DIRECTORY,path> - $<PATH:GET_ROOT_PATH,path> - $<PATH:GET_FILENAME,path> - $<PATH:GET_EXTENSION[,LAST_ONLY],path> - $<PATH:GET_STEM[,LAST_ONLY],path> - $<PATH:GET_RELATIVE_PART,path> - $<PATH:GET_PARENT_PATH,path> + $<PATH:GET_ROOT_NAME,path...> + $<PATH:GET_ROOT_DIRECTORY,path...> + $<PATH:GET_ROOT_PATH,path...> + $<PATH:GET_FILENAME,path...> + $<PATH:GET_EXTENSION[,LAST_ONLY],path...> + $<PATH:GET_STEM[,LAST_ONLY],path...> + $<PATH:GET_RELATIVE_PART,path...> + $<PATH:GET_PARENT_PATH,path...> If a requested component is not present in the path, an empty string is returned. @@ -470,9 +474,14 @@ These expressions provide the generation-time capabilities equivalent to the options of the :command:`cmake_path` command. All paths are expected to be in cmake-style format. +.. versionchanged:: 3.27 + All operations now accept a list of paths as argument. When a list of paths + is specified, the operation will be applied to each path. + + .. _GenEx PATH-CMAKE_PATH: -.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path> +.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path...> .. versionadded:: 3.24 @@ -483,7 +492,7 @@ in cmake-style format. When the ``NORMALIZE`` option is specified, the path is :ref:`normalized <Normalization>` after the conversion. -.. genex:: $<PATH:APPEND,path,input,...> +.. genex:: $<PATH:APPEND,path...,input,...> .. versionadded:: 3.24 @@ -493,7 +502,7 @@ in cmake-style format. See :ref:`cmake_path(APPEND) <APPEND>` for more details. -.. genex:: $<PATH:REMOVE_FILENAME,path> +.. genex:: $<PATH:REMOVE_FILENAME,path...> .. versionadded:: 3.24 @@ -503,7 +512,7 @@ in cmake-style format. See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details. -.. genex:: $<PATH:REPLACE_FILENAME,path,input> +.. genex:: $<PATH:REPLACE_FILENAME,path...,input> .. versionadded:: 3.24 @@ -513,7 +522,7 @@ in cmake-style format. See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details. -.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path> +.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path...> .. versionadded:: 3.24 @@ -521,7 +530,7 @@ in cmake-style format. See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details. -.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path,input> +.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path...,input> .. versionadded:: 3.24 @@ -530,14 +539,14 @@ in cmake-style format. See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details. -.. genex:: $<PATH:NORMAL_PATH,path> +.. genex:: $<PATH:NORMAL_PATH,path...> .. versionadded:: 3.24 Returns ``path`` normalized according to the steps described in :ref:`Normalization`. -.. genex:: $<PATH:RELATIVE_PATH,path,base_directory> +.. genex:: $<PATH:RELATIVE_PATH,path...,base_directory> .. versionadded:: 3.24 @@ -547,7 +556,7 @@ in cmake-style format. See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more details. -.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory> +.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path...,base_directory> .. versionadded:: 3.24 @@ -1494,6 +1503,76 @@ In the following, the phrase "the ``tgt`` filename" means the name of the Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on (see policy :policy:`CMP0112`). +.. genex:: $<TARGET_IMPORT_FILE:tgt> + + .. versionadded:: 3.27 + + Full path to the linker import file. On DLL platforms, it would be the + ``.lib`` file. On AIX, for the executables, and on macOS, for the shared + libraries, it could be, respectively, the ``.imp`` or ``.tbd`` import file, + depending of the value of :prop_tgt:`ENABLE_EXPORTS` property. + + An empty string is returned when there is no import file associated with the + target. + +.. genex:: $<TARGET_IMPORT_FILE_BASE_NAME:tgt> + + .. versionadded:: 3.27 + + Base name of file linker import file of the target ``tgt`` without prefix and + suffix. For example, if target file name is ``libbase.tbd``, the base name is + ``base``. + + See also the :prop_tgt:`OUTPUT_NAME` and :prop_tgt:`ARCHIVE_OUTPUT_NAME` + target properties and their configuration specific variants + :prop_tgt:`OUTPUT_NAME_<CONFIG>` and :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`. + + The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target + properties can also be considered. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_IMPORT_FILE_PREFIX:tgt> + + .. versionadded:: 3.27 + + Prefix of the import file of the target ``tgt``. + + See also the :prop_tgt:`IMPORT_PREFIX` target property. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_IMPORT_FILE_SUFFIX:tgt> + + .. versionadded:: 3.27 + + Suffix of the import file of the target ``tgt``. + + The suffix corresponds to the file extension (such as ".lib" or ".tbd"). + + See also the :prop_tgt:`IMPORT_SUFFIX` target property. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_IMPORT_FILE_NAME:tgt> + + .. versionadded:: 3.27 + + Name of the import file of the target target ``tgt``. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_IMPORT_FILE_DIR:tgt> + + Directory of the import file of the target ``tgt``. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + .. genex:: $<TARGET_LINKER_FILE:tgt> File used when linking to the ``tgt`` target. This will usually @@ -1501,13 +1580,22 @@ In the following, the phrase "the ``tgt`` filename" means the name of the but for a shared library on DLL platforms, it would be the ``.lib`` import library associated with the DLL. + .. versionadded:: 3.27 + On macOS, it could be the ``.tbd`` import file associated with the shared + library, depending of the value of :prop_tgt:`ENABLE_EXPORTS` property. + + This generator expression is equivalent to + :genex:`$<TARGET_LINKER_LIBRARY_FILE>` or + :genex:`$<TARGET_LINKER_IMPORT_FILE>` generator expressions, depending of the + characteristics of the target and the platform. + .. genex:: $<TARGET_LINKER_FILE_BASE_NAME:tgt> .. versionadded:: 3.15 Base name of file used to link the target ``tgt``, i.e. - ``$<TARGET_LINKER_FILE_NAME:tgt>`` without prefix and suffix. For example, - if target file name is ``libbase.a``, the base name is ``base``. + :genex:`$<TARGET_LINKER_FILE_NAME:tgt>` without prefix and suffix. For + example, if target file name is ``libbase.a``, the base name is ``base``. See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`, and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration @@ -1561,9 +1649,151 @@ In the following, the phrase "the ``tgt`` filename" means the name of the Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on (see policy :policy:`CMP0112`). +.. genex:: $<TARGET_LINKER_LIBRARY_FILE:tgt> + + .. versionadded:: 3.27 + + File used when linking o the ``tgt`` target is done using directly the + library, and not an import file. This will usually be the library that + ``tgt`` represents (``.a``, ``.so``, ``.dylib``). So, on DLL platforms, it + will be an empty string. + +.. genex:: $<TARGET_LINKER_LIBRARY_FILE_BASE_NAME:tgt> + + .. versionadded:: 3.27 + + Base name of library file used to link the target ``tgt``, i.e. + :genex:`$<TARGET_LINKER_LIBRARY_FILE_NAME:tgt>` without prefix and suffix. + For example, if target file name is ``libbase.a``, the base name is ``base``. + + See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`, + and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration + specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>`, + :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` and + :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>`. + + The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target + properties can also be considered. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_LIBRARY_FILE_PREFIX:tgt> + + .. versionadded:: 3.27 + + Prefix of the library file used to link target ``tgt``. + + See also the :prop_tgt:`PREFIX` target property. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_LIBRARY_FILE_SUFFIX:tgt> + + .. versionadded:: 3.27 + + Suffix of the library file used to link target ``tgt``. + + The suffix corresponds to the file extension (such as ".a" or ".dylib"). + + See also the :prop_tgt:`SUFFIX` target property. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_LIBRARY_FILE_NAME:tgt> + + .. versionadded:: 3.27 + + Name of the library file used to link target ``tgt``. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_LIBRARY_FILE_DIR:tgt> + + .. versionadded:: 3.27 + + Directory of the library file used to link target ``tgt``. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_IMPORT_FILE:tgt> + + .. versionadded:: 3.27 + + File used when linking to the ``tgt`` target is done using an import + file. This will usually be the import file that ``tgt`` represents + (``.lib``, ``.tbd``). So, when no import file is involved in the link step, + an empty string is returned. + +.. genex:: $<TARGET_LINKER_IMPORT_FILE_BASE_NAME:tgt> + + .. versionadded:: 3.27 + + Base name of the import file used to link the target ``tgt``, i.e. + :genex:`$<TARGET_LINKER_IMPORT_FILE_NAME:tgt>` without prefix and suffix. + For example, if target file name is ``libbase.tbd``, the base name is ``base``. + + See also the :prop_tgt:`OUTPUT_NAME` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`, + target properties and their configuration + specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>` and + :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`. + + The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target + properties can also be considered. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_IMPORT_FILE_PREFIX:tgt> + + .. versionadded:: 3.27 + + Prefix of the import file used to link target ``tgt``. + + See also the :prop_tgt:`IMPORT_PREFIX` target property. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_IMPORT_FILE_SUFFIX:tgt> + + .. versionadded:: 3.27 + + Suffix of the import file used to link target ``tgt``. + + The suffix corresponds to the file extension (such as ".lib" or ".tbd"). + + See also the :prop_tgt:`IMPORT_SUFFIX` target property. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_IMPORT_FILE_NAME:tgt> + + .. versionadded:: 3.27 + + Name of the import file used to link target ``tgt``. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_LINKER_IMPORT_FILE_DIR:tgt> + + .. versionadded:: 3.27 + + Directory of the import file used to link target ``tgt``. + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + .. genex:: $<TARGET_SONAME_FILE:tgt> File with soname (``.so.3``) where ``tgt`` is the name of a target. + .. genex:: $<TARGET_SONAME_FILE_NAME:tgt> Name of file with soname (``.so.3``). @@ -1573,11 +1803,35 @@ In the following, the phrase "the ``tgt`` filename" means the name of the .. genex:: $<TARGET_SONAME_FILE_DIR:tgt> - Directory of with soname (``.so.3``). + Directory of file with soname (``.so.3``). Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on (see policy :policy:`CMP0112`). +.. genex:: $<TARGET_SONAME_IMPORT_FILE:tgt> + + .. versionadded:: 3.27 + + Import file with soname (``.3.tbd``) where ``tgt`` is the name of a target. + +.. genex:: $<TARGET_SONAME_IMPORT_FILE_NAME:tgt> + + .. versionadded:: 3.27 + + Name of the import file with soname (``.3.tbd``). + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + +.. genex:: $<TARGET_SONAME_IMPORT_FILE_DIR:tgt> + + .. versionadded:: 3.27 + + Directory of the import file with soname (``.3.tbd``). + + Note that ``tgt`` is not added as a dependency of the target this + expression is evaluated on. + .. genex:: $<TARGET_PDB_FILE:tgt> .. versionadded:: 3.1 @@ -1668,7 +1922,9 @@ In the following, the phrase "the ``tgt`` filename" means the name of the List of DLLs that the target depends on at runtime. This is determined by the locations of all the ``SHARED`` targets in the target's transitive - dependencies. Using this generator expression on targets other than + dependencies. If only the directories of the DLLs are needed, see the + :genex:`TARGET_RUNTIME_DLL_DIRS` generator expression. + Using this generator expression on targets other than executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error. **On non-DLL platforms, this expression always evaluates to an empty string**. @@ -1700,6 +1956,20 @@ On platforms that support runtime paths (``RPATH``), refer to the :prop_tgt:`INSTALL_RPATH` target property. On Apple platforms, refer to the :prop_tgt:`INSTALL_NAME_DIR` target property. +.. genex:: $<TARGET_RUNTIME_DLL_DIRS:tgt> + + .. versionadded:: 3.27 + + List of the directories which contain the DLLs that the target depends on at + runtime (see :genex:`TARGET_RUNTIME_DLLS`). This is determined by + the locations of all the ``SHARED`` targets in the target's transitive + dependencies. Using this generator expression on targets other than + executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error. + **On non-DLL platforms, this expression always evaluates to an empty string**. + + This generator expression can e.g. be used to create a batch file using + :command:`file(GENERATE)` which sets the PATH environment variable accordingly. + Export And Install Expressions ------------------------------ @@ -1725,8 +1995,10 @@ Export And Install Expressions Content of the install prefix when the target is exported via :command:`install(EXPORT)`, or when evaluated in the - :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of - :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise. + :prop_tgt:`INSTALL_NAME_DIR` property, the ``INSTALL_NAME_DIR`` argument of + :command:`install(RUNTIME_DEPENDENCY_SET)`, the code argument of + :command:`install(CODE)`, or the file argument of :command:`install(SCRIPT)`, + and empty otherwise. Multi-level Expression Evaluation --------------------------------- diff --git a/Help/manual/cmake-generators.7.rst b/Help/manual/cmake-generators.7.rst index ed5bbbf..9647f0d 100644 --- a/Help/manual/cmake-generators.7.rst +++ b/Help/manual/cmake-generators.7.rst @@ -107,6 +107,12 @@ Other Generators Extra Generators ================ +.. deprecated:: 3.27 + + Support for "Extra Generators" is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + Some of the `CMake Generators`_ listed in the :manual:`cmake(1)` command-line tool :option:`--help <cmake --help>` output may have variants that specify an extra generator for an auxiliary IDE tool. diff --git a/Help/manual/cmake-gui.1.rst b/Help/manual/cmake-gui.1.rst index cdb860f..26083ca 100644 --- a/Help/manual/cmake-gui.1.rst +++ b/Help/manual/cmake-gui.1.rst @@ -9,9 +9,9 @@ Synopsis .. parsed-literal:: cmake-gui [<options>] + cmake-gui [<options>] -B <path-to-build> [-S <path-to-source>] cmake-gui [<options>] <path-to-source | path-to-existing-build> - cmake-gui [<options>] -S <path-to-source> -B <path-to-build> - cmake-gui [<options>] --browse-manual + cmake-gui [<options>] --browse-manual [<filename>] Description =========== @@ -46,9 +46,11 @@ Options Name of the preset to use from the project's :manual:`presets <cmake-presets(7)>` files, if it has them. -.. option:: --browse-manual +.. option:: --browse-manual [<filename>] - Open the CMake reference manual in a browser and immediately exit. + Open the CMake reference manual in a browser and immediately exit. If + ``<filename>`` is specified, open that file within the reference manual + instead of ``index.html``. .. include:: OPTIONS_HELP.txt diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 22e9eb2..28272ad 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -51,6 +51,15 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used to determine whether to report an error on use of deprecated macros or functions. +Policies Introduced by CMake 3.27 +================================= + +.. toctree:: + :maxdepth: 1 + + CMP0145: The Dart and FindDart modules are removed. </policy/CMP0145> + CMP0144: find_package uses upper-case PACKAGENAME_ROOT variables. </policy/CMP0144> + Policies Introduced by CMake 3.26 ================================= diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index fb84f5a..01c9ce8 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -202,6 +202,7 @@ Properties on Targets /prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY /prop_tgt/DEPRECATION /prop_tgt/DISABLE_PRECOMPILE_HEADERS + /prop_tgt/DLL_NAME_WITH_SOVERSION /prop_tgt/DOTNET_SDK /prop_tgt/DOTNET_TARGET_FRAMEWORK /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 23d8256..99ea564 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -166,6 +166,7 @@ Variables that Change Behavior /variable/BUILD_SHARED_LIBS /variable/CMAKE_ABSOLUTE_DESTINATION_FILES + /variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY /variable/CMAKE_APPBUNDLE_PATH /variable/CMAKE_AUTOMOC_RELAXED_MODE /variable/CMAKE_BACKWARDS_COMPATIBILITY @@ -226,6 +227,8 @@ Variables that Change Behavior /variable/CMAKE_INSTALL_MESSAGE /variable/CMAKE_INSTALL_PREFIX /variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT + /variable/CMAKE_KATE_FILES_MODE + /variable/CMAKE_KATE_MAKE_ARGUMENTS /variable/CMAKE_LIBRARY_PATH /variable/CMAKE_LINK_DIRECTORIES_BEFORE /variable/CMAKE_LINK_LIBRARIES_ONLY_TARGETS @@ -424,7 +427,9 @@ Variables that Control the Build /variable/CMAKE_DEFAULT_CONFIGS /variable/CMAKE_DEPENDS_USE_COMPILER /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS + /variable/CMAKE_DLL_NAME_WITH_SOVERSION /variable/CMAKE_ENABLE_EXPORTS + /variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS /variable/CMAKE_EXE_LINKER_FLAGS /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT @@ -504,6 +509,7 @@ Variables that Control the Build /variable/CMAKE_POSITION_INDEPENDENT_CODE /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY /variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS /variable/CMAKE_SHARED_LINKER_FLAGS /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG /variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG_INIT diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index e48ecd9..1ea7626 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -9,8 +9,8 @@ Synopsis .. parsed-literal:: `Generate a Project Buildsystem`_ + cmake [<options>] -B <path-to-build> [-S <path-to-source>] cmake [<options>] <path-to-source | path-to-existing-build> - cmake [<options>] -S <path-to-source> -B <path-to-build> `Build a Project`_ cmake --build <dir> [<options>] [-- <build-tool-options>] @@ -116,6 +116,20 @@ Generate a Project Buildsystem Run CMake with one of the following command signatures to specify the source and build trees and generate a buildsystem: +``cmake [<options>] -B <path-to-build> [-S <path-to-source>]`` + + .. versionadded:: 3.13 + + Uses ``<path-to-build>`` as the build tree and ``<path-to-source>`` + as the source tree. The specified paths may be absolute or relative + to the current working directory. The source tree must contain a + ``CMakeLists.txt`` file. The build tree will be created automatically + if it does not already exist. For example: + + .. code-block:: console + + $ cmake -S src -B build + ``cmake [<options>] <path-to-source>`` Uses the current working directory as the build tree, and ``<path-to-source>`` as the source tree. The specified path may @@ -141,20 +155,6 @@ source and build trees and generate a buildsystem: $ cd build $ cmake . -``cmake [<options>] -S <path-to-source> -B <path-to-build>`` - - .. versionadded:: 3.13 - - Uses ``<path-to-build>`` as the build tree and ``<path-to-source>`` - as the source tree. The specified paths may be absolute or relative - to the current working directory. The source tree must contain a - ``CMakeLists.txt`` file. The build tree will be created automatically - if it does not already exist. For example: - - .. code-block:: console - - $ cmake -S src -B build - In all cases the ``<options>`` may be zero or more of the `Options`_ below. The above styles for specifying the source and build trees may be mixed. @@ -167,14 +167,14 @@ the current working directory (cwd) is used for the other. For example: ============================== ============ =========== Command Line Source Dir Build Dir ============================== ============ =========== + ``cmake -B build`` `cwd` ``build`` + ``cmake -B build src`` ``src`` ``build`` + ``cmake -B build -S src`` ``src`` ``build`` ``cmake src`` ``src`` `cwd` ``cmake build`` (existing) `loaded` ``build`` ``cmake -S src`` ``src`` `cwd` ``cmake -S src build`` ``src`` ``build`` ``cmake -S src -B build`` ``src`` ``build`` - ``cmake -B build`` `cwd` ``build`` - ``cmake -B build src`` ``src`` ``build`` - ``cmake -B build -S src`` ``src`` ``build`` ============================== ============ =========== .. versionchanged:: 3.23 @@ -781,7 +781,7 @@ Available commands are: (:option:`-A ... <cmake -A>`). The value is a list of platforms known to be supported. ``extraGenerators`` - A list of strings with all the extra generators compatible with + A list of strings with all the :ref:`Extra Generators` compatible with the generator. ``fileApi`` @@ -1080,11 +1080,18 @@ Available commands are: situations instead. Use ``--`` to stop interpreting options and treat all remaining arguments as paths, even if they start with ``-``. -.. option:: sleep <number>... +.. option:: sleep <number> .. versionadded:: 3.0 - Sleep for given number of seconds. + Sleep for ``<number>`` seconds. ``<number>`` may be a floating point number. + A practical minimum is about 0.1 seconds due to overhead in starting/stopping + CMake executable. This can be useful in a CMake script to insert a delay: + + .. code-block:: cmake + + # Sleep for about 0.5 seconds + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 0.5) .. option:: tar [cxt][vf][zjJ] file.tar [<options>] [--] [<pathname>...] @@ -1189,7 +1196,7 @@ Available commands are: .. option:: time <command> [<args>...] - Run command and display elapsed time. + Run ``<command>`` and display elapsed time (including overhead of CMake frontend). .. versionadded:: 3.5 The command now properly passes arguments with spaces or special characters diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index 5e82faa..994ae47 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst @@ -11,7 +11,7 @@ Synopsis .. parsed-literal:: `Run Tests`_ - ctest [<options>] + ctest [<options>] [--test-dir <path-to-build>] `Build and Test Mode`_ ctest --build-and-test <path-to-source> <path-to-build> @@ -354,7 +354,8 @@ Run Tests .. option:: --test-dir <dir> - Specify the directory in which to look for tests. + Specify the directory in which to look for tests, typically a CMake project + build directory. If not specified, the current directory is used. .. option:: --test-output-size-passed <size> diff --git a/Help/policy/CMP0144.rst b/Help/policy/CMP0144.rst new file mode 100644 index 0000000..3959d96 --- /dev/null +++ b/Help/policy/CMP0144.rst @@ -0,0 +1,26 @@ +CMP0144 +------- + +.. versionadded:: 3.27 + +:command:`find_package` uses upper-case ``<PACKAGENAME>_ROOT`` variables. + +In CMake 3.27 and above the :command:`find_package(<PackageName>)` command now +searches prefixes specified by the upper-case :variable:`<PACKAGENAME>_ROOT` +CMake variable and the :envvar:`<PACKAGENAME>_ROOT` environment variable +in addition to the case-preserved :variable:`<PackageName>_ROOT` and +:envvar:`<PackageName>_ROOT` variables used since policy :policy:`CMP0074`. +This policy provides compatibility with projects that have not been +updated to avoid using ``<PACKAGENAME>_ROOT`` variables for other purposes. + +The ``OLD`` behavior for this policy is to ignore ``<PACKAGENAME>_ROOT`` +variables if the original ``<PackageName>`` has lower-case characters. +The ``NEW`` behavior for this policy is to use ``<PACKAGENAME>_ROOT`` +variables. + +This policy was introduced in CMake version 3.27. CMake version +|release| warns when the policy is not set and uses ``OLD`` behavior. +Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` +explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0145.rst b/Help/policy/CMP0145.rst new file mode 100644 index 0000000..bb1c02e --- /dev/null +++ b/Help/policy/CMP0145.rst @@ -0,0 +1,30 @@ +CMP0145 +------- + +.. versionadded:: 3.27 + +The :module:`Dart` and :module:`FindDart` modules are removed. + +These modules were added very early in CMake's development to support +driving tests with a "DART" tool, but DART has not been distributed or +maintained for many years. Projects would ``include(Dart)`` to use it, +and the ``Dart`` module would run ``find_package(Dart)`` internally. +Since :manual:`ctest(1)` was created, the ``Dart`` module has just been +a compatibility shim that finds ``Dart`` to support some legacy +functionality and then forwards to the :module:`CTest` module. + +CMake 3.27 and above prefer to not provide the :module:`Dart` or +:module:`FindDart` modules. This policy provides compatibility for +projects that have not been ported away from them. Projects using the +``Dart`` module should be updated to use the :module:`CTest` module directly. + +The ``OLD`` behavior of this policy is for ``include(Dart)`` and +``find_package(Dart)`` to load the deprecated modules. The ``NEW`` +behavior is for uses of the modules to fail as if they do not exist. + +This policy was introduced in CMake version 3.27. CMake version +|release| warns when the policy is not set and uses ``OLD`` behavior. +Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` +explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/prop_test/WILL_FAIL.rst b/Help/prop_test/WILL_FAIL.rst index f1f94a4..4926f40 100644 --- a/Help/prop_test/WILL_FAIL.rst +++ b/Help/prop_test/WILL_FAIL.rst @@ -3,5 +3,6 @@ WILL_FAIL If set to true, this will invert the pass/fail flag of the test. -This property can be used for tests that are expected to fail and -return a non zero return code. +This property can be used for tests that are expected to fail and return a +non-zero return code. Note that system-level test failures such as segmentation +faults or heap errors will still fail the test even if ``WILL_FALL`` is true. diff --git a/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst b/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst index 677e06d..abb627c 100644 --- a/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst +++ b/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst @@ -6,4 +6,7 @@ ARCHIVE_OUTPUT_DIRECTORY .. |CMAKE_XXX_OUTPUT_DIRECTORY| replace:: :variable:`CMAKE_ARCHIVE_OUTPUT_DIRECTORY` .. include:: XXX_OUTPUT_DIRECTORY.txt +.. |IDEM| replace:: in the same directory +.. include:: MACOS_IMPORT_FILES.txt + See also the :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>` target property. diff --git a/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst b/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst index 6150193..1f1c467 100644 --- a/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst +++ b/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst @@ -5,4 +5,7 @@ ARCHIVE_OUTPUT_NAME .. |xxx| replace:: archive .. include:: XXX_OUTPUT_NAME.txt +.. |IDEM| replace:: with the same name +.. include:: MACOS_IMPORT_FILES.txt + See also the :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` target property. diff --git a/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst b/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst new file mode 100644 index 0000000..59ef00f --- /dev/null +++ b/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst @@ -0,0 +1,17 @@ +DLL_NAME_WITH_SOVERSION +----------------------- + +.. versionadded:: 3.27 + +This property control whether the :prop_tgt:`SOVERSION` target +property are added to the filename of generated DLL filenames +for the Windows platform, which is selected when the +:variable:`WIN32` variable is set. + +The value of the listed property is appended to the +basename of the runtime component of the shared library +target as ``-<SOVERSION>``. + +Please note that setting this property has no effect +if versioned filenames are globally disabled with the +:variable:`CMAKE_PLATFORM_NO_VERSIONED_SONAME` variable. diff --git a/Help/prop_tgt/ENABLE_EXPORTS.rst b/Help/prop_tgt/ENABLE_EXPORTS.rst index 0b1064a..3e9b285 100644 --- a/Help/prop_tgt/ENABLE_EXPORTS.rst +++ b/Help/prop_tgt/ENABLE_EXPORTS.rst @@ -1,7 +1,7 @@ ENABLE_EXPORTS -------------- -Specify whether an executable exports symbols for loadable modules. +Specify whether an executable or a shared library exports symbols. Normally an executable does not export any symbols because it is the final program. It is possible for an executable to export symbols to @@ -28,4 +28,29 @@ varies by platform: automatically bind symbols when the module is loaded. This property is initialized by the value of the variable -:variable:`CMAKE_ENABLE_EXPORTS` if it is set when a target is created. +:variable:`CMAKE_EXECUTABLE_ENABLE_EXPORTS` if it is set when an executable +target is created. + +.. versionadded:: 3.27 + On macOS, to link with a shared library (standard one as well as framework), + a linker import file (e.g. a text-based stubs file, with ``.tbd`` extension) + can be used instead of the shared library itself. + +The generation of these linker import files, as well as the consumption, is +controlled by this property. When this property is set to true, CMake will +generate a ``.tbd`` file for each shared library created by +:command:`add_library` command. This allow other targets to use this ``.tbd`` +file to link to the library with the :command:`target_link_libraries` +command. + +.. note:: + + For compatibility purpose, this property will be ignored if + :prop_tgt:`XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS <XCODE_ATTRIBUTE_<an-attribute>>` + target property or the + :variable:`CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS <CMAKE_XCODE_ATTRIBUTE_<an-attribute>>` + variable is set to ``NO``. + +This property is initialized by the value of the variable +:variable:`CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS` if it is set when a shared +library target is created. diff --git a/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst b/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst index 6de1baa..a4746d3 100644 --- a/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst +++ b/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst @@ -1,11 +1,22 @@ IMPORTED_CONFIGURATIONS ----------------------- -Configurations provided for an IMPORTED target. - -Set this to the list of configuration names available for an IMPORTED -target. The names correspond to configurations defined in the project -from which the target is imported. If the importing project uses a -different set of configurations the names may be mapped using the -MAP_IMPORTED_CONFIG_<CONFIG> property. Ignored for non-imported -targets. +Configurations provided for an :ref:`imported target <Imported targets>`. + +Set this to the list of configuration names available for an imported +target. For each configuration named, the imported target's artifacts +must be specified in other target properties: + +* :prop_tgt:`IMPORTED_LOCATION_<CONFIG>`, or +* :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>` (on DLL platforms, on AIX for + :ref:`Executables <Binary Executables>` or on Apple for + :ref:`Shared Libraries <Normal Libraries>`), or +* :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>` (for :ref:`Object Libraries`), or +* :prop_tgt:`IMPORTED_LIBNAME_<CONFIG>` (for :ref:`Interface Libraries`). + +The configuration names correspond to those defined in the project from +which the target is imported. If the importing project uses a different +set of configurations, the names may be mapped using the +:prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` target property. + +The ``IMPORTED_CONFIGURATIONS`` property is ignored for non-imported targets. diff --git a/Help/prop_tgt/IMPORTED_IMPLIB.rst b/Help/prop_tgt/IMPORTED_IMPLIB.rst index c8b6fde..e67acba 100644 --- a/Help/prop_tgt/IMPORTED_IMPLIB.rst +++ b/Help/prop_tgt/IMPORTED_IMPLIB.rst @@ -3,7 +3,22 @@ IMPORTED_IMPLIB Full path to the import library for an ``IMPORTED`` target. -Set this to the location of the ``.lib`` part of a Windows DLL, or on -AIX set it to an import file created for executables that export symbols -(see the :prop_tgt:`ENABLE_EXPORTS` target property). -Ignored for non-imported targets. +This property may be set: + +* On DLL platforms, to the location of the ``.lib`` part of the DLL. +* On AIX, to an import file (e.g. ``.imp``) created for executables that export + symbols (see the :prop_tgt:`ENABLE_EXPORTS` target property). +* On macOS, to an import file (e.g. ``.tbd``) created for shared libraries (see + the :prop_tgt:`ENABLE_EXPORTS` target property). For frameworks this is the + location of the ``.tbd`` file symlink just inside the framework folder. + +The ``IMPORTED_IMPLIB`` target property may be overridden for a +given configuration ``<CONFIG>`` by the configuration-specific +:prop_tgt:`IMPORTED_IMPLIB_<CONFIG>` target property. Furthermore, +the :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` target property may be +used to map between a project's configurations and those of an imported +target. If none of these is set then the name of any other configuration +listed in the :prop_tgt:`IMPORTED_CONFIGURATIONS` target property may be +selected and its :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>` value used. + +This property is ignored for non-imported targets. diff --git a/Help/prop_tgt/IMPORTED_LOCATION.rst b/Help/prop_tgt/IMPORTED_LOCATION.rst index ddd910a..a7207d8 100644 --- a/Help/prop_tgt/IMPORTED_LOCATION.rst +++ b/Help/prop_tgt/IMPORTED_LOCATION.rst @@ -27,5 +27,5 @@ selected and its :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` value used. To get the location of an imported target read one of the :prop_tgt:`LOCATION` or ``LOCATION_<CONFIG>`` properties. -For platforms with import libraries (e.g. Windows) see also +For platforms with import libraries (e.g. Windows, AIX or macOS) see also :prop_tgt:`IMPORTED_IMPLIB`. diff --git a/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst b/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst index f6ca5ad..d39ec20 100644 --- a/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst +++ b/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst @@ -14,3 +14,8 @@ arguments to the tool. This is useful for tools such as static analyzers. This property is initialized by the value of the :variable:`CMAKE_<LANG>_LINKER_LAUNCHER` variable if it is set when a target is created. + +.. versionadded:: 3.27 + + The property value may use + :manual:`generator expressions <cmake-generator-expressions(7)>`. diff --git a/Help/prop_tgt/MACOS_IMPORT_FILES.txt b/Help/prop_tgt/MACOS_IMPORT_FILES.txt new file mode 100644 index 0000000..3c98fc8 --- /dev/null +++ b/Help/prop_tgt/MACOS_IMPORT_FILES.txt @@ -0,0 +1,12 @@ +.. note:: + + On macOS, this property will be ignored for the linker import files (e.g. + ``.tbd`` files, see :prop_tgt:`ENABLE_EXPORTS` property for details) when: + + * The :prop_tgt:`FRAMEWORK` is set, because the framework layout cannot be + changed. + * The :generator:`Xcode` generator is used, due to the limitations and + constraints of the ``Xcode`` tool. + + In both cases, the linker import files will be generated |IDEM| as the shared + library. diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst new file mode 100644 index 0000000..e4cc01e --- /dev/null +++ b/Help/release/dev/0-sample-topic.rst @@ -0,0 +1,7 @@ +0-sample-topic +-------------- + +* This is a sample release note for the change in a topic. + Developers should add similar notes for each topic branch + making a noteworthy change. Each document should be named + and titled to match the topic name to avoid merge conflicts. diff --git a/Help/release/dev/Apple-tbd-files-management.rst b/Help/release/dev/Apple-tbd-files-management.rst new file mode 100644 index 0000000..edcfe55 --- /dev/null +++ b/Help/release/dev/Apple-tbd-files-management.rst @@ -0,0 +1,6 @@ +Apple-tbd-files-management +-------------------------- + +* Support for text-based stubs (i.e. ``.tbd`` files) was added on macOS + platform. This capability is managed through the :prop_tgt:`ENABLE_EXPORTS` + property. diff --git a/Help/release/dev/PATH-genex-support-list.rst b/Help/release/dev/PATH-genex-support-list.rst new file mode 100644 index 0000000..ce87fdd --- /dev/null +++ b/Help/release/dev/PATH-genex-support-list.rst @@ -0,0 +1,5 @@ +PATH-genex-supports-list +------------------------ + +* The :genex:`$<PATH>` generator expression learned to process list of paths + for decomposition and transformation operations. diff --git a/Help/release/dev/deprecate-extra-generators.rst b/Help/release/dev/deprecate-extra-generators.rst new file mode 100644 index 0000000..ceb2f4e --- /dev/null +++ b/Help/release/dev/deprecate-extra-generators.rst @@ -0,0 +1,5 @@ +deprecate-extra-generators +-------------------------- + +* The :ref:`Extra Generators` have been deprecated. IDEs may use the + :manual:`cmake-file-api(7)` to view CMake-generated project build trees. diff --git a/Help/release/dev/deprecate-policy-old.rst b/Help/release/dev/deprecate-policy-old.rst new file mode 100644 index 0000000..9c38866 --- /dev/null +++ b/Help/release/dev/deprecate-policy-old.rst @@ -0,0 +1,7 @@ +deprecate-policy-old +-------------------- + +* Compatibility with versions of CMake older than 3.5 is now deprecated + and will be removed from a future version. Calls to + :command:`cmake_minimum_required` or :command:`cmake_policy` that set + the policy version to an older value now issue a deprecation diagnostic. diff --git a/Help/release/dev/dll-name-soversion.rst b/Help/release/dev/dll-name-soversion.rst new file mode 100644 index 0000000..56d0842 --- /dev/null +++ b/Help/release/dev/dll-name-soversion.rst @@ -0,0 +1,7 @@ +dll-name-soversion +------------------ + +* The :variable:`CMAKE_DLL_NAME_WITH_SOVERSION` variable and associated + :prop_tgt:`DLL_NAME_WITH_SOVERSION` target property were added to + optionally append the :prop_tgt:`SOVERSION` to the filename of the + ``.dll`` part of a shared library on Windows. diff --git a/Help/release/dev/find_package-PACKAGENAME_ROOT.rst b/Help/release/dev/find_package-PACKAGENAME_ROOT.rst new file mode 100644 index 0000000..0388271 --- /dev/null +++ b/Help/release/dev/find_package-PACKAGENAME_ROOT.rst @@ -0,0 +1,7 @@ +find_package-PACKAGENAME_ROOT +----------------------------- + +* The :command:`find_package` command now searches prefixes specified by + upper-case :variable:`<PACKAGENAME>_ROOT` CMake variables and upper-case + :envvar:`<PACKAGENAME>_ROOT` environment variables. + See policy :policy:`CMP0144`. diff --git a/Help/release/dev/install-prefix-genex-install-code-script.rst b/Help/release/dev/install-prefix-genex-install-code-script.rst new file mode 100644 index 0000000..810f448 --- /dev/null +++ b/Help/release/dev/install-prefix-genex-install-code-script.rst @@ -0,0 +1,5 @@ +install-prefix-genex-install-code-script +---------------------------------------- + +* The :command:`install(CODE)` and :command:`install(SCRIPT)` commands + now support the :genex:`$<INSTALL_PREFIX>` generator expression. diff --git a/Help/release/dev/lang-linker-launcher-genex.rst b/Help/release/dev/lang-linker-launcher-genex.rst new file mode 100644 index 0000000..b6494eb --- /dev/null +++ b/Help/release/dev/lang-linker-launcher-genex.rst @@ -0,0 +1,5 @@ +lang-linker-launcher-genex +-------------------------- + +* The :prop_tgt:`<LANG>_LINKER_LAUNCHER` target property now supports + :manual:`generator expressions <cmake-generator-expressions(7)>`. diff --git a/Help/release/dev/ninja-custom-command-depends.rst b/Help/release/dev/ninja-custom-command-depends.rst new file mode 100644 index 0000000..0b7840c --- /dev/null +++ b/Help/release/dev/ninja-custom-command-depends.rst @@ -0,0 +1,11 @@ +ninja-custom-command-depends +---------------------------- + +* The :command:`add_custom_command` command gained a new + ``DEPENDS_EXPLICIT_ONLY`` option to tell the :ref:`Ninja Generators` + not to add any dependencies implied by the target to which it is + attached. + +* The :variable:`CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY` variable can + be set to enable ``DEPENDS_EXPLICIT_ONLY`` in all uses of + :command:`add_custom_command` command. diff --git a/Help/release/dev/remove-dart-modules.rst b/Help/release/dev/remove-dart-modules.rst new file mode 100644 index 0000000..5da2eda --- /dev/null +++ b/Help/release/dev/remove-dart-modules.rst @@ -0,0 +1,5 @@ +remove-dart-modules +------------------- + +* The :module:`Dart` and :module:`FindDart` modules have been deprecated via + policy :policy:`CMP0145`. Port projects to the :module:`CTest` module. diff --git a/Help/release/dev/vs9-deprecate.rst b/Help/release/dev/vs9-deprecate.rst new file mode 100644 index 0000000..46568f8 --- /dev/null +++ b/Help/release/dev/vs9-deprecate.rst @@ -0,0 +1,5 @@ +vs9-deprecate +------------- + +* The :generator:`Visual Studio 9 2008` generator is now deprecated + and will be removed in a future version of CMake. diff --git a/Help/release/index.rst b/Help/release/index.rst index c82889f..d434a3a 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst @@ -7,6 +7,8 @@ CMake Release Notes This file should include the adjacent "dev.txt" file in development versions but not in release versions. +.. include:: dev.txt + Releases ======== diff --git a/Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst b/Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst new file mode 100644 index 0000000..9c9bd2c --- /dev/null +++ b/Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst @@ -0,0 +1,11 @@ +CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY +---------------------------------------------- + +.. versionadded:: 3.27 + +Whether to enable DEPENDS_EXPLICIT_ONLY option by default in +:command:`add_custom_command`. + +This variable affects the default behavior of the :command:`add_custom_command` +command. Setting this variable to ``ON`` is equivalent to using the ``DEPENDS_EXPLICIT_ONLY`` +option in all uses of that command. diff --git a/Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst b/Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst new file mode 100644 index 0000000..5fa49de --- /dev/null +++ b/Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst @@ -0,0 +1,14 @@ +CMAKE_DLL_NAME_WITH_SOVERSION +----------------------------- + +.. versionadded:: 3.27 + +This variable is used to initialize the :prop_tgt:`DLL_NAME_WITH_SOVERSION` +property on shared library targets for the Windows platform, which is selected +when the :variable:`WIN32` variable is set. + +See this target property for additional information. + +Please note that setting this variable has no effect if versioned filenames +are globally disabled with the :variable:`CMAKE_PLATFORM_NO_VERSIONED_SONAME` +variable. diff --git a/Help/variable/CMAKE_EDIT_COMMAND.rst b/Help/variable/CMAKE_EDIT_COMMAND.rst index 2f4ab1f..b21434f 100644 --- a/Help/variable/CMAKE_EDIT_COMMAND.rst +++ b/Help/variable/CMAKE_EDIT_COMMAND.rst @@ -2,7 +2,8 @@ CMAKE_EDIT_COMMAND ------------------ Full path to :manual:`cmake-gui(1)` or :manual:`ccmake(1)`. Defined only for -:ref:`Makefile Generators` when not using an "extra" generator for an IDE. +:ref:`Makefile Generators` and :ref:`Ninja Generators` when not using any +:ref:`Extra Generators`. This is the full path to the CMake executable that can graphically edit the cache. For example, :manual:`cmake-gui(1)` or :manual:`ccmake(1)`. diff --git a/Help/variable/CMAKE_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_ENABLE_EXPORTS.rst index 9f43de3..d2c5ed0 100644 --- a/Help/variable/CMAKE_ENABLE_EXPORTS.rst +++ b/Help/variable/CMAKE_ENABLE_EXPORTS.rst @@ -8,3 +8,7 @@ Specify whether executables export symbols for loadable modules. This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target property for executable targets when they are created by calls to the :command:`add_executable` command. See the property documentation for details. + +This command has been superseded by the +:variable:`CMAKE_EXECUTABLE_ENABLE_EXPORTS` command. It is provided for +compatibility with older CMake code. diff --git a/Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst new file mode 100644 index 0000000..aa6dda2 --- /dev/null +++ b/Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst @@ -0,0 +1,12 @@ +CMAKE_EXECUTABLE_ENABLE_EXPORTS +------------------------------- + +.. versionadded:: 3.27 + +Specify whether executables export symbols for loadable modules. + +This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target +property for executable targets when they are created by calls to the +:command:`add_executable` command. See the property documentation for details. + +This variable supersede the :variable:`CMAKE_ENABLE_EXPORTS` variable. diff --git a/Help/variable/CMAKE_EXTRA_GENERATOR.rst b/Help/variable/CMAKE_EXTRA_GENERATOR.rst index 2c92323..0a113a5 100644 --- a/Help/variable/CMAKE_EXTRA_GENERATOR.rst +++ b/Help/variable/CMAKE_EXTRA_GENERATOR.rst @@ -1,6 +1,12 @@ CMAKE_EXTRA_GENERATOR --------------------- +.. deprecated:: 3.27 + + Support for :ref:`Extra Generators` is deprecated and will be removed from + a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)` + to view CMake-generated project build trees. + The extra generator used to build the project. See :manual:`cmake-generators(7)`. diff --git a/Help/variable/CMAKE_KATE_FILES_MODE.rst b/Help/variable/CMAKE_KATE_FILES_MODE.rst new file mode 100644 index 0000000..195c15d --- /dev/null +++ b/Help/variable/CMAKE_KATE_FILES_MODE.rst @@ -0,0 +1,20 @@ +CMAKE_KATE_FILES_MODE +--------------------- + +.. versionadded:: 3.27 + +This cache variable is used by the Kate project generator and controls +to what mode the ``files`` entry in the project file will be set. See +:manual:`cmake-generators(7)`. + +Possible values are ``AUTO``, ``SVN``, ``GIT``, ``HG``, ``FOSSIL`` and ``LIST``. + +When set to ``LIST``, CMake will put the list of source files known to CMake +in the project file. +When set to ``SVN``, ``GIT``, ``HG`` or ``FOSSIL``, CMake will set +the generated project accordingly to Subversion, git, Mercurial +or Fossil, and Kate will then use the respective command line tool to +retrieve the list of files in the project. +When unset or set to ``AUTO``, CMake will try to detect whether the +source directory is part of a git or svn checkout or not, and put the +respective entry into the project file. diff --git a/Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst b/Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst new file mode 100644 index 0000000..c830332 --- /dev/null +++ b/Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst @@ -0,0 +1,11 @@ +CMAKE_KATE_MAKE_ARGUMENTS +------------------------- + +.. versionadded:: 3.0 + +This cache variable is used by the Kate project generator. See +:manual:`cmake-generators(7)`. + +This variable holds arguments which are used when Kate invokes the make +tool. By default it is initialized to hold flags to enable parallel builds +(using -j typically). diff --git a/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst index a8c4035..79a65b8 100644 --- a/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst +++ b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst @@ -3,27 +3,38 @@ CMAKE_NINJA_OUTPUT_PATH_PREFIX .. versionadded:: 3.6 -Set output files path prefix for the :generator:`Ninja` generator. +Tell the :ref:`Ninja Generators` to add a prefix to every output path in +``build.ninja``. A trailing slash is appended to the prefix, if missing. -Every output files listed in the generated ``build.ninja`` will be -prefixed by the contents of this variable (a trailing slash is -appended if missing). This is useful when the generated ninja file is -meant to be embedded as a ``subninja`` file into a *super* ninja -project. For example, a ninja build file generated with a command -like:: +This is useful when the generated ninja file is meant to be embedded as a +``subninja`` file into a *super* ninja project. For example, the command: - cd top-build-dir/sub && - cmake -G Ninja -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/ path/to/source +.. code-block:: shell -can be embedded in ``top-build-dir/build.ninja`` with a directive like -this:: + cd super-build-dir && + cmake -G Ninja -S /path/to/src -B sub -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/ + # ^^^---------- these match -----------^^^ + +generates a build directory with its top-level (:variable:`CMAKE_BINARY_DIR`) +in ``super-build-dir/sub``. The path to the build directory ends in the +output path prefix. This makes it suitable for use in a separately-written +``super-build-dir/build.ninja`` file with a directive like this:: subninja sub/build.ninja -The ``auto-regeneration`` rule in ``top-build-dir/build.ninja`` must have an -order-only dependency on ``sub/build.ninja``. +The ``auto-regeneration`` rule in ``super-build-dir/build.ninja`` must +have an order-only dependency on ``sub/build.ninja``. + +.. versionadded:: 3.27 + + The :generator:`Ninja Multi-Config` generator supports this variable. .. note:: When ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` is set, the project generated by CMake cannot be used as a standalone project. No default targets are specified. + + The value of ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` must match one or more + path components at the *end* of :variable:`CMAKE_BINARY_DIR`, or the + behavior is undefined. However, this requirement is not checked + automatically. diff --git a/Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst new file mode 100644 index 0000000..3e2c6df --- /dev/null +++ b/Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst @@ -0,0 +1,10 @@ +CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS +----------------------------------- + +.. versionadded:: 3.27 + +Specify whether shared library generates an import file. + +This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target +property for shared library targets when they are created by calls to the +:command:`add_library` command. See the property documentation for details. diff --git a/Help/variable/PackageName_ROOT.rst b/Help/variable/PackageName_ROOT.rst index 6b17be3..8b728ba 100644 --- a/Help/variable/PackageName_ROOT.rst +++ b/Help/variable/PackageName_ROOT.rst @@ -14,3 +14,11 @@ This variable may hold a single prefix or a :ref:`semicolon-separated list <CMake Language Lists>` of multiple prefixes. See also the :envvar:`<PackageName>_ROOT` environment variable. + +.. variable:: <PACKAGENAME>_ROOT + + .. versionadded:: 3.27 + + Calls to :command:`find_package(<PackageName>)` will also search in + prefixes specified by the upper-case ``<PACKAGENAME>_ROOT`` CMake + variable. See policy :policy:`CMP0144`. diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in index 3953b30..e300782 100644 --- a/Modules/CMakeASMCompiler.cmake.in +++ b/Modules/CMakeASMCompiler.cmake.in @@ -6,6 +6,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1) set(CMAKE_ASM@ASM_DIALECT@_COMPILER_ID "@_CMAKE_ASM_COMPILER_ID@") set(CMAKE_ASM@ASM_DIALECT@_COMPILER_VERSION "@_CMAKE_ASM_COMPILER_VERSION@") diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in index 2b24ff2..8ae07a3 100644 --- a/Modules/CMakeCCompiler.cmake.in +++ b/Modules/CMakeCCompiler.cmake.in @@ -27,6 +27,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@) set(CMAKE_C_COMPILER_LOADED 1) set(CMAKE_C_COMPILER_WORKS @CMAKE_C_COMPILER_WORKS@) diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in index 534e960..dcfff6f 100644 --- a/Modules/CMakeCXXCompiler.cmake.in +++ b/Modules/CMakeCXXCompiler.cmake.in @@ -28,6 +28,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@) set(CMAKE_CXX_COMPILER_LOADED 1) set(CMAKE_CXX_COMPILER_WORKS @CMAKE_CXX_COMPILER_WORKS@) diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 41e0e1a..1f89c74 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -35,7 +35,7 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) else(CMAKE_${lang}_FLAGS_INIT) set(CMAKE_${lang}_COMPILER_ID_FLAGS ${CMAKE_${lang}_FLAGS_INIT}) endif() - string(REPLACE " " ";" CMAKE_${lang}_COMPILER_ID_FLAGS_LIST "${CMAKE_${lang}_COMPILER_ID_FLAGS}") + separate_arguments(CMAKE_${lang}_COMPILER_ID_FLAGS_LIST NATIVE_COMMAND "${CMAKE_${lang}_COMPILER_ID_FLAGS}") # Compute the directory in which to run the test. set(CMAKE_${lang}_COMPILER_ID_DIR ${CMAKE_PLATFORM_INFO_DIR}/CompilerId${lang}) diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index 2ac8879..190117c 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -165,6 +165,7 @@ else() set(_CMAKE_READELF_NAMES "readelf") set(_CMAKE_DLLTOOL_NAMES "dlltool") set(_CMAKE_ADDR2LINE_NAMES "addr2line") + set(_CMAKE_TAPI_NAMES "tapi") # Prepend toolchain-specific names. if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang) @@ -201,7 +202,7 @@ else() list(PREPEND _CMAKE_LINKER_NAMES "armlink") endif() - list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE) + list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE TAPI) endif() foreach(_CMAKE_TOOL IN LISTS _CMAKE_TOOL_VARS) @@ -225,6 +226,20 @@ if(NOT CMAKE_RANLIB) set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib") endif() +if(NOT CMAKE_TAPI) + # try to pick-up from Apple toolchain + execute_process(COMMAND xcrun --find tapi + OUTPUT_VARIABLE _xcrun_out + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE _xcrun_failed) + if(NOT _xcrun_failed AND EXISTS "${_xcrun_out}") + set_property(CACHE CMAKE_TAPI PROPERTY VALUE "${_xcrun_out}") + endif() + unset(_xcrun_out) + unset(_xcrun_failed) +endif() + if(CMAKE_PLATFORM_HAS_INSTALLNAME) find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) diff --git a/Modules/CMakeFindKate.cmake b/Modules/CMakeFindKate.cmake index 9aaf6e5..521bc5c 100644 --- a/Modules/CMakeFindKate.cmake +++ b/Modules/CMakeFindKate.cmake @@ -3,7 +3,7 @@ # This file is included in CMakeSystemSpecificInformation.cmake if -# the Eclipse CDT4 extra generator has been selected. +# the Kate extra generator has been selected. # Try to find out how many CPUs we have and set the -j argument for make accordingly @@ -17,5 +17,9 @@ if("${_CMAKE_KATE_PROCESSOR_COUNT}" GREATER 1 AND CMAKE_HOST_UNIX AND "${CMA set(_CMAKE_KATE_INITIAL_MAKE_ARGS "-j${_CMAKE_KATE_PROCESSOR_COUNT}") endif() -# This variable is used by the Eclipse generator and appended to the make invocation commands. +# This variable is used by the Kate generator and appended to the make invocation commands. set(CMAKE_KATE_MAKE_ARGUMENTS "${_CMAKE_KATE_INITIAL_MAKE_ARGS}" CACHE STRING "Additional command line arguments when Kate invokes make. Enter e.g. -j<some_number> to get parallel builds") + + +set(CMAKE_KATE_FILES_MODE "AUTO" CACHE STRING "Option to override the version control detection and force a mode for the Kate project.") +set_property(CACHE CMAKE_KATE_FILES_MODE PROPERTY STRINGS "AUTO;SVN;GIT;LIST") diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in index f52ad02..b8b409a 100644 --- a/Modules/CMakeFortranCompiler.cmake.in +++ b/Modules/CMakeFortranCompiler.cmake.in @@ -14,6 +14,7 @@ set(CMAKE_Fortran_SIMULATE_VERSION "@CMAKE_Fortran_SIMULATE_VERSION@") set(CMAKE_AR "@CMAKE_AR@") set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@") set(CMAKE_RANLIB "@CMAKE_RANLIB@") +set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@") set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@) set(CMAKE_Fortran_COMPILER_LOADED 1) diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index ce4e2cf..8a747c6 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in @@ -58,3 +58,4 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_TAPI "@CMAKE_TAPI@") diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in index 36f6ec1..ea11a7a 100644 --- a/Modules/CMakeOBJCCompiler.cmake.in +++ b/Modules/CMakeOBJCCompiler.cmake.in @@ -25,6 +25,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@) set(CMAKE_OBJC_COMPILER_LOADED 1) set(CMAKE_OBJC_COMPILER_WORKS @CMAKE_OBJC_COMPILER_WORKS@) diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in index 4f27100..5d0b381 100644 --- a/Modules/CMakeOBJCXXCompiler.cmake.in +++ b/Modules/CMakeOBJCXXCompiler.cmake.in @@ -26,6 +26,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@") set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@") set(CMAKE_LINKER "@CMAKE_LINKER@") set(CMAKE_MT "@CMAKE_MT@") +set(CMAKE_TAPI "@CMAKE_TAPI@") set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@) set(CMAKE_OBJCXX_COMPILER_LOADED 1) set(CMAKE_OBJCXX_COMPILER_WORKS @CMAKE_OBJCXX_COMPILER_WORKS@) diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index a75dfce..d27aa3f 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -72,9 +72,9 @@ if(CMAKE_GENERATOR STREQUAL "Xcode") # these options here will have no effect when compiling with the built-in driver, # and will explode violently, leaving build products in the source directory, when # using the old swift driver. - set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g") + set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g ${CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS}") set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O") - set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g") + set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g ${CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS}") set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize") else() set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g -incremental") diff --git a/Modules/CPackIFW.cmake b/Modules/CPackIFW.cmake index d4e02f1..2338b79 100644 --- a/Modules/CPackIFW.cmake +++ b/Modules/CPackIFW.cmake @@ -8,7 +8,7 @@ CPackIFW .. versionadded:: 3.1 This module looks for the location of the command-line utilities supplied with the -`Qt Installer Framework <http://doc.qt.io/qtinstallerframework/index.html>`_ +`Qt Installer Framework <https://doc.qt.io/qtinstallerframework/index.html>`_ (QtIFW). The module also defines several commands to control the behavior of the diff --git a/Modules/CTestTargets.cmake b/Modules/CTestTargets.cmake index b91b48e..99ef8e5 100644 --- a/Modules/CTestTargets.cmake +++ b/Modules/CTestTargets.cmake @@ -41,7 +41,7 @@ set(__conf_types "") get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(_isMultiConfig) # We need to pass the configuration type on the test command line. - set(__conf_types -C "${CMAKE_CFG_INTDIR}") + set(__conf_types -C "$<CONFIG>") endif() # Add convenience targets. Do this at most once in case of nested diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake index 7908f96..e75df59 100644 --- a/Modules/Compiler/IAR.cmake +++ b/Modules/Compiler/IAR.cmake @@ -9,7 +9,7 @@ include_guard() macro(__compiler_iar_common lang) - if (${lang} MATCHES "^(C|CXX)$") + if ("x${lang}" MATCHES "^x(C|CXX)$") set(CMAKE_${lang}_COMPILE_OBJECT "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>") set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy") diff --git a/Modules/Compiler/LCC-Fortran.cmake b/Modules/Compiler/LCC-Fortran.cmake index 8091b29..2d82ea8 100644 --- a/Modules/Compiler/LCC-Fortran.cmake +++ b/Modules/Compiler/LCC-Fortran.cmake @@ -10,8 +10,11 @@ set(CMAKE_Fortran_PREPROCESS_SOURCE set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form") set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form") -set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") -set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp") +# LCC < 1.24.00 has a broken Fortran preprocessor +if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.00") + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp") + set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp") +endif() set(CMAKE_Fortran_POSTPROCESS_FLAG "-fpreprocessed") diff --git a/Modules/Dart.cmake b/Modules/Dart.cmake index 154fe9d..3610012 100644 --- a/Modules/Dart.cmake +++ b/Modules/Dart.cmake @@ -5,6 +5,11 @@ Dart ---- +.. deprecated:: 3.27 + This module is available only if policy :policy:`CMP0145` + is not set to ``NEW``. Do not use it in new code. + Use the :module:`CTest` module instead. + Configure a project for testing with CTest or old Dart Tcl Client This file is the backwards-compatibility version of the CTest module. @@ -33,10 +38,24 @@ whether testing support should be enabled. The default is ON. # # +# include(Dart) already warns about CMP0145, but back when this module was in +# common use, it was often loaded via include(${CMAKE_ROOT}/Modules/Dart.cmake) +# which will not warn. Warn again just in case. +cmake_policy(GET CMP0145 cmp0145) +if(cmp0145 STREQUAL "") + cmake_policy(GET_WARNING CMP0145 _cmp0145_warning) + message(AUTHOR_WARNING "${_cmp0145_warning}") +endif() + option(BUILD_TESTING "Build the testing tree." ON) if(BUILD_TESTING) + # We only get here if a project already ran include(Dart), + # so avoid warning about CMP0145 again. + cmake_policy(PUSH) + cmake_policy(SET CMP0145 OLD) find_package(Dart QUIET) + cmake_policy(POP) # # Section #1: diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index b34a35b..9a6cbd6 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -2090,13 +2090,7 @@ function(_ep_get_configuration_subdir_genex suffix_var) set(suffix "") get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(_isMultiConfig) - if(CMAKE_GENERATOR STREQUAL "Xcode") - # The Xcode generator does not support per-config sources, - # so use the underlying build system's placeholder instead. - set(suffix "/${CMAKE_CFG_INTDIR}") - else() - set(suffix "/$<CONFIG>") - endif() + set(suffix "/$<CONFIG>") endif() set(${suffix_var} "${suffix}" PARENT_SCOPE) endfunction() diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in index ff8c659..bf7f209 100644 --- a/Modules/ExternalProject/download.cmake.in +++ b/Modules/ExternalProject/download.cmake.in @@ -108,7 +108,7 @@ message(STATUS "Downloading... timeout='@TIMEOUT_MSG@' inactivity timeout='@INACTIVITY_TIMEOUT_MSG@'" ) -set(download_retry_codes 7 6 8 15) +set(download_retry_codes 7 6 8 15 28) set(skip_url_list) set(status_code) foreach(i RANGE ${retry_number}) diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index 8afb9bc..dd5f617 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake @@ -1536,7 +1536,9 @@ ExternalProject_Add_Step(${contentName}-populate copyfile if(CMAKE_GENERATOR_TOOLSET) list(APPEND subCMakeOpts "-T${CMAKE_GENERATOR_TOOLSET}") endif() - + if(CMAKE_GENERATOR_INSTANCE) + list(APPEND subCMakeOpts "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${CMAKE_GENERATOR_INSTANCE}") + endif() if(CMAKE_MAKE_PROGRAM) list(APPEND subCMakeOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") endif() @@ -1596,7 +1598,9 @@ set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION # has this set to something not findable on the PATH. We also ensured above # that the Debug config will be defined for multi-config generators. configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/FetchContent/CMakeLists.cmake.in" - "${ARG_SUBBUILD_DIR}/CMakeLists.txt") + "${ARG_SUBBUILD_DIR}/CMakeLists.txt" + @ONLY + ) execute_process( COMMAND ${CMAKE_COMMAND} ${subCMakeOpts} . RESULT_VARIABLE result diff --git a/Modules/FetchContent/CMakeLists.cmake.in b/Modules/FetchContent/CMakeLists.cmake.in index d94b0f4..8adb533 100644 --- a/Modules/FetchContent/CMakeLists.cmake.in +++ b/Modules/FetchContent/CMakeLists.cmake.in @@ -1,21 +1,27 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION ${CMAKE_VERSION}) +cmake_minimum_required(VERSION @CMAKE_VERSION@) + +# Reject any attempt to use a toolchain file. We must not use one because +# we could be downloading it here. If the CMAKE_TOOLCHAIN_FILE environment +# variable is set, the cache variable will have been initialized from it. +unset(CMAKE_TOOLCHAIN_FILE CACHE) +unset(ENV{CMAKE_TOOLCHAIN_FILE}) # We name the project and the target for the ExternalProject_Add() call # to something that will highlight to the user what we are working on if # something goes wrong and an error message is produced. -project(${contentName}-populate NONE) +project(@contentName@-populate NONE) @__FETCHCONTENT_CACHED_INFO@ include(ExternalProject) -ExternalProject_Add(${contentName}-populate - ${ARG_EXTRA} - SOURCE_DIR "${ARG_SOURCE_DIR}" - BINARY_DIR "${ARG_BINARY_DIR}" +ExternalProject_Add(@contentName@-populate + @ARG_EXTRA@ + SOURCE_DIR "@ARG_SOURCE_DIR@" + BINARY_DIR "@ARG_BINARY_DIR@" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index c3b6bc3..d7c6d5e 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake @@ -173,7 +173,7 @@ cuFile .. versionadded:: 3.25 -The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/cuda/cufile-api/index.html>`_ library. +The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/gpudirect-storage/api-reference-guide/index.html>`_ library. Targets Created: @@ -236,7 +236,7 @@ Targets Created: cupti """"" -The `NVIDIA CUDA Profiling Tools Interface <https://developer.nvidia.com/CUPTI>`_. +The `NVIDIA CUDA Profiling Tools Interface <https://developer.nvidia.com/cupti>`_. Targets Created: @@ -330,7 +330,7 @@ Targets Created: nvGRAPH """"""" -The `nvGRAPH <https://docs.nvidia.com/cuda/nvgraph/index.html>`_ library. +The `nvGRAPH <https://web.archive.org/web/20201111171403/https://docs.nvidia.com/cuda/nvgraph/index.html>`_ library. Removed starting in CUDA 11.0 Targets Created: @@ -417,7 +417,7 @@ nvToolsExt .. deprecated:: 3.25 With CUDA 10.0+, use :ref:`nvtx3 <cuda_toolkit_nvtx3>`. -The `NVIDIA Tools Extension <https://docs.nvidia.com/gameworks/content/gameworkslibrary/nvtx/nvidia_tools_extension_library_nvtx.htm>`_. +The `NVIDIA Tools Extension <https://docs.nvidia.com/nvtx/>`_. This is a shared library only. Targets Created: diff --git a/Modules/FindDart.cmake b/Modules/FindDart.cmake index 0492578..fed50e1 100644 --- a/Modules/FindDart.cmake +++ b/Modules/FindDart.cmake @@ -5,12 +5,20 @@ FindDart -------- +.. deprecated:: 3.27 + This module is available only if policy :policy:`CMP0145` is not set to ``NEW``. + Find DART This module looks for the dart testing software and sets DART_ROOT to point to where it found it. #]=======================================================================] +if(_FindDart_testing) + set(_FindDart_included TRUE) + return() +endif() + find_path(DART_ROOT README.INSTALL HINTS ENV DART_ROOT diff --git a/Modules/FindEXPAT.cmake b/Modules/FindEXPAT.cmake index f9cb432..3bedc73 100644 --- a/Modules/FindEXPAT.cmake +++ b/Modules/FindEXPAT.cmake @@ -39,27 +39,62 @@ pkg_check_modules(PC_EXPAT QUIET expat) # Look for the header file. find_path(EXPAT_INCLUDE_DIR NAMES expat.h HINTS ${PC_EXPAT_INCLUDE_DIRS}) -# Look for the library. -find_library(EXPAT_LIBRARY NAMES expat libexpat NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS}) - -if (EXPAT_INCLUDE_DIR AND EXISTS "${EXPAT_INCLUDE_DIR}/expat.h") - file(STRINGS "${EXPAT_INCLUDE_DIR}/expat.h" expat_version_str - REGEX "^#[\t ]*define[\t ]+XML_(MAJOR|MINOR|MICRO)_VERSION[\t ]+[0-9]+$") - - unset(EXPAT_VERSION_STRING) - foreach(VPART MAJOR MINOR MICRO) - foreach(VLINE ${expat_version_str}) - if(VLINE MATCHES "^#[\t ]*define[\t ]+XML_${VPART}_VERSION[\t ]+([0-9]+)$") - set(EXPAT_VERSION_PART "${CMAKE_MATCH_1}") - if(EXPAT_VERSION_STRING) - string(APPEND EXPAT_VERSION_STRING ".${EXPAT_VERSION_PART}") - else() - set(EXPAT_VERSION_STRING "${EXPAT_VERSION_PART}") - endif() - endif() - endforeach() +set(EXPAT_NAMES expat expatw) +set(EXPAT_NAMES_DEBUG expatd expatwd) + +if(WIN32) + list(APPEND EXPAT_NAMES expatMT expatMD expatwMT expatwMD) + list(APPEND EXPAT_NAMES_DEBUG expatdMT expatdMD expatwdMT expatwdMD) +endif() + +# Allow EXPAT_LIBRARY to be set manually, as the location of the expat library +if(NOT EXPAT_LIBRARY) + if(DEFINED CMAKE_FIND_LIBRARY_PREFIXES) + set(_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES}") + else() + set(_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES) + endif() + + if(WIN32) + list(APPEND CMAKE_FIND_LIBRARY_PREFIXES "" "lib") + endif() + + # Look for the library. + find_library(EXPAT_LIBRARY_RELEASE NAMES ${EXPAT_NAMES} NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS} PATH_SUFFIXES lib) + find_library(EXPAT_LIBRARY_DEBUG NAMES ${EXPAT_NAMES_DEBUG} NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS} PATH_SUFFIXES lib) + + # Restore the original find library ordering + if(DEFINED _expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES) + set(CMAKE_FIND_LIBRARY_PREFIXES "${_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES}") + else() + set(CMAKE_FIND_LIBRARY_PREFIXES) + endif() + + include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) + select_library_configurations(EXPAT) +endif() + +unset(EXPAT_NAMES) +unset(EXPAT_NAMES_DEBUG) + +if(EXPAT_INCLUDE_DIR AND EXISTS "${EXPAT_INCLUDE_DIR}/expat.h") + file(STRINGS "${EXPAT_INCLUDE_DIR}/expat.h" expat_version_str + REGEX "^#[\t ]*define[\t ]+XML_(MAJOR|MINOR|MICRO)_VERSION[\t ]+[0-9]+$") + + unset(EXPAT_VERSION_STRING) + foreach(VPART MAJOR MINOR MICRO) + foreach(VLINE ${expat_version_str}) + if(VLINE MATCHES "^#[\t ]*define[\t ]+XML_${VPART}_VERSION[\t ]+([0-9]+)$") + set(EXPAT_VERSION_PART "${CMAKE_MATCH_1}") + if(EXPAT_VERSION_STRING) + string(APPEND EXPAT_VERSION_STRING ".${EXPAT_VERSION_PART}") + else() + set(EXPAT_VERSION_STRING "${EXPAT_VERSION_PART}") + endif() + endif() endforeach() -endif () + endforeach() +endif() include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) FIND_PACKAGE_HANDLE_STANDARD_ARGS(EXPAT @@ -68,15 +103,36 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(EXPAT # Copy the results to the output variables and target. if(EXPAT_FOUND) - set(EXPAT_LIBRARIES ${EXPAT_LIBRARY}) set(EXPAT_INCLUDE_DIRS ${EXPAT_INCLUDE_DIR}) + if(NOT EXPAT_LIBRARIES) + set(EXPAT_LIBRARIES ${EXPAT_LIBRARY}) + endif() + if(NOT TARGET EXPAT::EXPAT) add_library(EXPAT::EXPAT UNKNOWN IMPORTED) set_target_properties(EXPAT::EXPAT PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${EXPAT_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${EXPAT_INCLUDE_DIRS}") + + if(EXPAT_LIBRARY_RELEASE) + set_property(TARGET EXPAT::EXPAT APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(EXPAT::EXPAT PROPERTIES + IMPORTED_LOCATION_RELEASE "${EXPAT_LIBRARY_RELEASE}") + endif() + + if(EXPAT_LIBRARY_DEBUG) + set_property(TARGET EXPAT::EXPAT APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(EXPAT::EXPAT PROPERTIES + IMPORTED_LOCATION_DEBUG "${EXPAT_LIBRARY_DEBUG}") + endif() + + if(NOT EXPAT_LIBRARY_RELEASE AND NOT EXPAT_LIBRARY_DEBUG) + set_property(TARGET EXPAT::EXPAT APPEND PROPERTY + IMPORTED_LOCATION "${EXPAT_LIBRARY}") + endif() endif() endif() diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake index db03c54..62c492c 100644 --- a/Modules/FindHDF5.cmake +++ b/Modules/FindHDF5.cmake @@ -334,8 +334,15 @@ function(_HDF5_test_regular_compiler_Fortran success is_parallel) ERROR_VARIABLE config_error RESULT_VARIABLE config_result ) - if(config_output MATCHES "Parallel HDF5: yes") - set(${is_parallel} TRUE PARENT_SCOPE) + if(config_output MATCHES "Parallel HDF5: ([A-Za-z0-9]+)") + # The value may be anything used when HDF5 was configured, + # so see if CMake interprets it as "true". + set(parallelHDF5 "${CMAKE_MATCH_1}") + if(parallelHDF5) + set(${is_parallel} TRUE PARENT_SCOPE) + else() + set(${is_parallel} FALSE PARENT_SCOPE) + endif() else() set(${is_parallel} FALSE PARENT_SCOPE) endif() @@ -401,8 +408,13 @@ function( _HDF5_invoke_compiler language output_var return_value_var version_var string(REPLACE "HDF5 Version: " "" version "${version}") string(REPLACE "-patch" "." version "${version}") endif() - if(config_output MATCHES "Parallel HDF5: yes") - set(is_parallel TRUE) + if(config_output MATCHES "Parallel HDF5: ([A-Za-z0-9]+)") + # The value may be anything used when HDF5 was configured, + # so see if CMake interprets it as "true". + set(parallelHDF5 "${CMAKE_MATCH_1}") + if(parallelHDF5) + set(is_parallel TRUE) + endif() endif() endif() foreach(var output return_value version is_parallel) diff --git a/Modules/FindOpenCL.cmake b/Modules/FindOpenCL.cmake index 2b700ff..55be667 100644 --- a/Modules/FindOpenCL.cmake +++ b/Modules/FindOpenCL.cmake @@ -39,6 +39,8 @@ The module will also define two cache variables:: #]=======================================================================] +set(_OPENCL_x86 "(x86)") + function(_FIND_OPENCL_VERSION) include(CheckSymbolExists) include(CMakePushCheckState) @@ -79,6 +81,9 @@ find_path(OpenCL_INCLUDE_DIR CL/cl.h OpenCL/cl.h PATHS ENV "PROGRAMFILES(X86)" + ENV "PROGRAMFILES" + $ENV{PROGRAMFILES${_OPENCL_x86}}/OpenCLHeaders + $ENV{PROGRAMFILES}/OpenCLHeaders ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV NVSDKCOMPUTE_ROOT @@ -100,6 +105,9 @@ if(WIN32) NAMES OpenCL PATHS ENV "PROGRAMFILES(X86)" + ENV "PROGRAMFILES" + $ENV{PROGRAMFILES${_OPENCL_x86}}/OpenCL-ICD-Loader + $ENV{PROGRAMFILES}/OpenCL-ICD-Loader ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV CUDA_PATH @@ -116,6 +124,9 @@ if(WIN32) NAMES OpenCL PATHS ENV "PROGRAMFILES(X86)" + ENV "PROGRAMFILES" + $ENV{PROGRAMFILES${_OPENCL_x86}}/OpenCL-ICD-Loader + $ENV{PROGRAMFILES}/OpenCL-ICD-Loader ENV AMDAPPSDKROOT ENV INTELOCLSDKROOT ENV CUDA_PATH @@ -126,6 +137,7 @@ if(WIN32) "AMD APP/lib/x86_64" lib/x86_64 lib/x64 + lib OpenCL/common/lib/x64) endif() else() @@ -156,6 +168,8 @@ else() endif() endif() +unset(_OPENCL_x86) + set(OpenCL_LIBRARIES ${OpenCL_LIBRARY}) set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR}) diff --git a/Modules/FindPNG.cmake b/Modules/FindPNG.cmake index 94d15db..043b69c 100644 --- a/Modules/FindPNG.cmake +++ b/Modules/FindPNG.cmake @@ -48,18 +48,35 @@ Since PNG depends on the ZLib compression library, none of the above will be defined unless ZLib can be found. #]=======================================================================] +# Default install location on windows when installing from included cmake build +# From FindZLIB.cmake +set(_PNG_x86 "(x86)") +set(_PNG_INCLUDE_SEARCH_NORMAL + "$ENV{ProgramFiles}/libpng" + "$ENV{ProgramFiles${_PNG_x86}}/libpng") +set(_PNG_LIB_SEARCH_NORMAL + "$ENV{ProgramFiles}/libpng/lib" + "$ENV{ProgramFiles${_PNG_x86}}/libpng/lib") +unset(_PNG_x86) + if(PNG_FIND_QUIETLY) set(_FIND_ZLIB_ARG QUIET) endif() find_package(ZLIB ${_FIND_ZLIB_ARG}) if(ZLIB_FOUND) - find_path(PNG_PNG_INCLUDE_DIR png.h PATH_SUFFIXES include/libpng) + set(_PNG_VERSION_SUFFIXES 17 16 15 14 12) + + list(APPEND _PNG_INCLUDE_PATH_SUFFIXES include/libpng) + foreach(v IN LISTS _PNG_VERSION_SUFFIXES) + list(APPEND _PNG_INCLUDE_PATH_SUFFIXES include/libpng${v}) + endforeach() + + find_path(PNG_PNG_INCLUDE_DIR png.h PATH_SUFFIXES ${_PNG_INCLUDE_PATH_SUFFIXES} PATHS ${_PNG_INCLUDE_SEARCH_NORMAL} ) mark_as_advanced(PNG_PNG_INCLUDE_DIR) list(APPEND PNG_NAMES png libpng) unset(PNG_NAMES_DEBUG) - set(_PNG_VERSION_SUFFIXES 17 16 15 14 12) if (PNG_FIND_VERSION MATCHES "^([0-9]+)\\.([0-9]+)(\\..*)?$") set(_PNG_VERSION_SUFFIX_MIN "${CMAKE_MATCH_1}${CMAKE_MATCH_2}") if (PNG_FIND_VERSION_EXACT) @@ -79,14 +96,15 @@ if(ZLIB_FOUND) # For compatibility with versions prior to this multi-config search, honor # any PNG_LIBRARY that is already specified and skip the search. if(NOT PNG_LIBRARY) - find_library(PNG_LIBRARY_RELEASE NAMES ${PNG_NAMES} NAMES_PER_DIR) - find_library(PNG_LIBRARY_DEBUG NAMES ${PNG_NAMES_DEBUG} NAMES_PER_DIR) + find_library(PNG_LIBRARY_RELEASE NAMES ${PNG_NAMES} NAMES_PER_DIR PATHS ${_PNG_LIB_SEARCH_NORMAL}) + find_library(PNG_LIBRARY_DEBUG NAMES ${PNG_NAMES_DEBUG} NAMES_PER_DIR PATHS ${_PNG_LIB_SEARCH_NORMAL}) include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) select_library_configurations(PNG) mark_as_advanced(PNG_LIBRARY_RELEASE PNG_LIBRARY_DEBUG) endif() unset(PNG_NAMES) unset(PNG_NAMES_DEBUG) + unset(_PNG_INCLUDE_PATH_SUFFIXES) # Set by select_library_configurations(), but we want the one from # find_package_handle_standard_args() below. diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake index 3817987..581763d 100644 --- a/Modules/FindVulkan.cmake +++ b/Modules/FindVulkan.cmake @@ -244,23 +244,26 @@ endif() if(WIN32) set(_Vulkan_library_name vulkan-1) set(_Vulkan_hint_include_search_paths - "$ENV{VULKAN_SDK}/Include" + "$ENV{VULKAN_SDK}/include" ) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_Vulkan_hint_executable_search_paths - "$ENV{VULKAN_SDK}/Bin" + "$ENV{VULKAN_SDK}/bin" ) set(_Vulkan_hint_library_search_paths - "$ENV{VULKAN_SDK}/Lib" - "$ENV{VULKAN_SDK}/Bin" + "$ENV{VULKAN_SDK}/lib" + "$ENV{VULKAN_SDK}/bin" ) else() set(_Vulkan_hint_executable_search_paths - "$ENV{VULKAN_SDK}/Bin32" + "$ENV{VULKAN_SDK}/bin32" + "$ENV{VULKAN_SDK}/bin" ) set(_Vulkan_hint_library_search_paths - "$ENV{VULKAN_SDK}/Lib32" - "$ENV{VULKAN_SDK}/Bin32" + "$ENV{VULKAN_SDK}/lib32" + "$ENV{VULKAN_SDK}/bin32" + "$ENV{VULKAN_SDK}/lib" + "$ENV{VULKAN_SDK}/bin" ) endif() else() diff --git a/Modules/FindX11.cmake b/Modules/FindX11.cmake index 8e5a5f1..6f6483a 100644 --- a/Modules/FindX11.cmake +++ b/Modules/FindX11.cmake @@ -30,10 +30,13 @@ and also the following more fine grained variables and targets: X11_Xau_INCLUDE_PATH, X11_Xau_LIB, X11_Xau_FOUND, X11::Xau X11_xcb_INCLUDE_PATH, X11_xcb_LIB, X11_xcb_FOUND, X11::xcb X11_X11_xcb_INCLUDE_PATH, X11_X11_xcb_LIB, X11_X11_xcb_FOUND, X11::X11_xcb + X11_xcb_cursor_INCLUDE_PATH, X11_xcb_cursor_LIB, X11_xcb_cursor_FOUND, X11::xcb_cursor X11_xcb_icccm_INCLUDE_PATH, X11_xcb_icccm_LIB, X11_xcb_icccm_FOUND, X11::xcb_icccm X11_xcb_randr_INCLUDE_PATH, X11_xcb_randr_LIB, X11_xcb_randr_FOUND, X11::xcb_randr + X11_xcb_shape_INCLUDE_PATH, X11_xcb_shape_LIB, X11_xcb_shape_FOUND, X11::xcb_shape X11_xcb_util_INCLUDE_PATH, X11_xcb_util_LIB, X11_xcb_util_FOUND, X11::xcb_util X11_xcb_xfixes_INCLUDE_PATH, X11_xcb_xfixes_LIB, X11_xcb_xfixes_FOUND, X11::xcb_xfixes + X11_xcb_xrm_INCLUDE_PATH, X11_xcb_xrm_LIB, X11_xcb_xrm_FOUND, X11::xcb_xrm X11_xcb_xtest_INCLUDE_PATH, X11_xcb_xtest_LIB, X11_xcb_xtest_FOUND, X11::xcb_xtest X11_xcb_keysyms_INCLUDE_PATH, X11_xcb_keysyms_LIB,X11_xcb_keysyms_FOUND,X11::xcb_keysyms X11_xcb_xkb_INCLUDE_PATH, X11_xcb_xkb_LIB, X11_xcb_xkb_FOUND, X11::xcb_xkb @@ -88,6 +91,9 @@ and also the following more fine grained variables and targets: .. versionadded:: 3.24 Added the ``xcb_randr``, ``xcb_xtext``, and ``xcb_keysyms`` libraries. +.. versionadded:: 3.27 + Added the ``xcb_cursor``, ``xcb_shape``, and ``xcb_xrm`` libraries. + #]=======================================================================] if (UNIX) @@ -132,10 +138,13 @@ if (UNIX) find_path(X11_Xaw_INCLUDE_PATH X11/Xaw/Intrinsic.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_INCLUDE_PATH xcb/xcb.h ${X11_INC_SEARCH_PATH}) find_path(X11_X11_xcb_INCLUDE_PATH X11/Xlib-xcb.h ${X11_INC_SEARCH_PATH}) + find_path(X11_xcb_cursor_INCLUDE_PATH xcb/xcb_cursor.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_icccm_INCLUDE_PATH xcb/xcb_icccm.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_randr_INCLUDE_PATH xcb/randr.h ${X11_INC_SEARCH_PATH}) + find_path(X11_xcb_shape_INCLUDE_PATH xcb/shape.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_util_INCLUDE_PATH xcb/xcb_aux.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_xfixes_INCLUDE_PATH xcb/xfixes.h ${X11_INC_SEARCH_PATH}) + find_path(X11_xcb_xrm_INCLUDE_PATH xcb/xcb_xrm.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_xtest_INCLUDE_PATH xcb/xtest.h ${X11_INC_SEARCH_PATH}) find_path(X11_xcb_keysyms_INCLUDE_PATH xcb/xcb_keysyms.h ${X11_INC_SEARCH_PATH}) find_path(X11_Xcomposite_INCLUDE_PATH X11/extensions/Xcomposite.h ${X11_INC_SEARCH_PATH}) @@ -188,10 +197,13 @@ if (UNIX) find_library(X11_Xaw_LIB Xaw ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_LIB xcb ${X11_LIB_SEARCH_PATH}) find_library(X11_X11_xcb_LIB X11-xcb ${X11_LIB_SEARCH_PATH}) + find_library(X11_xcb_cursor_LIB xcb-cursor ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_icccm_LIB xcb-icccm ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_randr_LIB xcb-randr ${X11_LIB_SEARCH_PATH}) + find_library(X11_xcb_shape_LIB xcb-shape ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_util_LIB xcb-util ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_xfixes_LIB xcb-xfixes ${X11_LIB_SEARCH_PATH}) + find_library(X11_xcb_xrm_LIB xcb-xrm ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_xtest_LIB xcb-xtest ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_keysyms_LIB xcb-keysyms ${X11_LIB_SEARCH_PATH}) find_library(X11_xcb_xkb_LIB xcb-xkb ${X11_LIB_SEARCH_PATH}) @@ -289,6 +301,10 @@ if (UNIX) set(X11_X11_xcb_FOUND TRUE) endif () + if (X11_xcb_cursor_LIB AND X11_xcb_cursor_INCLUDE_PATH) + set(X11_xcb_cursor_FOUND TRUE) + endif () + if (X11_xcb_icccm_LIB AND X11_xcb_icccm_INCLUDE_PATH) set(X11_xcb_icccm_FOUND TRUE) endif () @@ -297,6 +313,10 @@ if (UNIX) set(X11_xcb_randr_FOUND TRUE) endif () + if (X11_xcb_shape_LIB AND X11_xcb_shape_INCLUDE_PATH) + set(X11_xcb_shape_FOUND TRUE) + endif () + if (X11_xcb_util_LIB AND X11_xcb_util_INCLUDE_PATH) set(X11_xcb_util_FOUND TRUE) endif () @@ -305,6 +325,10 @@ if (UNIX) set(X11_xcb_xfixes_FOUND TRUE) endif () + if (X11_xcb_xrm_LIB AND X11_xcb_xrm_INCLUDE_PATH) + set(X11_xcb_xrm_FOUND TRUE) + endif () + if (X11_xcb_xtest_LIB) set(X11_xcb_xtest_FOUND TRUE) endif () @@ -617,6 +641,13 @@ if (UNIX) INTERFACE_LINK_LIBRARIES "X11::xcb;X11::X11") endif () + if (X11_xcb_cursor_FOUND AND NOT TARGET X11::xcb_cursor) + add_library(X11::xcb_cursor UNKNOWN IMPORTED) + set_target_properties(X11::xcb_cursor PROPERTIES + IMPORTED_LOCATION "${X11_xcb_cursor_LIB}" + INTERFACE_LINK_LIBRARIES "X11::xcb") + endif () + if (X11_xcb_icccm_FOUND AND NOT TARGET X11::xcb_icccm) add_library(X11::xcb_icccm UNKNOWN IMPORTED) set_target_properties(X11::xcb_icccm PROPERTIES @@ -631,6 +662,13 @@ if (UNIX) INTERFACE_LINK_LIBRARIES "X11::xcb") endif () + if (X11_xcb_shape_FOUND AND NOT TARGET X11::xcb_shape) + add_library(X11::xcb_shape UNKNOWN IMPORTED) + set_target_properties(X11::xcb_shape PROPERTIES + IMPORTED_LOCATION "${X11_xcb_shape_LIB}" + INTERFACE_LINK_LIBRARIES "X11::xcb") + endif () + if (X11_xcb_util_FOUND AND NOT TARGET X11::xcb_util) add_library(X11::xcb_util UNKNOWN IMPORTED) set_target_properties(X11::xcb_util PROPERTIES @@ -645,6 +683,13 @@ if (UNIX) INTERFACE_LINK_LIBRARIES "X11::xcb") endif () + if (X11_xcb_xrm_FOUND AND NOT TARGET X11::xcb_xrm) + add_library(X11::xcb_xrm UNKNOWN IMPORTED) + set_target_properties(X11::xcb_xrm PROPERTIES + IMPORTED_LOCATION "${X11_xcb_xrm_LIB}" + INTERFACE_LINK_LIBRARIES "X11::xcb") + endif () + if (X11_xcb_xtest_FOUND AND NOT TARGET X11::xcb_xtest) add_library(X11::xcb_xtest UNKNOWN IMPORTED) set_target_properties(X11::xcb_xtest PROPERTIES @@ -873,14 +918,20 @@ if (UNIX) X11_Xau_INCLUDE_PATH X11_xcb_LIB X11_xcb_INCLUDE_PATH + X11_xcb_cursor_LIB + X11_xcb_cursor_INCLUDE_PATH X11_xcb_icccm_LIB X11_xcb_icccm_INCLUDE_PATH X11_xcb_randr_LIB X11_xcb_randr_INCLUDE_PATH + X11_xcb_shape_LIB + X11_xcb_shape_INCLUDE_PATH X11_xcb_util_LIB X11_xcb_util_INCLUDE_PATH X11_xcb_xfixes_LIB X11_xcb_xfixes_INCLUDE_PATH + X11_xcb_xrm_LIB + X11_xcb_xrm_INCLUDE_PATH X11_xcb_xtest_LIB X11_xcb_xtest_INCLUDE_PATH X11_xcb_keysyms_LIB diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake index 7118df2..40e767b 100644 --- a/Modules/FindXCTest.cmake +++ b/Modules/FindXCTest.cmake @@ -13,7 +13,7 @@ An XCTest bundle is a CFBundle with a special product-type and bundle extension. The Mac Developer Library provides more information in the `Testing with Xcode`_ document. -.. _Testing with Xcode: https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/ +.. _Testing with Xcode: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/ Module Functions ^^^^^^^^^^^^^^^^ diff --git a/Modules/GenerateExportHeader.cmake b/Modules/GenerateExportHeader.cmake index 7461a3e..ea8616a 100644 --- a/Modules/GenerateExportHeader.cmake +++ b/Modules/GenerateExportHeader.cmake @@ -293,7 +293,7 @@ macro(_DO_SET_MACRO_VALUES TARGET_LIBRARY) set(DEFINE_IMPORT) set(DEFINE_NO_EXPORT) - if (COMPILER_HAS_DEPRECATED_ATTR) + if (COMPILER_HAS_DEPRECATED_ATTR AND NOT WIN32) set(DEFINE_DEPRECATED "__attribute__ ((__deprecated__))") elseif(COMPILER_HAS_DEPRECATED) set(DEFINE_DEPRECATED "__declspec(deprecated)") diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake index 0ba35b6..0cd49ab 100644 --- a/Modules/GetPrerequisites.cmake +++ b/Modules/GetPrerequisites.cmake @@ -730,7 +730,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa if(gp_tool MATCHES "ldd$") set(gp_cmd_args "") - set(gp_regex "^[\t ]*[^\t ]+ =>[\t ]+([^\t\(]+)( \(.+\))?${eol_char}$") + set(gp_regex "^[\t ]*[^\t ]+ =>[\t ]+(/[^\t\(]+)( \(.+\))?${eol_char}$") set(gp_regex_error "not found${eol_char}$") set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$") set(gp_regex_cmp_count 1) diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index ac2478b..a6c86f1 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -45,6 +45,8 @@ set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") set(CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES ".tbd" ".so") set(CMAKE_SHARED_MODULE_PREFIX "lib") set(CMAKE_SHARED_MODULE_SUFFIX ".so") +set(CMAKE_APPLE_IMPORT_FILE_PREFIX "lib") +set(CMAKE_APPLE_IMPORT_FILE_SUFFIX ".tbd") set(CMAKE_MODULE_EXISTS 1) set(CMAKE_DL_LIBS "") if(NOT "${_CURRENT_OSX_VERSION}" VERSION_LESS "10.5") @@ -108,6 +110,9 @@ foreach(lang C CXX Fortran OBJC OBJCXX) set(CMAKE_${lang}_FRAMEWORK_SEARCH_FLAG -F) endforeach() +# To generate text-based stubs +set(CMAKE_CREATE_TEXT_STUBS "<CMAKE_TAPI> stubify -isysroot <CMAKE_OSX_SYSROOT> -o <TARGET_IMPLIB> <TARGET>") + # Defines LINK_LIBRARY features for frameworks set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>") set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) diff --git a/Modules/Platform/Linux-LCC-Fortran.cmake b/Modules/Platform/Linux-LCC-Fortran.cmake index bf2a1c2..308c771 100644 --- a/Modules/Platform/Linux-LCC-Fortran.cmake +++ b/Modules/Platform/Linux-LCC-Fortran.cmake @@ -1,7 +1,9 @@ include(Platform/Linux-LCC) __linux_compiler_lcc(Fortran) -if (CMAKE_Fortran_COMPILER_VERSION VERSION_LESS "1.26.03") +if (CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.26.03") + set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-lgfortran") +elseif (CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.01") set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-llfortran") else() - set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-lgfortran") + unset(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE) endif() diff --git a/Modules/Platform/Windows-Apple-Swift.cmake b/Modules/Platform/Windows-Apple-Swift.cmake index 1177755..3f754fd 100644 --- a/Modules/Platform/Windows-Apple-Swift.cmake +++ b/Modules/Platform/Windows-Apple-Swift.cmake @@ -1 +1,3 @@ set(CMAKE_Swift_IMPLIB_LINKER_FLAGS "-Xlinker -implib:<TARGET_IMPLIB>") +set(CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS "-Xlinker -debug") +set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS "-Xlinker -debug") diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index de388f5..c8934a4 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,8 +1,8 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 26) -set(CMake_VERSION_PATCH 0) -set(CMake_VERSION_RC 5) +set(CMake_VERSION_PATCH 20230308) +#set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) # Start with the full version number used in tags. It has no dev info. diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx index 162dfc7..ea7b24b 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.cxx +++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx @@ -263,7 +263,7 @@ cmGeneratedFileStream& operator<<(cmGeneratedFileStream& s, } // Look up variable; if no value is set, returns an empty string; -// basically a wrapper that handles the NULL-ptr return from GetOption(). +// basically a wrapper that handles the nullptr return from GetOption(). std::string cmCPackFreeBSDGenerator::var_lookup(const char* var_name) { cmValue pv = this->GetOption(var_name); @@ -402,7 +402,7 @@ int cmCPackFreeBSDGenerator::PackageFiles() this->packageFileNames.emplace_back(actualPackage); } - if (!pkg_initialized() && pkg_init(NULL, NULL) != EPKG_OK) { + if (!pkg_initialized() && pkg_init(nullptr, nullptr) != EPKG_OK) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Can not initialize FreeBSD libpkg." << std::endl); return 0; diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index 643bc6f..cece98e 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -72,11 +72,6 @@ int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring, if (!this->CTest->GetConfigType().empty()) { config = this->CTest->GetConfigType().c_str(); } -#ifdef CMAKE_INTDIR - if (!config) { - config = CMAKE_INTDIR; - } -#endif if (config) { args.push_back("-DCMAKE_BUILD_TYPE:STRING=" + std::string(config)); @@ -256,11 +251,6 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) if (!this->CTest->GetConfigType().empty()) { config = this->CTest->GetConfigType().c_str(); } -#ifdef CMAKE_INTDIR - if (!config) { - config = CMAKE_INTDIR; - } -#endif if (!config) { config = "Debug"; } diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 71787ea..50d69df 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -89,11 +89,7 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() } } if (cmakeBuildConfiguration.empty()) { -#ifdef CMAKE_INTDIR - cmakeBuildConfiguration = CMAKE_INTDIR; -#else cmakeBuildConfiguration = "Debug"; -#endif } std::string dir = this->CTest->GetCTestConfiguration("BuildDirectory"); diff --git a/Source/CTest/cmCTestCurl.cxx b/Source/CTest/cmCTestCurl.cxx index 13b0278..8f7d581 100644 --- a/Source/CTest/cmCTestCurl.cxx +++ b/Source/CTest/cmCTestCurl.cxx @@ -157,7 +157,7 @@ bool cmCTestCurl::UploadFile(std::string const& local_file, // Now run off and do what you've been told! ::curl_easy_perform(this->Curl); ::fclose(ftpfile); - ::curl_easy_setopt(this->Curl, CURLOPT_HTTPHEADER, NULL); + ::curl_easy_setopt(this->Curl, CURLOPT_HTTPHEADER, nullptr); ::curl_slist_free_all(headers); if (!responseData.empty()) { diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 1c8c713..1d509cf 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -2569,7 +2569,7 @@ bool cmCTestTestHandler::WriteJUnitXML() xml.EndElement(); // </skipped> } else if (status == "fail") { xml.StartElement("failure"); - xml.Attribute("message", result.Reason); + xml.Attribute("message", this->GetTestStatus(result)); xml.EndElement(); // </failure> } diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index 73e7ef2..f5b7587 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -38,6 +38,8 @@ function(cm_check_cxx_feature name) string(REGEX REPLACE " +0 Warning\\(s\\)" "" check_output "${check_output}") # Filter out MSBuild output that looks like a warning. string(REGEX REPLACE "[^\n]*warning MSB[0-9][0-9][0-9][0-9][^\n]*" "" check_output "${check_output}") + # 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}") # Filter out warnings caused by local configuration. diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake index 5cfb0e7..3dc099f 100644 --- a/Source/Modules/CMakeBuildUtilities.cmake +++ b/Source/Modules/CMakeBuildUtilities.cmake @@ -54,7 +54,6 @@ if(BUILD_TESTING) CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestProcess "${kwsys_folder}") CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestsC "${kwsys_folder}") CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestsCxx "${kwsys_folder}") - CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestSharedForward "${kwsys_folder}") endif() #--------------------------------------------------------------------- diff --git a/Source/QtDialog/AddCacheEntry.cxx b/Source/QtDialog/AddCacheEntry.cxx index 1075895..7c34c60 100644 --- a/Source/QtDialog/AddCacheEntry.cxx +++ b/Source/QtDialog/AddCacheEntry.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "AddCacheEntry.h" +#include "QCMakeSizeType.h" #include <QCompleter> #include <QMetaProperty> @@ -88,7 +89,7 @@ QString AddCacheEntry::typeString() const void AddCacheEntry::onCompletionActivated(const QString& text) { - int idx = this->VarNames.indexOf(text); + cm_qsizetype idx = this->VarNames.indexOf(text); if (idx != -1) { QString vartype = this->VarTypes[idx]; for (int i = 0; i < NumTypes; i++) { diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index c90b7ee..e6e41ec 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -175,6 +175,7 @@ add_library( QCMakePresetComboBox.h QCMakePresetItemModel.cxx QCMakePresetItemModel.h + QCMakeSizeType.h QCMakeWidgets.cxx QCMakeWidgets.h RegexExplorer.cxx diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 50e8e3a..21ed8c8 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -34,7 +34,7 @@ const cmDocumentationEntry cmDocumentationUsage = { " cmake-gui [options] <path-to-source>\n" " cmake-gui [options] <path-to-existing-build>\n" " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n" - " cmake-gui [options] --browse-manual" + " cmake-gui [options] --browse-manual [<filename>]" }; const cmDocumentationEntry cmDocumentationOptions[3] = { @@ -62,7 +62,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); int CMakeGUIExec(CMakeSetupDialog* window); void SetupDefaultQSettings(); -void OpenReferenceManual(); +void OpenReferenceManual(const QString& filename); int main(int argc, char** argv) { @@ -199,7 +199,12 @@ int main(int argc, char** argv) } presetName = preset.toStdString(); } else if (arg == "--browse-manual") { - OpenReferenceManual(); + ++i; + if (i >= args.size()) { + OpenReferenceManual("index.html"); + } else { + OpenReferenceManual(args[i]); + } return 0; } } diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index 3d4d726..1effdd3 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -32,6 +32,7 @@ #include "QCMake.h" #include "QCMakeCacheView.h" +#include "QCMakeSizeType.h" #include "cmSystemTools.h" #include "cmVersion.h" @@ -42,7 +43,7 @@ #include "RegexExplorer.h" #include "WarningMessagesDialog.h" -void OpenReferenceManual() +void OpenReferenceManual(const QString& filename) { QString urlFormat("https://cmake.org/cmake/help/v%1.%2/"); QUrl url(urlFormat.arg(QString::number(cmVersion::GetMajorVersion()), @@ -51,7 +52,7 @@ void OpenReferenceManual() if (!cmSystemTools::GetHTMLDoc().empty()) { url = QUrl::fromLocalFile( QDir(QString::fromStdString(cmSystemTools::GetHTMLDoc())) - .filePath("index.html")); + .filePath(filename)); } QDesktopServices::openUrl(url); @@ -212,7 +213,8 @@ CMakeSetupDialog::CMakeSetupDialog() QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doHelp); a->setShortcut(QKeySequence::HelpContents); a = HelpMenu->addAction(tr("CMake Reference Manual")); - QObject::connect(a, &QAction::triggered, this, OpenReferenceManual); + QObject::connect(a, &QAction::triggered, this, + [] { OpenReferenceManual("index.html"); }); a = HelpMenu->addAction(tr("About")); QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doAbout); @@ -1119,12 +1121,12 @@ void CMakeSetupDialog::saveBuildPaths(const QStringList& paths) QSettings settings; settings.beginGroup("Settings/StartPath"); - int num = paths.count(); + cm_qsizetype num = paths.count(); if (num > 10) { num = 10; } - for (int i = 0; i < num; i++) { + for (cm_qsizetype i = 0; i < num; i++) { settings.setValue(QString("WhereBuild%1").arg(i), paths[i]); } } diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx index 707eaae..a454cb6 100644 --- a/Source/QtDialog/FirstConfigure.cxx +++ b/Source/QtDialog/FirstConfigure.cxx @@ -1,6 +1,7 @@ #include "FirstConfigure.h" +#include "QCMakeSizeType.h" #include <QComboBox> #include <QRadioButton> #include <QSettings> @@ -242,10 +243,12 @@ void StartCompilerSetup::onGeneratorChanged(int index) // Default to generator platform from environment if (!DefaultGeneratorPlatform.isEmpty()) { - int platform_index = platforms.indexOf(DefaultGeneratorPlatform); + cm_qsizetype platform_index = + platforms.indexOf(DefaultGeneratorPlatform); if (platform_index != -1) { // The index is off-by-one due to the first empty item added above. - this->PlatformOptions->setCurrentIndex(platform_index + 1); + this->PlatformOptions->setCurrentIndex( + static_cast<int>(platform_index + 1)); } } } else { diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 6b3cb9f..ea02f98 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -6,6 +6,7 @@ #include <cm/memory> +#include "QCMakeSizeType.h" #include <QCoreApplication> #include <QDir> #include <QString> @@ -326,7 +327,7 @@ void QCMake::setProperties(const QCMakePropertyList& newProps) QCMakeProperty prop; prop.Key = QString::fromStdString(key); - int idx = props.indexOf(prop); + cm_qsizetype idx = props.indexOf(prop); if (idx == -1) { toremove.append(QString::fromStdString(key)); } else { diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx index 6f19b67..e67e0c2 100644 --- a/Source/QtDialog/QCMakeCacheView.cxx +++ b/Source/QtDialog/QCMakeCacheView.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "QCMakeCacheView.h" +#include "QCMakeSizeType.h" #include "QCMakeWidgets.h" #include <QApplication> #include <QEvent> @@ -188,7 +189,7 @@ QCMakeCacheModel::~QCMakeCacheModel() = default; static uint qHash(const QCMakeProperty& p) { - return qHash(p.Key); + return static_cast<uint>(qHash(p.Key)); } void QCMakeCacheModel::setShowNewProperties(bool f) @@ -241,7 +242,7 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) bool b = this->blockSignals(true); this->clear(); - this->NewPropertyCount = newProps.size(); + this->NewPropertyCount = static_cast<int>(newProps.size()); if (View == FlatView) { QCMakePropertyList newP = newProps.values(); @@ -297,8 +298,8 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) parentItems[1]->setData(1, GroupRole); root->appendRow(parentItems); - int num = props2.size(); - for (int i = 0; i < num; i++) { + cm_qsizetype num = props2.size(); + for (cm_qsizetype i = 0; i < num; i++) { QCMakeProperty const& prop = props2[i]; QList<QStandardItem*> items; items.append(new QStandardItem()); @@ -319,8 +320,8 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) root->appendRow(parentItem); parentItem->setData(1, GroupRole); - int num = props2.size(); - for (int i = 0; i < num; i++) { + cm_qsizetype num = props2.size(); + for (cm_qsizetype i = 0; i < num; i++) { QCMakeProperty const& prop = props2[i]; QList<QStandardItem*> items; items.append(new QStandardItem()); @@ -349,8 +350,8 @@ void QCMakeCacheModel::setViewType(QCMakeCacheModel::ViewType t) QCMakePropertyList props = this->properties(); QCMakePropertyList oldProps; int numNew = this->NewPropertyCount; - int numTotal = props.count(); - for (int i = numNew; i < numTotal; i++) { + cm_qsizetype numTotal = props.count(); + for (cm_qsizetype i = numNew; i < numTotal; i++) { oldProps.append(props[i]); } diff --git a/Source/QtDialog/QCMakePresetItemModel.cxx b/Source/QtDialog/QCMakePresetItemModel.cxx index 7ada2a5..31a6000 100644 --- a/Source/QtDialog/QCMakePresetItemModel.cxx +++ b/Source/QtDialog/QCMakePresetItemModel.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "QCMakePresetItemModel.h" +#include "QCMakeSizeType.h" #include <QFont> QCMakePresetItemModel::QCMakePresetItemModel(QObject* parent) @@ -27,7 +28,8 @@ QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const if (index.internalId() == SEPARATOR_INDEX) { return QVariant{}; } - auto const& preset = this->m_presets[index.internalId()]; + auto const& preset = + this->m_presets[static_cast<cm_qsizetype>(index.internalId())]; return preset.displayName.isEmpty() ? preset.name : preset.displayName; } case Qt::ToolTipRole: @@ -37,7 +39,8 @@ QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const if (index.internalId() == SEPARATOR_INDEX) { return QVariant{}; } - return this->m_presets[index.internalId()].description; + return this->m_presets[static_cast<cm_qsizetype>(index.internalId())] + .description; case Qt::UserRole: if (index.internalId() == CUSTOM_INDEX) { return QVariant{}; @@ -45,7 +48,8 @@ QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const if (index.internalId() == SEPARATOR_INDEX) { return QVariant{}; } - return QVariant::fromValue(this->m_presets[index.internalId()]); + return QVariant::fromValue( + this->m_presets[static_cast<cm_qsizetype>(index.internalId())]); case Qt::FontRole: if (index.internalId() == CUSTOM_INDEX) { QFont font; @@ -64,7 +68,8 @@ Qt::ItemFlags QCMakePresetItemModel::flags(const QModelIndex& index) const Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; if (index.internalId() != SEPARATOR_INDEX && (index.internalId() == CUSTOM_INDEX || - this->m_presets[index.internalId()].enabled)) { + this->m_presets[static_cast<cm_qsizetype>(index.internalId())] + .enabled)) { flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return flags; @@ -78,7 +83,7 @@ int QCMakePresetItemModel::rowCount(const QModelIndex& parent) const if (this->m_presets.empty()) { return 1; } - return this->m_presets.size() + 2; + return static_cast<int>(this->m_presets.size() + 2); } int QCMakePresetItemModel::columnCount(const QModelIndex& parent) const @@ -139,5 +144,5 @@ int QCMakePresetItemModel::presetNameToRow(const QString& name) const index++; } - return this->m_presets.size() + 1; + return static_cast<int>(this->m_presets.size() + 1); } diff --git a/Source/QtDialog/QCMakeSizeType.h b/Source/QtDialog/QCMakeSizeType.h new file mode 100644 index 0000000..b5cac17 --- /dev/null +++ b/Source/QtDialog/QCMakeSizeType.h @@ -0,0 +1,12 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <QtGlobal> + +// The signed integer type that Qt uses for indexing. +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +using cm_qsizetype = qsizetype; +#else +using cm_qsizetype = int; +#endif diff --git a/Source/bindexplib.cxx b/Source/bindexplib.cxx index 52e200c..3495aed 100644 --- a/Source/bindexplib.cxx +++ b/Source/bindexplib.cxx @@ -281,6 +281,7 @@ public: // the symbol const char* scalarPrefix = "??_G"; const char* vectorPrefix = "??_E"; + const char* vftablePrefix = "??_7"; // The original code had a check for // symbol.find("real@") == std::string::npos) // but this disallows member functions with the name "real". @@ -302,7 +303,8 @@ public: this->DataSymbols.insert(symbol); } else { if (pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ) || - (SectChar & IMAGE_SCN_MEM_EXECUTE)) { + (SectChar & IMAGE_SCN_MEM_EXECUTE) || + (symbol.compare(0, 4, vftablePrefix) == 0)) { this->Symbols.insert(symbol); } } @@ -406,7 +408,7 @@ static bool DumpFile(std::string const& nmPath, const char* filename, LPVOID lpFileBase; hFile = CreateFileW(cmsys::Encoding::ToWide(filename).c_str(), GENERIC_READ, - FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) { @@ -414,7 +416,8 @@ static bool DumpFile(std::string const& nmPath, const char* filename, return false; } - hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + hFileMapping = + CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); if (hFileMapping == 0) { CloseHandle(hFile); fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n"); diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 831e9c7..b1398db 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -49,6 +49,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, bool append = false; bool uses_terminal = false; bool command_expand_lists = false; + bool depends_explicit_only = + mf.IsOn("CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY"); std::string implicit_depends_lang; cmImplicitDependsList implicit_depends; @@ -104,6 +106,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, MAKE_STATIC_KEYWORD(USES_TERMINAL); MAKE_STATIC_KEYWORD(VERBATIM); MAKE_STATIC_KEYWORD(WORKING_DIRECTORY); + MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY); #undef MAKE_STATIC_KEYWORD static std::unordered_set<std::string> const keywords{ keyAPPEND, @@ -126,7 +129,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, keyTARGET, keyUSES_TERMINAL, keyVERBATIM, - keyWORKING_DIRECTORY + keyWORKING_DIRECTORY, + keyDEPENDS_EXPLICIT_ONLY }; for (std::string const& copy : args) { @@ -155,6 +159,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, uses_terminal = true; } else if (copy == keyCOMMAND_EXPAND_LISTS) { command_expand_lists = true; + } else if (copy == keyDEPENDS_EXPLICIT_ONLY) { + depends_explicit_only = true; } else if (copy == keyTARGET) { doing = doing_target; } else if (copy == keyARGS) { @@ -329,6 +335,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, cc->SetDepfile(depfile); cc->SetJobPool(job_pool); cc->SetCommandExpandLists(command_expand_lists); + cc->SetDependsExplicitOnly(depends_explicit_only); if (source.empty() && output.empty()) { // Source is empty, use the target. mf.AddCustomCommandToTarget(target, cctype, std::move(cc)); diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index a065ba9..5601bf2 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -10,6 +10,8 @@ #include <cmext/string_view> #include "cmComputeLinkInformation.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorTarget.h" #include "cmGlobalCommonGenerator.h" #include "cmGlobalGenerator.h" @@ -23,7 +25,6 @@ #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" -#include "cmTarget.h" #include "cmValue.h" cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt) @@ -174,7 +175,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories( // We can ignore the INTERFACE_LIBRARY items because // Target->GetLinkInformation already processed their // link interface and they don't have any output themselves. - && linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY && + && (linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY + // Synthesized targets may have relevant rules. + || linkee->IsSynthetic()) && ((lang == "CXX"_s && linkee->HaveCxx20ModuleSources()) || (lang == "Fortran"_s && linkee->HaveFortranSources(config))) && emitted.insert(linkee).second) { @@ -244,7 +247,7 @@ std::string cmCommonTargetGenerator::GetManifests(const std::string& config) std::string cmCommonTargetGenerator::GetAIXExports(std::string const&) { std::string aixExports; - if (this->GeneratorTarget->Target->IsAIX()) { + if (this->GeneratorTarget->IsAIX()) { if (cmValue exportAll = this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) { if (cmIsOff(*exportAll)) { @@ -288,11 +291,17 @@ std::string cmCommonTargetGenerator::GetLinkerLauncher( const std::string& config) { std::string lang = this->GeneratorTarget->GetLinkerLanguage(config); - cmValue launcherProp = - this->GeneratorTarget->GetProperty(lang + "_LINKER_LAUNCHER"); + std::string propName = lang + "_LINKER_LAUNCHER"; + cmValue launcherProp = this->GeneratorTarget->GetProperty(propName); if (cmNonempty(launcherProp)) { + cmGeneratorExpressionDAGChecker dagChecker(this->GeneratorTarget, propName, + nullptr, nullptr); + std::string evaluatedLinklauncher = cmGeneratorExpression::Evaluate( + *launcherProp, this->LocalCommonGenerator, config, this->GeneratorTarget, + &dagChecker, this->GeneratorTarget, lang); // Convert ;-delimited list to single string - std::vector<std::string> args = cmExpandedList(*launcherProp, true); + std::vector<std::string> args = + cmExpandedList(evaluatedLinklauncher, true); if (!args.empty()) { args[0] = this->LocalCommonGenerator->ConvertToOutputFormat( args[0], cmOutputConverter::SHELL); diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index ad8fb8b..a93477b 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -289,28 +289,28 @@ cmComputeLinkInformation::cmComputeLinkInformation( // Get options needed to link libraries. if (cmValue flag = this->Makefile->GetDefinition( - "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FLAG")) { + cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_FLAG"))) { this->LibLinkFlag = *flag; } else { this->LibLinkFlag = this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG"); } if (cmValue flag = this->Makefile->GetDefinition( - "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FILE_FLAG")) { + cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_FILE_FLAG"))) { this->LibLinkFileFlag = *flag; } else { this->LibLinkFileFlag = this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG"); } if (cmValue suffix = this->Makefile->GetDefinition( - "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_SUFFIX")) { + cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_SUFFIX"))) { this->LibLinkSuffix = *suffix; } else { this->LibLinkSuffix = this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); } if (cmValue flag = this->Makefile->GetDefinition( - "CMAKE_" + this->LinkLanguage + "_LINK_OBJECT_FILE_FLAG")) { + cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_OBJECT_FILE_FLAG"))) { this->ObjLinkFileFlag = *flag; } else { this->ObjLinkFileFlag = @@ -325,7 +325,7 @@ cmComputeLinkInformation::cmComputeLinkInformation( : "SHARED_LIBRARY"); std::string rtVar = cmStrCat("CMAKE_", tType, "_RUNTIME_", this->LinkLanguage, "_FLAG"); - std::string rtSepVar = rtVar + "_SEP"; + std::string rtSepVar = cmStrCat(rtVar, "_SEP"); this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar); this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar); this->RuntimeAlways = (this->Makefile->GetSafeDefinition( @@ -1070,8 +1070,8 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang) if (runtimeLibrary.empty()) { return; } - if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition( - "CMAKE_" + lang + "_RUNTIME_LIBRARY_LINK_OPTIONS_" + runtimeLibrary)) { + if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition(cmStrCat( + "CMAKE_", lang, "_RUNTIME_LIBRARY_LINK_OPTIONS_", runtimeLibrary))) { std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions); for (std::string const& i : libsVec) { if (!cm::contains(this->ImplicitLinkLibs, i)) { @@ -1157,7 +1157,7 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry) // Pass the full path to the target file. BT<std::string> lib = BT<std::string>( tgt->GetFullPath(config, artifact, true), item.Backtrace); - if (tgt->Target->IsAIX() && cmHasLiteralSuffix(lib.Value, "-NOTFOUND") && + if (tgt->IsAIX() && cmHasLiteralSuffix(lib.Value, "-NOTFOUND") && artifact == cmStateEnums::ImportLibraryArtifact) { // This is an imported executable on AIX that has ENABLE_EXPORTS // but not IMPORTED_IMPLIB. CMake used to produce and accept such @@ -1185,7 +1185,7 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry) if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) || (entry.Feature == DEFAULT && cmSystemTools::IsPathToFramework(item.Value) && - this->Makefile->IsOn("APPLE"))) { + this->Target->IsApple())) { // This is a framework. this->AddFrameworkItem(entry); } else if (cmSystemTools::FileIsFullPath(item.Value)) { @@ -1399,10 +1399,9 @@ void cmComputeLinkInformation::ComputeItemParserInfo() reg = "^("; for (std::string const& p : this->LinkPrefixes) { reg += p; - reg += "|"; + reg += '|'; } - reg += ")"; - reg += "([^/:]*)"; + reg += ")([^/:]*)"; // Create a regex to match any library name. std::string reg_any = cmStrCat(reg, libext); @@ -1479,14 +1478,14 @@ std::string cmComputeLinkInformation::CreateExtensionRegex( } // Finish the list. - libext += ")"; + libext += ')'; // Add an optional OpenBSD-style version or major.minor.version component. if (this->OpenBSD || type == LinkShared) { libext += "(\\.[0-9]+)*"; } - libext += "$"; + libext += '$'; return libext; } @@ -1564,7 +1563,7 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) this->OldLinkDirItems.push_back(item.Value); } - if (target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) { + if (target->IsFrameworkOnApple()) { // Add the framework directory and the framework item itself auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( item.Value, cmGlobalGenerator::FrameworkFormat::Extended); @@ -1580,26 +1579,32 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) // Add the directory portion to the framework search path. this->AddFrameworkPath(fwDescriptor->Directory); } - if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) { - this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes, - target, - this->FindLibraryFeature(entry.Feature)); - } else { + + if (this->GlobalGenerator->IsXcode()) { this->Items.emplace_back( item, ItemIsPath::Yes, target, - this->FindLibraryFeature( - entry.Feature == DEFAULT ? "__CMAKE_LINK_LIBRARY" : entry.Feature)); + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "__CMAKE_LINK_FRAMEWORK" + : entry.Feature)); + } else { + if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) { + this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes, + target, + this->FindLibraryFeature(entry.Feature)); + } else { + this->Items.emplace_back( + item, ItemIsPath::Yes, target, + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "__CMAKE_LINK_LIBRARY" + : entry.Feature)); + } } } else { // Now add the full path to the library. this->Items.emplace_back( item, ItemIsPath::Yes, target, this->FindLibraryFeature( - entry.Feature == DEFAULT - ? (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode() - ? "__CMAKE_LINK_FRAMEWORK" - : "__CMAKE_LINK_LIBRARY") - : entry.Feature)); + entry.Feature == DEFAULT ? "__CMAKE_LINK_LIBRARY" : entry.Feature)); } } @@ -1697,7 +1702,8 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry) case cmPolicies::WARN: if (this->CMP0060Warn) { // Print the warning at most once for this item. - std::string const& wid = "CMP0060-WARNING-GIVEN-" + item.Value; + std::string const& wid = + cmStrCat("CMP0060-WARNING-GIVEN-", item.Value); if (!this->CMakeInstance->GetPropertyAsBool(wid)) { this->CMakeInstance->SetProperty(wid, "1"); this->CMP0060WarnItems.insert(item.Value); @@ -1859,8 +1865,8 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) : cmGlobalGenerator::FrameworkFormat::Extended); if (!fwDescriptor) { std::ostringstream e; - e << "Could not parse framework path \"" << item << "\" " - << "linked by target " << this->Target->GetName() << "."; + e << "Could not parse framework path \"" << item << "\" linked by target " + << this->Target->GetName() << '.'; cmSystemTools::Error(e.str()); return; } @@ -1994,9 +2000,9 @@ void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry, std::ostringstream w; /* clang-format off */ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0008) << "\n" - << "Target \"" << this->Target->GetName() << "\" links to item\n" - << " " << item << "\n" - << "which is a full-path but not a valid library file name."; + "Target \"" << this->Target->GetName() << "\" links to item\n" + " " << item << "\n" + "which is a full-path but not a valid library file name."; /* clang-format on */ this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(), this->Target->GetBacktrace()); @@ -2014,9 +2020,9 @@ void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry, std::ostringstream e; /* clang-format off */ e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0008) << "\n" - << "Target \"" << this->Target->GetName() << "\" links to item\n" - << " " << item << "\n" - << "which is a full-path but not a valid library file name."; + "Target \"" << this->Target->GetName() << "\" links to item\n" + " " << item << "\n" + "which is a full-path but not a valid library file name."; /* clang-format on */ this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(), this->Target->GetBacktrace()); @@ -2055,7 +2061,7 @@ bool cmComputeLinkInformation::FinishLinkerSearchDirectories() case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: { std::ostringstream e; - e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0003) << "\n"; + e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0003) << '\n'; this->PrintLinkPolicyDiagnosis(e); this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(), this->Target->GetBacktrace()); @@ -2075,18 +2081,17 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os) // Tell the user what to do. /* clang-format off */ os << "Policy CMP0003 should be set before this line. " - << "Add code such as\n" - << " if(COMMAND cmake_policy)\n" - << " cmake_policy(SET CMP0003 NEW)\n" - << " endif(COMMAND cmake_policy)\n" - << "as early as possible but after the most recent call to " - << "cmake_minimum_required or cmake_policy(VERSION). "; + "Add code such as\n" + " if(COMMAND cmake_policy)\n" + " cmake_policy(SET CMP0003 NEW)\n" + " endif(COMMAND cmake_policy)\n" + "as early as possible but after the most recent call to " + "cmake_minimum_required or cmake_policy(VERSION). "; /* clang-format on */ // List the items that might need the old-style paths. os << "This warning appears because target \"" << this->Target->GetName() - << "\" " - << "links to some libraries for which the linker must search:\n"; + << "\" links to some libraries for which the linker must search:\n"; { // Format the list of unknown items to be as short as possible while // still fitting in the allowed width (a true solution would be the @@ -2099,7 +2104,7 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os) // output the current line and reset it. Note that the separator // is either " " or ", " which is always 2 characters. if (!line.empty() && (line.size() + i.size() + 2) > max_size) { - os << line << "\n"; + os << line << '\n'; sep = " "; line.clear(); } @@ -2109,7 +2114,7 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os) sep = ", "; } if (!line.empty()) { - os << line << "\n"; + os << line << '\n'; } } @@ -2118,17 +2123,17 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os) std::set<std::string> emitted; for (std::string const& i : this->OldLinkDirItems) { if (emitted.insert(cmSystemTools::GetFilenamePath(i)).second) { - os << " " << i << "\n"; + os << " " << i << '\n'; } } // Explain. os << "CMake is adding directories in the second list to the linker " - << "search path in case they are needed to find libraries from the " - << "first list (for backwards compatibility with CMake 2.4). " - << "Set policy CMP0003 to OLD or NEW to enable or disable this " - << "behavior explicitly. " - << "Run \"cmake --help-policy CMP0003\" for more information."; + "search path in case they are needed to find libraries from the " + "first list (for backwards compatibility with CMake 2.4). " + "Set policy CMP0003 to OLD or NEW to enable or disable this " + "behavior explicitly. " + "Run \"cmake --help-policy CMP0003\" for more information."; } void cmComputeLinkInformation::LoadImplicitLinkInfo() @@ -2144,7 +2149,7 @@ void cmComputeLinkInformation::LoadImplicitLinkInfo() if (cmValue libraryArch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { for (std::string const& i : implicitDirVec) { - this->ImplicitLinkDirs.insert(i + "/" + *libraryArch); + this->ImplicitLinkDirs.insert(cmStrCat(i, '/', *libraryArch)); } } @@ -2400,10 +2405,11 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, cmGeneratorTarget::LinkClosure const* lc = this->Target->GetLinkClosure(this->Config); for (std::string const& li : lc->Languages) { - std::string useVar = - "CMAKE_" + li + "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH"; + std::string useVar = cmStrCat( + "CMAKE_", li, "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH"); if (this->Makefile->IsOn(useVar)) { - std::string dirVar = "CMAKE_" + li + "_IMPLICIT_LINK_DIRECTORIES"; + std::string dirVar = + cmStrCat("CMAKE_", li, "_IMPLICIT_LINK_DIRECTORIES"); if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) { cmCLI_ExpandListUnique(*dirs, runtimeDirs, emitted); } diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index acf1c20..3149ccf 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -72,6 +72,10 @@ SETUP_LANGUAGE(swift_properties, Swift); std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES"; std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY"; std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS"; +std::string const kCMAKE_EXECUTABLE_ENABLE_EXPORTS = + "CMAKE_EXECUTABLE_ENABLE_EXPORTS"; +std::string const kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS = + "CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS"; std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES"; std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY"; std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS"; @@ -997,6 +1001,8 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( vars.insert(kCMAKE_CUDA_ARCHITECTURES); vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY); vars.insert(kCMAKE_ENABLE_EXPORTS); + vars.insert(kCMAKE_EXECUTABLE_ENABLE_EXPORTS); + vars.insert(kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS); vars.insert(kCMAKE_HIP_ARCHITECTURES); vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY); vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS); diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx index fd6aee1..24ba368 100644 --- a/Source/cmCurl.cxx +++ b/Source/cmCurl.cxx @@ -131,12 +131,12 @@ std::string cmCurlFixFileURL(std::string url) // Convert string from UTF-8 to ACP if this is a file:// URL. std::wstring wurl = cmsys::Encoding::ToWide(url); if (!wurl.empty()) { - int mblen = - WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, NULL, 0, NULL, NULL); + int mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, nullptr, 0, + nullptr, nullptr); if (mblen > 0) { std::vector<char> chars(mblen); mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0], - mblen, NULL, NULL); + mblen, nullptr, nullptr); if (mblen > 0) { url = &chars[0]; } diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx index 68c65bb..e12cf70 100644 --- a/Source/cmCustomCommand.cxx +++ b/Source/cmCustomCommand.cxx @@ -7,6 +7,8 @@ #include <cmext/algorithm> +#include "cmStateSnapshot.h" + const std::vector<std::string>& cmCustomCommand::GetOutputs() const { return this->Outputs; @@ -162,6 +164,16 @@ void cmCustomCommand::SetCommandExpandLists(bool b) this->CommandExpandLists = b; } +bool cmCustomCommand::GetDependsExplicitOnly() const +{ + return this->DependsExplicitOnly; +} + +void cmCustomCommand::SetDependsExplicitOnly(bool b) +{ + this->DependsExplicitOnly = b; +} + const std::string& cmCustomCommand::GetDepfile() const { return this->Depfile; @@ -182,14 +194,19 @@ void cmCustomCommand::SetJobPool(const std::string& job_pool) this->JobPool = job_pool; } -cmPolicies::PolicyStatus cmCustomCommand::GetCMP0116Status() const -{ - return this->CMP0116Status; -} +#define DEFINE_CC_POLICY_ACCESSOR(P) \ + cmPolicies::PolicyStatus cmCustomCommand::Get##P##Status() const \ + { \ + return this->P##Status; \ + } +CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DEFINE_CC_POLICY_ACCESSOR) +#undef DEFINE_CC_POLICY_ACCESSOR -void cmCustomCommand::SetCMP0116Status(cmPolicies::PolicyStatus cmp0116) +void cmCustomCommand::RecordPolicyValues(const cmStateSnapshot& snapshot) { - this->CMP0116Status = cmp0116; +#define SET_CC_POLICY(P) this->P##Status = snapshot.GetPolicy(cmPolicies::P); + CM_FOR_EACH_CUSTOM_COMMAND_POLICY(SET_CC_POLICY) +#undef SET_CC_POLICY } const std::string& cmCustomCommand::GetTarget() const diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index 5533847..1e68dbf 100644 --- a/Source/cmCustomCommand.h +++ b/Source/cmCustomCommand.h @@ -17,6 +17,8 @@ class cmImplicitDependsList { }; +class cmStateSnapshot; + /** \class cmCustomCommand * \brief A class to encapsulate a custom command * @@ -100,6 +102,11 @@ public: bool GetCommandExpandLists() const; void SetCommandExpandLists(bool b); + /** Set/Get whether to use additional dependencies coming from + users of OUTPUT of the custom command. */ + bool GetDependsExplicitOnly() const; + void SetDependsExplicitOnly(bool b); + /** Set/Get the depfile (used by the Ninja generator) */ const std::string& GetDepfile() const; void SetDepfile(const std::string& depfile); @@ -108,9 +115,13 @@ public: const std::string& GetJobPool() const; void SetJobPool(const std::string& job_pool); - /** Set/Get the CMP0116 status (used by the Ninja generator) */ - cmPolicies::PolicyStatus GetCMP0116Status() const; - void SetCMP0116Status(cmPolicies::PolicyStatus cmp0116); +#define DECLARE_CC_POLICY_ACCESSOR(P) \ + cmPolicies::PolicyStatus Get##P##Status() const; + CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DECLARE_CC_POLICY_ACCESSOR) +#undef DECLARE_CC_POLICY_ACCESSOR + + /** Record policy values from state snapshot */ + void RecordPolicyValues(const cmStateSnapshot& snapshot); /** Set/Get the associated target */ const std::string& GetTarget() const; @@ -135,5 +146,12 @@ private: bool CommandExpandLists = false; bool StdPipesUTF8 = false; bool HasMainDependency_ = false; - cmPolicies::PolicyStatus CMP0116Status = cmPolicies::WARN; + bool DependsExplicitOnly = false; + +// Policies are NEW for synthesized custom commands, and set by cmMakefile for +// user-created custom commands. +#define DECLARE_CC_POLICY_FIELD(P) \ + cmPolicies::PolicyStatus P##Status = cmPolicies::NEW; + CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DECLARE_CC_POLICY_FIELD) +#undef DECLARE_CC_POLICY_FIELD }; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index ed199ea..caf8ac2 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -254,7 +254,7 @@ void cmExportBuildFileGenerator::SetImportLocationProperty( if (target->HasImportLibrary(config)) { std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix); std::string value = - target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact); + target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact, true); if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) { target->GetImplibGNUtoMS(config, value, value, "${CMAKE_IMPORT_LIBRARY_SUFFIX}"); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index c8e2cb8..6e7ef4e 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -938,13 +938,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) // Isolate the file policy level. // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.24 (this upper limit may be reviewed + // policy settings for up to CMake 3.25 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. /* clang-format off */ os << "cmake_policy(PUSH)\n" - << "cmake_policy(VERSION 2.8.3...3.24)\n"; + << "cmake_policy(VERSION 2.8.3...3.25)\n"; /* clang-format on */ } @@ -1063,7 +1063,8 @@ void cmExportFileGenerator::GenerateImportTargetCode( } // Mark the imported executable if it has exports. - if (target->IsExecutableWithExports()) { + if (target->IsExecutableWithExports() || + (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) { os << "set_property(TARGET " << targetName << " PROPERTY ENABLE_EXPORTS 1)\n"; } diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 5e190f4..def8227 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -409,7 +409,7 @@ void cmExportInstallFileGenerator::SetImportLocationProperty( // Append the installed file name. value += cmInstallTargetGenerator::GetInstallFilename( - target, config, cmInstallTargetGenerator::NameImplib); + target, config, cmInstallTargetGenerator::NameImplibReal); // Store the property. properties[prop] = value; @@ -430,6 +430,19 @@ void cmExportInstallFileGenerator::SetImportLocationProperty( properties[prop] = cmJoin(objects, ";"); importedLocations.insert(prop); } else { + if (target->IsFrameworkOnApple() && target->HasImportLibrary(config)) { + // store as well IMPLIB value + auto importProp = cmStrCat("IMPORTED_IMPLIB", suffix); + auto importValue = + cmStrCat(value, + cmInstallTargetGenerator::GetInstallFilename( + target, config, cmInstallTargetGenerator::NameImplibReal)); + + // Store the property. + properties[importProp] = importValue; + importedLocations.insert(importProp); + } + // Construct the property name. std::string prop = cmStrCat("IMPORTED_LOCATION", suffix); diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 33c057d..c6ebad5 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -85,7 +85,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets( std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*prop); cmTarget dummyHead("try_compile_dummy_exe", cmStateEnums::EXECUTABLE, - cmTarget::VisibilityNormal, tgt->Target->GetMakefile(), + cmTarget::Visibility::Normal, tgt->Target->GetMakefile(), cmTarget::PerConfig::Yes); cmGeneratorTarget gDummyHead(&dummyHead, tgt->GetLocalGenerator()); diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index 988c5c3..e9e2921 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -45,7 +45,7 @@ cmExtraCodeBlocksGenerator::GetFactory() { static cmExternalMakefileProjectGeneratorSimpleFactory< cmExtraCodeBlocksGenerator> - factory("CodeBlocks", "Generates CodeBlocks project files."); + factory("CodeBlocks", "Generates CodeBlocks project files (deprecated)."); if (factory.GetSupportedGlobalGenerators().empty()) { #if defined(_WIN32) diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index 9e8ac5c..7538a7f 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -33,7 +33,7 @@ cmExtraCodeLiteGenerator::GetFactory() { static cmExternalMakefileProjectGeneratorSimpleFactory< cmExtraCodeLiteGenerator> - factory("CodeLite", "Generates CodeLite project files."); + factory("CodeLite", "Generates CodeLite project files (deprecated)."); if (factory.GetSupportedGlobalGenerators().empty()) { #if defined(_WIN32) diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index a07acdc..6201889 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -63,7 +63,8 @@ cmExtraEclipseCDT4Generator::GetFactory() { static cmExternalMakefileProjectGeneratorSimpleFactory< cmExtraEclipseCDT4Generator> - factory("Eclipse CDT4", "Generates Eclipse CDT 4.0 project files."); + factory("Eclipse CDT4", + "Generates Eclipse CDT 4.0 project files (deprecated)."); if (factory.GetSupportedGlobalGenerators().empty()) { // TODO: Verify if __CYGWIN__ should be checked. diff --git a/Source/cmExtraKateGenerator.cxx b/Source/cmExtraKateGenerator.cxx index eec43c4..6e6d009 100644 --- a/Source/cmExtraKateGenerator.cxx +++ b/Source/cmExtraKateGenerator.cxx @@ -8,6 +8,7 @@ #include <set> #include <vector> +#include "cmCMakePath.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -24,7 +25,7 @@ cmExtraKateGenerator::cmExtraKateGenerator() = default; cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory() { static cmExternalMakefileProjectGeneratorSimpleFactory<cmExtraKateGenerator> - factory("Kate", "Generates Kate project files."); + factory("Kate", "Generates Kate project files (deprecated)."); if (factory.GetSupportedGlobalGenerators().empty()) { #if defined(_WIN32) @@ -34,6 +35,7 @@ cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory() // factory.AddSupportedGlobalGenerator("MSYS Makefiles"); #endif factory.AddSupportedGlobalGenerator("Ninja"); + factory.AddSupportedGlobalGenerator("Ninja Multi-Config"); factory.AddSupportedGlobalGenerator("Unix Makefiles"); } @@ -47,7 +49,9 @@ void cmExtraKateGenerator::Generate() this->ProjectName = this->GenerateProjectName( lg->GetProjectName(), mf->GetSafeDefinition("CMAKE_BUILD_TYPE"), this->GetPathBasename(lg->GetBinaryDirectory())); - this->UseNinja = (this->GlobalGenerator->GetName() == "Ninja"); + this->UseNinja = + ((this->GlobalGenerator->GetName() == "Ninja") || + (this->GlobalGenerator->GetName() == "Ninja Multi-Config")); this->CreateKateProjectFile(*lg); this->CreateDummyKateProjectFile(*lg); @@ -81,6 +85,7 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg, const std::string& makeArgs = mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS"); std::string const& homeOutputDir = lg.GetBinaryDirectory(); + const auto configs = mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); /* clang-format off */ fout << @@ -104,16 +109,16 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg, // this is for kate >= 4.13: fout << "\t\t\"targets\":[\n"; - this->AppendTarget(fout, "all", make, makeArgs, homeOutputDir, + this->AppendTarget(fout, "all", configs, make, makeArgs, homeOutputDir, homeOutputDir); - this->AppendTarget(fout, "clean", make, makeArgs, homeOutputDir, + this->AppendTarget(fout, "clean", configs, make, makeArgs, homeOutputDir, homeOutputDir); // add all executable and library targets and some of the GLOBAL // and UTILITY targets for (const auto& localGen : this->GlobalGenerator->GetLocalGenerators()) { const auto& targets = localGen->GetGeneratorTargets(); - std::string currentDir = localGen->GetCurrentBinaryDirectory(); + const std::string currentDir = localGen->GetCurrentBinaryDirectory(); bool topLevel = (currentDir == localGen->GetBinaryDirectory()); for (const auto& target : targets) { @@ -137,8 +142,8 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg, } } if (insertTarget) { - this->AppendTarget(fout, targetName, make, makeArgs, currentDir, - homeOutputDir); + this->AppendTarget(fout, targetName, configs, make, makeArgs, + currentDir, homeOutputDir); } } break; case cmStateEnums::UTILITY: @@ -153,19 +158,21 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg, break; } - this->AppendTarget(fout, targetName, make, makeArgs, currentDir, - homeOutputDir); + this->AppendTarget(fout, targetName, configs, make, makeArgs, + currentDir, homeOutputDir); break; case cmStateEnums::EXECUTABLE: case cmStateEnums::STATIC_LIBRARY: case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::MODULE_LIBRARY: case cmStateEnums::OBJECT_LIBRARY: { - this->AppendTarget(fout, targetName, make, makeArgs, currentDir, - homeOutputDir); - std::string fastTarget = cmStrCat(targetName, "/fast"); - this->AppendTarget(fout, fastTarget, make, makeArgs, currentDir, - homeOutputDir); + this->AppendTarget(fout, targetName, configs, make, makeArgs, + currentDir, homeOutputDir); + if (!this->UseNinja) { + std::string fastTarget = cmStrCat(targetName, "/fast"); + this->AppendTarget(fout, fastTarget, configs, make, makeArgs, + currentDir, homeOutputDir); + } } break; default: @@ -178,29 +185,36 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg, std::vector<std::string> objectFileTargets; localGen->GetIndividualFileTargets(objectFileTargets); for (std::string const& f : objectFileTargets) { - this->AppendTarget(fout, f, make, makeArgs, currentDir, homeOutputDir); + this->AppendTarget(fout, f, configs, make, makeArgs, currentDir, + homeOutputDir); } } fout << "\t] }\n"; } -void cmExtraKateGenerator::AppendTarget(cmGeneratedFileStream& fout, - const std::string& target, - const std::string& make, - const std::string& makeArgs, - const std::string& path, - const std::string& homeOutputDir) const +void cmExtraKateGenerator::AppendTarget( + cmGeneratedFileStream& fout, const std::string& target, + const std::vector<std::string>& configs, const std::string& make, + const std::string& makeArgs, const std::string& path, + const std::string& homeOutputDir) const { static char JsonSep = ' '; - fout << "\t\t\t" << JsonSep << R"({"name":")" << target - << "\", " - "\"build_cmd\":\"" - << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path) - << "\\\" " << makeArgs << " " << target << "\"}\n"; - - JsonSep = ','; + for (const std::string& conf : configs) { + fout << "\t\t\t" << JsonSep << R"({"name":")" << target + << ((configs.size() > 1) ? (std::string(":") + conf) : std::string()) + << "\", " + "\"build_cmd\":\"" + << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path) + << "\\\" " + << ((this->UseNinja && configs.size() > 1) + ? std::string(" -f build-") + conf + ".ninja" + : std::string()) + << makeArgs << " " << target << "\"}\n"; + + JsonSep = ','; + } } void cmExtraKateGenerator::CreateDummyKateProjectFile( @@ -220,17 +234,58 @@ void cmExtraKateGenerator::CreateDummyKateProjectFile( std::string cmExtraKateGenerator::GenerateFilesString( const cmLocalGenerator& lg) const { - std::string s = cmStrCat(lg.GetSourceDirectory(), "/.git"); - if (cmSystemTools::FileExists(s)) { - return "\"git\": 1 "; + const cmMakefile* mf = lg.GetMakefile(); + std::string mode = + cmSystemTools::UpperCase(mf->GetSafeDefinition("CMAKE_KATE_FILES_MODE")); + static const std::string gitString = "\"git\": 1 "; + static const std::string svnString = "\"svn\": 1 "; + static const std::string hgString = "\"hg\": 1 "; + static const std::string fossilString = "\"fossil\": 1 "; + + if (mode == "SVN") { + return svnString; } - - s = cmStrCat(lg.GetSourceDirectory(), "/.svn"); - if (cmSystemTools::FileExists(s)) { - return "\"svn\": 1 "; + if (mode == "GIT") { + return gitString; } + if (mode == "HG") { + return hgString; + } + if (mode == "FOSSIL") { + return fossilString; + } + + // check for the VCS files except when "forced" to "FILES" mode: + if (mode != "LIST") { + cmCMakePath startDir(lg.GetSourceDirectory(), cmCMakePath::auto_format); + // move the directories up to the root directory to see whether we are in + // a subdir of a svn, git, hg or fossil checkout + for (;;) { + std::string s = startDir.String() + "/.git"; + if (cmSystemTools::FileExists(s)) { + return gitString; + } - s = cmStrCat(lg.GetSourceDirectory(), '/'); + s = startDir.String() + "/.svn"; + if (cmSystemTools::FileExists(s)) { + return svnString; + } + + s = startDir.String() + "/.hg"; + if (cmSystemTools::FileExists(s)) { + return hgString; + } + s = startDir.String() + "/.fslckout"; + if (cmSystemTools::FileExists(s)) { + return fossilString; + } + + if (!startDir.HasRelativePath()) { // have we reached the root dir ? + break; + } + startDir = startDir.GetParentPath(); + } + } std::set<std::string> files; std::string tmp; @@ -240,9 +295,8 @@ std::string cmExtraKateGenerator::GenerateFilesString( cmMakefile* makefile = lgen->GetMakefile(); const std::vector<std::string>& listFiles = makefile->GetListFiles(); for (std::string const& listFile : listFiles) { - tmp = listFile; - { - files.insert(tmp); + if (listFile.find("/CMakeFiles/") == std::string::npos) { + files.insert(listFile); } } diff --git a/Source/cmExtraKateGenerator.h b/Source/cmExtraKateGenerator.h index c66ddbf..203fa2f 100644 --- a/Source/cmExtraKateGenerator.h +++ b/Source/cmExtraKateGenerator.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <string> +#include <vector> #include "cmExternalMakefileProjectGenerator.h" @@ -29,6 +30,7 @@ private: void WriteTargets(const cmLocalGenerator& lg, cmGeneratedFileStream& fout) const; void AppendTarget(cmGeneratedFileStream& fout, const std::string& target, + const std::vector<std::string>& configs, const std::string& make, const std::string& makeArgs, const std::string& path, const std::string& homeOutputDir) const; diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx index 19e87d5..33901ac 100644 --- a/Source/cmExtraSublimeTextGenerator.cxx +++ b/Source/cmExtraSublimeTextGenerator.cxx @@ -43,7 +43,8 @@ cmExtraSublimeTextGenerator::GetFactory() { static cmExternalMakefileProjectGeneratorSimpleFactory< cmExtraSublimeTextGenerator> - factory("Sublime Text 2", "Generates Sublime Text 2 project files."); + factory("Sublime Text 2", + "Generates Sublime Text 2 project files (deprecated)."); if (factory.GetSupportedGlobalGenerators().empty()) { #if defined(_WIN32) diff --git a/Source/cmFileLockResult.cxx b/Source/cmFileLockResult.cxx index 70b8cdb..b7f7f38 100644 --- a/Source/cmFileLockResult.cxx +++ b/Source/cmFileLockResult.cxx @@ -56,9 +56,9 @@ std::string cmFileLockResult::GetOutputMessage() const # define WINMSG_BUF_LEN (1024) char winmsg[WINMSG_BUF_LEN]; DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - if (FormatMessageA(flags, NULL, this->ErrorValue, + if (FormatMessageA(flags, nullptr, this->ErrorValue, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)winmsg, WINMSG_BUF_LEN, NULL)) { + (LPSTR)winmsg, WINMSG_BUF_LEN, nullptr)) { const std::string message = winmsg; return message; } else { diff --git a/Source/cmFileLockWin32.cxx b/Source/cmFileLockWin32.cxx index b8e435a..7bee5f2 100644 --- a/Source/cmFileLockWin32.cxx +++ b/Source/cmFileLockWin32.cxx @@ -36,9 +36,9 @@ cmFileLockResult cmFileLock::OpenFile() { const DWORD access = GENERIC_READ | GENERIC_WRITE; const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - const PSECURITY_ATTRIBUTES security = NULL; + const PSECURITY_ATTRIBUTES security = nullptr; const DWORD attr = 0; - const HANDLE templ = NULL; + const HANDLE templ = nullptr; this->File = CreateFileW( cmSystemTools::ConvertToWindowsExtendedPath(this->Filename).c_str(), access, shareMode, security, OPEN_EXISTING, attr, templ); diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index c1b82ab..8acfe83 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -502,6 +502,7 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status) this->DebugMode = false; this->AppendSearchPathGroups(); + this->DeprecatedFindModules["Dart"] = cmPolicies::CMP0145; this->DeprecatedFindModules["Qt"] = cmPolicies::CMP0084; } @@ -975,35 +976,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } - { - // Allocate a PACKAGE_ROOT_PATH for the current find_package call. - this->Makefile->FindPackageRootPathStack.emplace_back(); - std::vector<std::string>& rootPaths = - this->Makefile->FindPackageRootPathStack.back(); - - // Add root paths from <PackageName>_ROOT CMake and environment variables, - // subject to CMP0074. - switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0074)) { - case cmPolicies::WARN: - this->Makefile->MaybeWarnCMP0074(this->Name); - CM_FALLTHROUGH; - case cmPolicies::OLD: - // OLD behavior is to ignore the <pkg>_ROOT variables. - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0074)); - break; - case cmPolicies::NEW: { - // NEW behavior is to honor the <pkg>_ROOT variables. - std::string const rootVar = this->Name + "_ROOT"; - this->Makefile->GetDefExpandList(rootVar, rootPaths, false); - cmSystemTools::GetPath(rootPaths, rootVar.c_str()); - } break; - } - } + this->PushFindPackageRootPathStack(); this->SetModuleVariables(components, componentVarDefs); @@ -1129,8 +1102,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Restore original state of "_FIND_" variables set in SetModuleVariables() this->RestoreFindDefinitions(); - // Pop the package stack - this->Makefile->FindPackageRootPathStack.pop_back(); + this->PopFindPackageRootPathStack(); if (!this->DebugBuffer.empty()) { this->DebugMessage(this->DebugBuffer); @@ -1834,6 +1806,99 @@ void cmFindPackageCommand::AppendSuccessInformation() } } +void cmFindPackageCommand::PushFindPackageRootPathStack() +{ + // Allocate a PACKAGE_ROOT_PATH for the current find_package call. + this->Makefile->FindPackageRootPathStack.emplace_back(); + std::vector<std::string>& rootPaths = + this->Makefile->FindPackageRootPathStack.back(); + + // Add root paths from <PackageName>_ROOT CMake and environment variables, + // subject to CMP0074. + std::string const rootVar = this->Name + "_ROOT"; + cmValue rootDef = this->Makefile->GetDefinition(rootVar); + if (rootDef && rootDef.IsEmpty()) { + rootDef = nullptr; + } + cm::optional<std::string> rootEnv = cmSystemTools::GetEnvVar(rootVar); + if (rootEnv && rootEnv->empty()) { + rootEnv = cm::nullopt; + } + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0074)) { + case cmPolicies::WARN: + this->Makefile->MaybeWarnCMP0074(rootVar, rootDef, rootEnv); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to ignore the <PackageName>_ROOT variables. + return; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0074)); + return; + case cmPolicies::NEW: { + // NEW behavior is to honor the <PackageName>_ROOT variables. + } break; + } + + // Add root paths from <PACKAGENAME>_ROOT CMake and environment variables, + // if they are different than <PackageName>_ROOT, and subject to CMP0144. + std::string const rootVAR = cmSystemTools::UpperCase(rootVar); + cmValue rootDEF; + cm::optional<std::string> rootENV; + if (rootVAR != rootVar) { + rootDEF = this->Makefile->GetDefinition(rootVAR); + if (rootDEF && (rootDEF.IsEmpty() || rootDEF == rootDef)) { + rootDEF = nullptr; + } + rootENV = cmSystemTools::GetEnvVar(rootVAR); + if (rootENV && (rootENV->empty() || rootENV == rootEnv)) { + rootENV = cm::nullopt; + } + } + + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0144)) { + case cmPolicies::WARN: + this->Makefile->MaybeWarnCMP0144(rootVAR, rootDEF, rootENV); + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to ignore the <PACKAGENAME>_ROOT variables. + rootDEF = nullptr; + rootENV = cm::nullopt; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0144)); + return; + case cmPolicies::NEW: { + // NEW behavior is to honor the <PACKAGENAME>_ROOT variables. + } break; + } + + if (rootDef) { + cmExpandList(*rootDef, rootPaths); + } + if (rootDEF) { + cmExpandList(*rootDEF, rootPaths); + } + if (rootEnv) { + std::vector<std::string> p = cmSystemTools::SplitEnvPath(*rootEnv); + std::move(p.begin(), p.end(), std::back_inserter(rootPaths)); + } + if (rootENV) { + std::vector<std::string> p = cmSystemTools::SplitEnvPath(*rootENV); + std::move(p.begin(), p.end(), std::back_inserter(rootPaths)); + } +} + +void cmFindPackageCommand::PopFindPackageRootPathStack() +{ + this->Makefile->FindPackageRootPathStack.pop_back(); +} + void cmFindPackageCommand::ComputePrefixes() { this->FillPrefixesPackageRedirect(); diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 28e00a1..18684c9 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -125,6 +125,9 @@ private: void StoreVersionFound(); void SetConfigDirCacheVariable(const std::string& value); + void PushFindPackageRootPathStack(); + void PopFindPackageRootPathStack(); + void ComputePrefixes(); void FillPrefixesPackageRedirect(); void FillPrefixesPackageRoot(); diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index 6be5153..82a6c57 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -177,6 +177,15 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const return property == "LINK_OPTIONS"_s; } +bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const +{ + cm::string_view property(this->Top()->Property); + + return property.length() > cmStrLen("_LINKER_LAUNCHER") && + property.substr(property.length() - cmStrLen("_LINKER_LAUNCHER")) == + "_LINKER_LAUNCHER"_s; +} + bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries( cmGeneratorTarget const* tgt, ForGenex genex) const { diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 55d131f..df1e005 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -70,6 +70,7 @@ struct cmGeneratorExpressionDAGChecker bool EvaluatingCompileExpression() const; bool EvaluatingLinkExpression() const; bool EvaluatingLinkOptionsExpression() const; + bool EvaluatingLinkerLauncher() const; enum class ForGenex { diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 6595323..6e78f55 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -688,6 +688,14 @@ static const struct PathNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { + static auto processList = + [](std::string const& arg, + std::function<void(std::string&)> transform) -> std::string { + auto list = cmExpandedList(arg); + std::for_each(list.begin(), list.end(), std::move(transform)); + return cmJoin(list, ";"); + }; + static std::unordered_map< cm::string_view, std::function<std::string(cmGeneratorExpressionContext*, @@ -698,38 +706,49 @@ static const struct PathNode : public cmGeneratorExpressionNode [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.GetRootName().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetRootName().String(); + }); + } + return std::string{}; } }, { "GET_ROOT_DIRECTORY"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s, - args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.GetRootDirectory().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetRootDirectory().String(); + }); + } + return std::string{}; } }, { "GET_ROOT_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.GetRootPath().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetRootPath().String(); + }); + } + return std::string{}; } }, { "GET_FILENAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.GetFileName().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetFileName().String(); + }); + } + return std::string{}; } }, { "GET_EXTENSION"_s, [](cmGeneratorExpressionContext* ctx, @@ -746,9 +765,14 @@ static const struct PathNode : public cmGeneratorExpressionNode if (args.front().empty()) { return std::string{}; } - return lastOnly - ? cmCMakePath{ args.front() }.GetExtension().String() - : cmCMakePath{ args.front() }.GetWideExtension().String(); + if (lastOnly) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetExtension().String(); + }); + } + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetWideExtension().String(); + }); } return std::string{}; } }, @@ -766,9 +790,14 @@ static const struct PathNode : public cmGeneratorExpressionNode if (args.front().empty()) { return std::string{}; } - return lastOnly - ? cmCMakePath{ args.front() }.GetStem().String() - : cmCMakePath{ args.front() }.GetNarrowStem().String(); + if (lastOnly) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetStem().String(); + }); + } + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetNarrowStem().String(); + }); } return std::string{}; } }, @@ -776,19 +805,24 @@ static const struct PathNode : public cmGeneratorExpressionNode [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s, - args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.GetRelativePath().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetRelativePath().String(); + }); + } + return std::string{}; } }, { "GET_PARENT_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args) - ? cmCMakePath{ args.front() }.GetParentPath().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.GetParentPath().String(); + }); + } + return std::string{}; } }, { "HAS_ROOT_NAME"_s, [](cmGeneratorExpressionContext* ctx, @@ -904,10 +938,12 @@ static const struct PathNode : public cmGeneratorExpressionNode normalize ? "CMAKE_PATH,NORMALIZE"_s : "CMAKE_PATH"_s, args.size(), 1)) { - auto path = - cmCMakePath{ args.front(), cmCMakePath::auto_format }; - return normalize ? path.Normal().GenericString() - : path.GenericString(); + return processList( + args.front(), [normalize](std::string& value) { + auto path = cmCMakePath{ value, cmCMakePath::auto_format }; + value = normalize ? path.Normal().GenericString() + : path.GenericString(); + }); } return std::string{}; } }, @@ -917,11 +953,16 @@ static const struct PathNode : public cmGeneratorExpressionNode Arguments& args) -> std::string { if (CheckPathParametersEx(ctx, cnt, "APPEND"_s, args.size(), 1, false)) { - cmCMakePath path; - for (const auto& p : args) { - path /= p; - } - return path.String(); + auto const& list = args.front(); + args.advance(1); + + return processList(list, [&args](std::string& value) { + cmCMakePath path{ value }; + for (const auto& p : args) { + path /= p; + } + value = path.String(); + }); } return std::string{}; } }, @@ -929,20 +970,26 @@ static const struct PathNode : public cmGeneratorExpressionNode [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.RemoveFileName().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.RemoveFileName().String(); + }); + } + return std::string{}; } }, { "REPLACE_FILENAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2) - ? cmCMakePath{ args[0] } - .ReplaceFileName(cmCMakePath{ args[1] }) - .String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)) { + return processList(args.front(), [&args](std::string& value) { + value = cmCMakePath{ value } + .ReplaceFileName(cmCMakePath{ args[1] }) + .String(); + }); + } + return std::string{}; } }, { "REMOVE_EXTENSION"_s, [](cmGeneratorExpressionContext* ctx, @@ -959,9 +1006,14 @@ static const struct PathNode : public cmGeneratorExpressionNode if (args.front().empty()) { return std::string{}; } - return lastOnly - ? cmCMakePath{ args.front() }.RemoveExtension().String() - : cmCMakePath{ args.front() }.RemoveWideExtension().String(); + if (lastOnly) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.RemoveExtension().String(); + }); + } + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.RemoveWideExtension().String(); + }); } return std::string{}; } }, @@ -979,13 +1031,17 @@ static const struct PathNode : public cmGeneratorExpressionNode : "REPLACE_EXTENSION"_s, args.size(), 2)) { if (lastOnly) { - return cmCMakePath{ args[0] } - .ReplaceExtension(cmCMakePath{ args[1] }) - .String(); + return processList(args.front(), [&args](std::string& value) { + value = cmCMakePath{ value } + .ReplaceExtension(cmCMakePath{ args[1] }) + .String(); + }); } - return cmCMakePath{ args[0] } - .ReplaceWideExtension(cmCMakePath{ args[1] }) - .String(); + return processList(args.front(), [&args](std::string& value) { + value = cmCMakePath{ value } + .ReplaceWideExtension(cmCMakePath{ args[1] }) + .String(); + }); } return std::string{}; } }, @@ -993,18 +1049,24 @@ static const struct PathNode : public cmGeneratorExpressionNode [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) && - !args.front().empty() - ? cmCMakePath{ args.front() }.Normal().String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) && + !args.front().empty()) { + return processList(args.front(), [](std::string& value) { + value = cmCMakePath{ value }.Normal().String(); + }); + } + return std::string{}; } }, { "RELATIVE_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { - return CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2) - ? cmCMakePath{ args[0] }.Relative(args[1]).String() - : std::string{}; + if (CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)) { + return processList(args.front(), [&args](std::string& value) { + value = cmCMakePath{ value }.Relative(args[1]).String(); + }); + } + return std::string{}; } }, { "ABSOLUTE_PATH"_s, [](cmGeneratorExpressionContext* ctx, @@ -1018,8 +1080,11 @@ static const struct PathNode : public cmGeneratorExpressionNode normalize ? "ABSOLUTE_PATH,NORMALIZE"_s : "ABSOLUTE_PATH"_s, args.size(), 2)) { - auto path = cmCMakePath{ args[0] }.Absolute(args[1]); - return normalize ? path.Normal().String() : path.String(); + return processList( + args.front(), [&args, normalize](std::string& value) { + auto path = cmCMakePath{ value }.Absolute(args[1]); + value = normalize ? path.Normal().String() : path.String(); + }); } return std::string{}; } } @@ -1468,7 +1533,8 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode genName.find("Ninja") == std::string::npos && genName.find("Visual Studio") == std::string::npos && genName.find("Xcode") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) { + genName.find("Watcom WMake") == std::string::npos && + genName.find("Green Hills MULTI") == std::string::npos) { reportError(context, content->GetOriginalExpression(), "$<COMPILE_LANGUAGE:...> not supported for this generator."); return std::string(); @@ -1516,7 +1582,8 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode genName.find("Ninja") == std::string::npos && genName.find("Visual Studio") == std::string::npos && genName.find("Xcode") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) { + genName.find("Watcom WMake") == std::string::npos && + genName.find("Green Hills MULTI") == std::string::npos) { reportError( context, content->GetOriginalExpression(), "$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator."); @@ -1548,7 +1615,8 @@ static const struct LinkLanguageNode : public cmGeneratorExpressionNode { if (!context->HeadTarget || !dagChecker || !(dagChecker->EvaluatingLinkExpression() || - dagChecker->EvaluatingLinkLibraries())) { + dagChecker->EvaluatingLinkLibraries() || + dagChecker->EvaluatingLinkerLauncher())) { reportError(context, content->GetOriginalExpression(), "$<LINK_LANGUAGE:...> may only be used with binary targets " "to specify link libraries, link directories, link options " @@ -1568,7 +1636,8 @@ static const struct LinkLanguageNode : public cmGeneratorExpressionNode genName.find("Ninja") == std::string::npos && genName.find("Visual Studio") == std::string::npos && genName.find("Xcode") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) { + genName.find("Watcom WMake") == std::string::npos && + genName.find("Green Hills MULTI") == std::string::npos) { reportError(context, content->GetOriginalExpression(), "$<LINK_LANGUAGE:...> not supported for this generator."); return std::string(); @@ -1641,7 +1710,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode { if (!context->HeadTarget || !dagChecker || !(dagChecker->EvaluatingLinkExpression() || - dagChecker->EvaluatingLinkLibraries())) { + dagChecker->EvaluatingLinkLibraries() || + dagChecker->EvaluatingLinkerLauncher())) { reportError( context, content->GetOriginalExpression(), "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets " @@ -1656,7 +1726,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode genName.find("Ninja") == std::string::npos && genName.find("Visual Studio") == std::string::npos && genName.find("Xcode") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) { + genName.find("Watcom WMake") == std::string::npos && + genName.find("Green Hills MULTI") == std::string::npos) { reportError( context, content->GetOriginalExpression(), "$<LINK_LANG_AND_ID:lang,id> not supported for this generator."); @@ -2098,7 +2169,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode if (dagCheckerParent) { if (dagCheckerParent->EvaluatingGenexExpression() || - dagCheckerParent->EvaluatingPICExpression()) { + dagCheckerParent->EvaluatingPICExpression() || + dagCheckerParent->EvaluatingLinkerLauncher()) { // No check required. } else if (dagCheckerParent->EvaluatingLinkLibraries()) { evaluatingLinkLibraries = true; @@ -2330,15 +2402,12 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode } } targetObjectsNode; -static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode +struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode { - TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default) - - std::string Evaluate( + std::vector<std::string> CollectDlls( const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + const GeneratorExpressionContent* content) const { std::string const& tgtName = parameters.front(); cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName); @@ -2347,7 +2416,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode e << "Objects of target \"" << tgtName << "\" referenced but no such target exists."; reportError(context, content->GetOriginalExpression(), e.str()); - return std::string(); + return std::vector<std::string>(); } cmStateEnums::TargetType type = gt->GetType(); if (type != cmStateEnums::EXECUTABLE && @@ -2358,7 +2427,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode << "\" referenced but is not one of the allowed target types " << "(EXECUTABLE, SHARED, MODULE)."; reportError(context, content->GetOriginalExpression(), e.str()); - return std::string(); + return std::vector<std::string>(); } if (auto* cli = gt->GetLinkInformation(context->Config)) { @@ -2371,13 +2440,51 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode } } - return cmJoin(dllPaths, ";"); + return dllPaths; } - return ""; + return std::vector<std::string>(); + } +}; + +static const struct TargetRuntimeDllsNode : public TargetRuntimeDllsBaseNode +{ + TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default) + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + std::vector<std::string> dlls = CollectDlls(parameters, context, content); + return cmJoin(dlls, ";"); } } targetRuntimeDllsNode; +static const struct TargetRuntimeDllDirsNode : public TargetRuntimeDllsBaseNode +{ + TargetRuntimeDllDirsNode() {} // NOLINT(modernize-use-equals-default) + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + std::vector<std::string> dlls = CollectDlls(parameters, context, content); + std::vector<std::string> dllDirs; + for (const std::string& dll : dlls) { + std::string directory = cmSystemTools::GetFilenamePath(dll); + if (std::find(dllDirs.begin(), dllDirs.end(), directory) == + dllDirs.end()) { + dllDirs.push_back(directory); + } + } + return cmJoin(dllDirs, ";"); + } +} targetRuntimeDllDirsNode; + static const struct CompileFeaturesNode : public cmGeneratorExpressionNode { CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default) @@ -2586,10 +2693,14 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode class ArtifactDirTag; class ArtifactLinkerTag; +class ArtifactLinkerLibraryTag; +class ArtifactLinkerImportTag; class ArtifactNameTag; +class ArtifactImportTag; class ArtifactPathTag; class ArtifactPdbTag; class ArtifactSonameTag; +class ArtifactSonameImportTag; class ArtifactBundleDirTag; class ArtifactBundleDirNameTag; class ArtifactBundleContentDirTag; @@ -2699,6 +2810,38 @@ struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag> }; template <> +struct TargetFilesystemArtifactResultCreator<ArtifactSonameImportTag> +{ + static std::string Create(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + // The target soname file (.so.1). + if (target->IsDLLPlatform()) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_SONAME_IMPORT_FILE is not allowed " + "for DLL target platforms."); + return std::string(); + } + if (target->GetType() != cmStateEnums::SHARED_LIBRARY) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_SONAME_IMPORT_FILE is allowed only for " + "SHARED libraries."); + return std::string(); + } + + if (target->HasImportLibrary(context->Config)) { + return cmStrCat(target->GetDirectory( + context->Config, cmStateEnums::ImportLibraryArtifact), + '/', + target->GetSOName(context->Config, + cmStateEnums::ImportLibraryArtifact)); + } + return std::string{}; + } +}; + +template <> struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag> { static std::string Create(cmGeneratorTarget* target, @@ -2745,7 +2888,8 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { - // The file used to link to the target (.so, .lib, .a). + // The file used to link to the target (.so, .lib, .a) or import file + // (.lib, .tbd). if (!target->IsLinkable()) { ::reportError(context, content->GetOriginalExpression(), "TARGET_LINKER_FILE is allowed only for libraries and " @@ -2761,6 +2905,55 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> }; template <> +struct TargetFilesystemArtifactResultCreator<ArtifactLinkerLibraryTag> +{ + static std::string Create(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + // The file used to link to the target (.dylib, .so, .a). + if (!target->IsLinkable() || + target->GetType() == cmStateEnums::EXECUTABLE) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_LIBRARY_FILE is allowed only for libraries " + "with ENABLE_EXPORTS."); + return std::string(); + } + + if (!target->IsDLLPlatform() || + target->GetType() == cmStateEnums::STATIC_LIBRARY) { + return target->GetFullPath(context->Config, + cmStateEnums::RuntimeBinaryArtifact); + } + return std::string{}; + } +}; + +template <> +struct TargetFilesystemArtifactResultCreator<ArtifactLinkerImportTag> +{ + static std::string Create(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + // The file used to link to the target (.lib, .tbd). + if (!target->IsLinkable()) { + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_LINKER_IMPORT_FILE is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); + return std::string(); + } + + if (target->HasImportLibrary(context->Config)) { + return target->GetFullPath(context->Config, + cmStateEnums::ImportLibraryArtifact); + } + return std::string{}; + } +}; + +template <> struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag> { static std::string Create(cmGeneratorTarget* target, @@ -2857,6 +3050,21 @@ struct TargetFilesystemArtifactResultCreator<ArtifactNameTag> } }; +template <> +struct TargetFilesystemArtifactResultCreator<ArtifactImportTag> +{ + static std::string Create(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* /*unused*/) + { + if (target->HasImportLibrary(context->Config)) { + return target->GetFullPath(context->Config, + cmStateEnums::ImportLibraryArtifact, true); + } + return std::string{}; + } +}; + template <typename ArtifactT> struct TargetFilesystemArtifactResultGetter { @@ -2982,12 +3190,24 @@ struct TargetFilesystemArtifactNodeGroup static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag> targetNodeGroup; +static const TargetFilesystemArtifactNodeGroup<ArtifactImportTag> + targetImportNodeGroup; + static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag> targetLinkerNodeGroup; +static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerLibraryTag> + targetLinkerLibraryNodeGroup; + +static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerImportTag> + targetLinkerImportNodeGroup; + static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag> targetSoNameNodeGroup; +static const TargetFilesystemArtifactNodeGroup<ArtifactSonameImportTag> + targetSoNameImportNodeGroup; + static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> targetPdbNodeGroup; @@ -3027,13 +3247,30 @@ struct TargetOutputNameArtifactResultGetter<ArtifactNameTag> }; template <> +struct TargetOutputNameArtifactResultGetter<ArtifactImportTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* /*unused*/) + { + if (target->HasImportLibrary(context->Config)) { + return target->GetOutputName(context->Config, + cmStateEnums::ImportLibraryArtifact) + + target->GetFilePostfix(context->Config); + } + return std::string{}; + } +}; + +template <> struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag> { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { - // The file used to link to the target (.so, .lib, .a). + // The library file used to link to the target (.so, .lib, .a) or import + // file (.lin, .tbd). if (!target->IsLinkable()) { ::reportError(context, content->GetOriginalExpression(), "TARGET_LINKER_FILE_BASE_NAME is allowed only for " @@ -3050,6 +3287,56 @@ struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag> }; template <> +struct TargetOutputNameArtifactResultGetter<ArtifactLinkerLibraryTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + // The library file used to link to the target (.so, .lib, .a). + if (!target->IsLinkable() || + target->GetType() == cmStateEnums::EXECUTABLE) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_LIBRARY_FILE_BASE_NAME is allowed only for " + "libraries with ENABLE_EXPORTS."); + return std::string(); + } + + if (!target->IsDLLPlatform() || + target->GetType() == cmStateEnums::STATIC_LIBRARY) { + return target->GetOutputName(context->Config, + cmStateEnums::ImportLibraryArtifact) + + target->GetFilePostfix(context->Config); + } + return std::string{}; + } +}; + +template <> +struct TargetOutputNameArtifactResultGetter<ArtifactLinkerImportTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + // The import file used to link to the target (.lib, .tbd). + if (!target->IsLinkable()) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_IMPORT_FILE_BASE_NAME is allowed only for " + "libraries and executables with ENABLE_EXPORTS."); + return std::string(); + } + + if (target->HasImportLibrary(context->Config)) { + return target->GetOutputName(context->Config, + cmStateEnums::ImportLibraryArtifact) + + target->GetFilePostfix(context->Config); + } + return std::string{}; + } +}; + +template <> struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> { static std::string Get(cmGeneratorTarget* target, @@ -3120,15 +3407,27 @@ struct TargetFileBaseNameArtifact : public TargetArtifactBase static const TargetFileBaseNameArtifact<ArtifactNameTag> targetFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactImportTag> + targetImportFileBaseNameNode; static const TargetFileBaseNameArtifact<ArtifactLinkerTag> targetLinkerFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactLinkerLibraryTag> + targetLinkerLibraryFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactLinkerImportTag> + targetLinkerImportFileBaseNameNode; static const TargetFileBaseNameArtifact<ArtifactPdbTag> targetPdbFileBaseNameNode; class ArtifactFilePrefixTag; +class ArtifactImportFilePrefixTag; class ArtifactLinkerFilePrefixTag; +class ArtifactLinkerLibraryFilePrefixTag; +class ArtifactLinkerImportFilePrefixTag; class ArtifactFileSuffixTag; +class ArtifactImportFileSuffixTag; class ArtifactLinkerFileSuffixTag; +class ArtifactLinkerLibraryFileSuffixTag; +class ArtifactLinkerImportFileSuffixTag; template <typename ArtifactT> struct TargetFileArtifactResultGetter @@ -3149,6 +3448,20 @@ struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag> } }; template <> +struct TargetFileArtifactResultGetter<ArtifactImportFilePrefixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent*) + { + if (target->HasImportLibrary(context->Config)) { + return target->GetFilePrefix(context->Config, + cmStateEnums::ImportLibraryArtifact); + } + return std::string{}; + } +}; +template <> struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag> { static std::string Get(cmGeneratorTarget* target, @@ -3156,9 +3469,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag> const GeneratorExpressionContent* content) { if (!target->IsLinkable()) { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_LINKER_PREFIX is allowed only for libraries and " - "executables with ENABLE_EXPORTS."); + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_LINKER_FILE_PREFIX is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); return std::string(); } @@ -3171,6 +3485,52 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag> } }; template <> +struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFilePrefixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (!target->IsLinkable() || + target->GetType() == cmStateEnums::EXECUTABLE) { + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_LINKER_LIBRARY_FILE_PREFIX is allowed only for libraries " + "with ENABLE_EXPORTS."); + return std::string(); + } + + if (!target->IsDLLPlatform() || + target->GetType() == cmStateEnums::STATIC_LIBRARY) { + return target->GetFilePrefix(context->Config, + cmStateEnums::RuntimeBinaryArtifact); + } + return std::string{}; + } +}; +template <> +struct TargetFileArtifactResultGetter<ArtifactLinkerImportFilePrefixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (!target->IsLinkable()) { + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_LINKER_IMPORT_FILE_PREFIX is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); + return std::string(); + } + + if (target->HasImportLibrary(context->Config)) { + return target->GetFilePrefix(context->Config, + cmStateEnums::ImportLibraryArtifact); + } + return std::string{}; + } +}; +template <> struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag> { static std::string Get(cmGeneratorTarget* target, @@ -3181,6 +3541,20 @@ struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag> } }; template <> +struct TargetFileArtifactResultGetter<ArtifactImportFileSuffixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent*) + { + if (target->HasImportLibrary(context->Config)) { + return target->GetFileSuffix(context->Config, + cmStateEnums::ImportLibraryArtifact); + } + return std::string{}; + } +}; +template <> struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag> { static std::string Get(cmGeneratorTarget* target, @@ -3188,9 +3562,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag> const GeneratorExpressionContent* content) { if (!target->IsLinkable()) { - ::reportError(context, content->GetOriginalExpression(), - "TARGET_LINKER_SUFFIX is allowed only for libraries and " - "executables with ENABLE_EXPORTS."); + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_LINKER_FILE_SUFFIX is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); return std::string(); } @@ -3202,6 +3577,51 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag> return target->GetFileSuffix(context->Config, artifact); } }; +template <> +struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFileSuffixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (!target->IsLinkable() || + target->GetType() == cmStateEnums::STATIC_LIBRARY) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_LIBRARY_FILE_SUFFIX is allowed only for " + "libraries with ENABLE_EXPORTS."); + return std::string(); + } + + if (!target->IsDLLPlatform() || + target->GetType() == cmStateEnums::STATIC_LIBRARY) { + return target->GetFileSuffix(context->Config, + cmStateEnums::RuntimeBinaryArtifact); + } + return std::string{}; + } +}; +template <> +struct TargetFileArtifactResultGetter<ArtifactLinkerImportFileSuffixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (!target->IsLinkable()) { + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_LINKER_IMPORT_FILE_SUFFIX is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); + return std::string(); + } + + if (target->HasImportLibrary(context->Config)) { + return target->GetFileSuffix(context->Config, + cmStateEnums::ImportLibraryArtifact); + } + return std::string{}; + } +}; template <typename ArtifactT> struct TargetFileArtifact : public TargetArtifactBase @@ -3232,11 +3652,23 @@ struct TargetFileArtifact : public TargetArtifactBase }; static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode; +static const TargetFileArtifact<ArtifactImportFilePrefixTag> + targetImportFilePrefixNode; static const TargetFileArtifact<ArtifactLinkerFilePrefixTag> targetLinkerFilePrefixNode; +static const TargetFileArtifact<ArtifactLinkerLibraryFilePrefixTag> + targetLinkerLibraryFilePrefixNode; +static const TargetFileArtifact<ArtifactLinkerImportFilePrefixTag> + targetLinkerImportFilePrefixNode; static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode; +static const TargetFileArtifact<ArtifactImportFileSuffixTag> + targetImportFileSuffixNode; static const TargetFileArtifact<ArtifactLinkerFileSuffixTag> targetLinkerFileSuffixNode; +static const TargetFileArtifact<ArtifactLinkerLibraryFileSuffixTag> + targetLinkerLibraryFileSuffixNode; +static const TargetFileArtifact<ArtifactLinkerImportFileSuffixTag> + targetLinkerImportFileSuffixNode; static const struct ShellPathNode : public cmGeneratorExpressionNode { @@ -3304,23 +3736,52 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "CONFIGURATION", &configurationNode }, { "CONFIG", &configurationTestNode }, { "TARGET_FILE", &targetNodeGroup.File }, + { "TARGET_IMPORT_FILE", &targetImportNodeGroup.File }, { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File }, + { "TARGET_LINKER_LIBRARY_FILE", &targetLinkerLibraryNodeGroup.File }, + { "TARGET_LINKER_IMPORT_FILE", &targetLinkerImportNodeGroup.File }, { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File }, + { "TARGET_SONAME_IMPORT_FILE", &targetSoNameImportNodeGroup.File }, { "TARGET_PDB_FILE", &targetPdbNodeGroup.File }, { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode }, + { "TARGET_IMPORT_FILE_BASE_NAME", &targetImportFileBaseNameNode }, { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode }, + { "TARGET_LINKER_LIBRARY_FILE_BASE_NAME", + &targetLinkerLibraryFileBaseNameNode }, + { "TARGET_LINKER_IMPORT_FILE_BASE_NAME", + &targetLinkerImportFileBaseNameNode }, { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode }, { "TARGET_FILE_PREFIX", &targetFilePrefixNode }, + { "TARGET_IMPORT_FILE_PREFIX", &targetImportFilePrefixNode }, { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode }, + { "TARGET_LINKER_LIBRARY_FILE_PREFIX", + &targetLinkerLibraryFilePrefixNode }, + { "TARGET_LINKER_IMPORT_FILE_PREFIX", &targetLinkerImportFilePrefixNode }, { "TARGET_FILE_SUFFIX", &targetFileSuffixNode }, + { "TARGET_IMPORT_FILE_SUFFIX", &targetImportFileSuffixNode }, { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode }, + { "TARGET_LINKER_LIBRARY_FILE_SUFFIX", + &targetLinkerLibraryFileSuffixNode }, + { "TARGET_LINKER_IMPORT_FILE_SUFFIX", &targetLinkerImportFileSuffixNode }, { "TARGET_FILE_NAME", &targetNodeGroup.FileName }, + { "TARGET_IMPORT_FILE_NAME", &targetImportNodeGroup.FileName }, { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName }, + { "TARGET_LINKER_LIBRARY_FILE_NAME", + &targetLinkerLibraryNodeGroup.FileName }, + { "TARGET_LINKER_IMPORT_FILE_NAME", + &targetLinkerImportNodeGroup.FileName }, { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName }, + { "TARGET_SONAME_IMPORT_FILE_NAME", + &targetSoNameImportNodeGroup.FileName }, { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName }, { "TARGET_FILE_DIR", &targetNodeGroup.FileDir }, + { "TARGET_IMPORT_FILE_DIR", &targetImportNodeGroup.FileDir }, { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir }, + { "TARGET_LINKER_LIBRARY_FILE_DIR", + &targetLinkerLibraryNodeGroup.FileDir }, + { "TARGET_LINKER_IMPORT_FILE_DIR", &targetLinkerImportNodeGroup.FileDir }, { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir }, + { "TARGET_SONAME_IMPORT_FILE_DIR", &targetSoNameImportNodeGroup.FileDir }, { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir }, { "TARGET_BUNDLE_DIR", &targetBundleDirNode }, { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode }, @@ -3348,6 +3809,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode }, { "TARGET_GENEX_EVAL", &targetGenexEvalNode }, { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode }, + { "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode }, { "GENEX_EVAL", &genexEvalNode }, { "BUILD_INTERFACE", &buildInterfaceNode }, { "INSTALL_INTERFACE", &installInterfaceNode }, diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 5e352b2..54cd8ba 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -458,6 +458,11 @@ std::string const& cmGeneratorTarget::GetSafeProperty( const char* cmGeneratorTarget::GetOutputTargetType( cmStateEnums::ArtifactType artifact) const { + if (this->IsFrameworkOnApple() || this->GetGlobalGenerator()->IsXcode()) { + // import file (i.e. .tbd file) is always in same location as library + artifact = cmStateEnums::RuntimeBinaryArtifact; + } + switch (this->GetType()) { case cmStateEnums::SHARED_LIBRARY: if (this->IsDLLPlatform()) { @@ -470,9 +475,15 @@ const char* cmGeneratorTarget::GetOutputTargetType( return "ARCHIVE"; } } else { - // For non-DLL platforms shared libraries are treated as - // library targets. - return "LIBRARY"; + switch (artifact) { + case cmStateEnums::RuntimeBinaryArtifact: + // For non-DLL platforms shared libraries are treated as + // library targets. + return "LIBRARY"; + case cmStateEnums::ImportLibraryArtifact: + // Library import libraries are treated as archive targets. + return "ARCHIVE"; + } } break; case cmStateEnums::STATIC_LIBRARY: @@ -1222,10 +1233,12 @@ bool cmGeneratorTarget::IsInBuildSystem() const case cmStateEnums::GLOBAL_TARGET: return true; case cmStateEnums::INTERFACE_LIBRARY: - // An INTERFACE library is in the build system if it has SOURCES or - // HEADER_SETS. + // An INTERFACE library is in the build system if it has SOURCES, + // HEADER_SETS, or C++ module filesets. if (!this->SourceEntries.empty() || - !this->Target->GetHeaderSetsEntries().empty()) { + !this->Target->GetHeaderSetsEntries().empty() || + !this->Target->GetCxxModuleSetsEntries().empty() || + !this->Target->GetCxxModuleHeaderSetsEntries().empty()) { return true; } break; @@ -1235,6 +1248,16 @@ bool cmGeneratorTarget::IsInBuildSystem() const return false; } +bool cmGeneratorTarget::IsNormal() const +{ + return this->Target->IsNormal(); +} + +bool cmGeneratorTarget::IsSynthetic() const +{ + return this->Target->IsSynthetic(); +} + bool cmGeneratorTarget::IsImported() const { return this->Target->IsImported(); @@ -1702,7 +1725,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget, std::move(entryCge), fileSet); entries.Entries.emplace_back( EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, tpe)); - for (auto const& file : entries.Entries.back().Values) { + 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"); @@ -1713,13 +1737,11 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget, std::string w; auto path = sf->ResolveFullPath(&e, &w); if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, - headTarget->GetBacktrace()); + cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); } if (path.empty()) { if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, - headTarget->GetBacktrace()); + cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); } return; } @@ -1794,11 +1816,11 @@ bool processSources(cmGeneratorTarget const* tgt, std::string fullPath = sf->ResolveFullPath(&e, &w); cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance(); if (!w.empty()) { - cm->IssueMessage(MessageType::AUTHOR_WARNING, w, tgt->GetBacktrace()); + cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace); } if (fullPath.empty()) { if (!e.empty()) { - cm->IssueMessage(MessageType::FATAL_ERROR, e, tgt->GetBacktrace()); + cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace); } return contextDependent; } @@ -2506,7 +2528,8 @@ bool cmGeneratorTarget::CanGenerateInstallNameDir( return !skip; } -std::string cmGeneratorTarget::GetSOName(const std::string& config) const +std::string cmGeneratorTarget::GetSOName( + const std::string& config, cmStateEnums::ArtifactType artifact) const { if (this->IsImported()) { // Lookup the imported soname. @@ -2534,7 +2557,9 @@ std::string cmGeneratorTarget::GetSOName(const std::string& config) const return ""; } // Compute the soname that will be built. - return this->GetLibraryNames(config).SharedObject; + return artifact == cmStateEnums::RuntimeBinaryArtifact + ? this->GetLibraryNames(config).SharedObject + : this->GetLibraryNames(config).ImportLibrary; } namespace { @@ -3062,6 +3087,16 @@ void cmGeneratorTarget::ComputeModuleDefinitionInfo( } } +bool cmGeneratorTarget::IsAIX() const +{ + return this->Target->IsAIX(); +} + +bool cmGeneratorTarget::IsApple() const +{ + return this->Target->IsApple(); +} + bool cmGeneratorTarget::IsDLLPlatform() const { return this->Target->IsDLLPlatform(); @@ -3400,7 +3435,7 @@ std::string cmGeneratorTarget::GetCompilePDBDirectory( void cmGeneratorTarget::GetAppleArchs(const std::string& config, std::vector<std::string>& archVec) const { - if (!this->Makefile->IsOn("APPLE")) { + if (!this->IsApple()) { return; } cmValue archs = nullptr; @@ -3898,7 +3933,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, &dagChecker, entries, IncludeRuntimeInterface::Yes); - if (this->Makefile->IsOn("APPLE")) { + if (this->IsApple()) { if (cmLinkImplementationLibraries const* impl = this->GetLinkImplementationLibraries(config, LinkInterfaceFor::Usage)) { @@ -5066,10 +5101,18 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const f = cmStrCat(dir, '/', targetNames.PDB); gg->AddToManifest(f); } + + dir = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact); + if (!targetNames.ImportOutput.empty()) { + f = cmStrCat(dir, '/', targetNames.ImportOutput); + gg->AddToManifest(f); + } if (!targetNames.ImportLibrary.empty()) { - f = - cmStrCat(this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact), - '/', targetNames.ImportLibrary); + f = cmStrCat(dir, '/', targetNames.ImportLibrary); + gg->AddToManifest(f); + } + if (!targetNames.ImportReal.empty()) { + f = cmStrCat(dir, '/', targetNames.ImportReal); gg->AddToManifest(f); } } @@ -5189,14 +5232,20 @@ std::string cmGeneratorTarget::NormalGetFullPath( } break; case cmStateEnums::ImportLibraryArtifact: - fpath += this->GetFullName(config, cmStateEnums::ImportLibraryArtifact); + if (realname) { + fpath += + this->NormalGetRealName(config, cmStateEnums::ImportLibraryArtifact); + } else { + fpath += + this->GetFullName(config, cmStateEnums::ImportLibraryArtifact); + } break; } return fpath; } std::string cmGeneratorTarget::NormalGetRealName( - const std::string& config) const + const std::string& config, cmStateEnums::ArtifactType artifact) const { // This should not be called for imported targets. // TODO: Split cmTarget into a class hierarchy to get compile-time @@ -5207,12 +5256,13 @@ std::string cmGeneratorTarget::NormalGetRealName( this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); } - if (this->GetType() == cmStateEnums::EXECUTABLE) { - // Compute the real name that will be built. - return this->GetExecutableNames(config).Real; - } + Names names = this->GetType() == cmStateEnums::EXECUTABLE + ? this->GetExecutableNames(config) + : this->GetLibraryNames(config); + // Compute the real name that will be built. - return this->GetLibraryNames(config).Real; + return artifact == cmStateEnums::RuntimeBinaryArtifact ? names.Real + : names.ImportReal; } cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( @@ -5257,17 +5307,16 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( // The library name. targetNames.Base = components.base; targetNames.Output = - components.prefix + targetNames.Base + components.suffix; + cmStrCat(components.prefix, targetNames.Base, components.suffix); if (this->IsFrameworkOnApple()) { targetNames.Real = components.prefix; if (!this->Makefile->PlatformIsAppleEmbedded()) { - targetNames.Real += "Versions/"; - targetNames.Real += this->GetFrameworkVersion(); - targetNames.Real += "/"; + targetNames.Real += + cmStrCat("Versions/", this->GetFrameworkVersion(), '/'); } - targetNames.Real += targetNames.Base + components.suffix; - targetNames.SharedObject = targetNames.Real + components.suffix; + targetNames.Real += cmStrCat(targetNames.Base, components.suffix); + targetNames.SharedObject = targetNames.Real; } else { // The library's soname. this->ComputeVersionedName(targetNames.SharedObject, components.prefix, @@ -5280,11 +5329,36 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( targetNames.Output, version); } - // The import library name. + // The import library names. if (this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::MODULE_LIBRARY) { - targetNames.ImportLibrary = - this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); + NameComponents const& importComponents = + this->GetFullNameInternalComponents(config, + cmStateEnums::ImportLibraryArtifact); + targetNames.ImportOutput = cmStrCat( + importComponents.prefix, importComponents.base, importComponents.suffix); + + if (this->IsFrameworkOnApple() && this->IsSharedLibraryWithExports()) { + targetNames.ImportReal = components.prefix; + if (!this->Makefile->PlatformIsAppleEmbedded()) { + targetNames.ImportReal += + cmStrCat("Versions/", this->GetFrameworkVersion(), '/'); + } + targetNames.ImportReal += + cmStrCat(importComponents.base, importComponents.suffix); + targetNames.ImportLibrary = targetNames.ImportOutput; + } else { + // The import library's soname. + this->ComputeVersionedName( + targetNames.ImportLibrary, importComponents.prefix, + importComponents.base, importComponents.suffix, + targetNames.ImportOutput, soversion); + + // The import library's real name on disk. + this->ComputeVersionedName( + targetNames.ImportReal, importComponents.prefix, importComponents.base, + importComponents.suffix, targetNames.ImportOutput, version); + } } // The program database file name. @@ -5346,6 +5420,8 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames( // The import library name. targetNames.ImportLibrary = this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); + targetNames.ImportReal = targetNames.ImportLibrary; + targetNames.ImportOutput = targetNames.ImportLibrary; // The program database file name. targetNames.PDB = this->GetPDBName(config); @@ -5427,15 +5503,18 @@ cmGeneratorTarget::GetFullNameInternalComponents( } // Compute the full name for main target types. - const std::string configPostfix = this->GetFilePostfix(config); + std::string configPostfix = this->GetFilePostfix(config); - // frameworks have directory prefix but no suffix + // frameworks have directory prefix std::string fw_prefix; if (this->IsFrameworkOnApple()) { fw_prefix = cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/'); targetPrefix = cmValue(fw_prefix); - targetSuffix = nullptr; + if (!isImportedLibraryArtifact) { + // no suffix + targetSuffix = nullptr; + } } if (this->IsCFBundleOnApple()) { @@ -5454,8 +5533,8 @@ cmGeneratorTarget::GetFullNameInternalComponents( // When using Xcode, the postfix should be part of the suffix rather than // the base, because the suffix ends up being used in Xcode's // EXECUTABLE_SUFFIX attribute. - if (this->IsFrameworkOnApple() && - this->GetGlobalGenerator()->GetName() == "Xcode") { + if (this->IsFrameworkOnApple() && this->GetGlobalGenerator()->IsXcode()) { + configPostfix += *targetSuffix; targetSuffix = cmValue(configPostfix); } else { outBase += configPostfix; @@ -5463,9 +5542,15 @@ cmGeneratorTarget::GetFullNameInternalComponents( // Name shared libraries with their version number on some platforms. if (cmValue soversion = this->GetProperty("SOVERSION")) { + cmValue dllProp; + if (this->IsDLLPlatform()) { + dllProp = this->GetProperty("DLL_NAME_WITH_SOVERSION"); + } if (this->GetType() == cmStateEnums::SHARED_LIBRARY && !isImportedLibraryArtifact && - this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")) { + (dllProp.IsOn() || + (!dllProp.IsSet() && + this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")))) { outBase += "-"; outBase += *soversion; } @@ -6745,12 +6830,12 @@ void cmGeneratorTarget::ComputeVersionedName( std::string& vName, std::string const& prefix, std::string const& base, std::string const& suffix, std::string const& name, cmValue version) const { - vName = this->Makefile->IsOn("APPLE") ? (prefix + base) : name; + vName = this->IsApple() ? (prefix + base) : name; if (version) { vName += "."; vName += *version; } - vName += this->Makefile->IsOn("APPLE") ? suffix : std::string(); + vName += this->IsApple() ? suffix : std::string(); } std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const @@ -7103,6 +7188,11 @@ cmGeneratorTarget::OutputInfo const* cmGeneratorTarget::GetOutputInfo( return nullptr; } + // Synthetic targets don't have output. + if (this->IsSynthetic()) { + return nullptr; + } + // Only libraries and executables have well-defined output files. if (!this->HaveWellDefinedOutputFiles()) { std::string msg = cmStrCat("cmGeneratorTarget::GetOutputInfo called for ", @@ -8514,19 +8604,38 @@ bool cmGeneratorTarget::HasContextDependentSources() const bool cmGeneratorTarget::IsExecutableWithExports() const { - return (this->GetType() == cmStateEnums::EXECUTABLE && - this->GetPropertyAsBool("ENABLE_EXPORTS")); + return this->Target->IsExecutableWithExports(); +} + +bool cmGeneratorTarget::IsSharedLibraryWithExports() const +{ + return this->Target->IsSharedLibraryWithExports(); } bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const { + bool generate_Stubs = true; + if (this->GetGlobalGenerator()->IsXcode()) { + // take care of CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS variable + // as well as XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS property + if (cmValue propGenStubs = + this->GetProperty("XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) { + generate_Stubs = propGenStubs == "YES"; + } else if (cmValue varGenStubs = this->Makefile->GetDefinition( + "CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) { + generate_Stubs = varGenStubs == "YES"; + } + } + return (this->IsDLLPlatform() && (this->GetType() == cmStateEnums::SHARED_LIBRARY || this->IsExecutableWithExports()) && // Assemblies which have only managed code do not have // import libraries. this->GetManagedType(config) != ManagedType::Managed) || - (this->Target->IsAIX() && this->IsExecutableWithExports()); + (this->IsAIX() && this->IsExecutableWithExports()) || + (this->Makefile->PlatformSupportsAppleTextStubs() && + this->IsSharedLibraryWithExports() && generate_Stubs); } bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const @@ -8564,17 +8673,12 @@ bool cmGeneratorTarget::IsLinkable() const bool cmGeneratorTarget::IsFrameworkOnApple() const { - return ((this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->GetType() == cmStateEnums::STATIC_LIBRARY) && - this->Makefile->IsOn("APPLE") && - this->GetPropertyAsBool("FRAMEWORK")); + return this->Target->IsFrameworkOnApple(); } bool cmGeneratorTarget::IsAppBundleOnApple() const { - return (this->GetType() == cmStateEnums::EXECUTABLE && - this->Makefile->IsOn("APPLE") && - this->GetPropertyAsBool("MACOSX_BUNDLE")); + return this->Target->IsAppBundleOnApple(); } bool cmGeneratorTarget::IsXCTestOnApple() const @@ -8584,8 +8688,8 @@ bool cmGeneratorTarget::IsXCTestOnApple() const bool cmGeneratorTarget::IsCFBundleOnApple() const { - return (this->GetType() == cmStateEnums::MODULE_LIBRARY && - this->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("BUNDLE")); + return (this->GetType() == cmStateEnums::MODULE_LIBRARY && this->IsApple() && + this->GetPropertyAsBool("BUNDLE")); } cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType( diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index afd9da4..5d26191 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -50,6 +50,8 @@ public: cmGlobalGenerator* GetGlobalGenerator() const; bool IsInBuildSystem() const; + bool IsNormal() const; + bool IsSynthetic() const; bool IsImported() const; bool IsImportedGloballyVisible() const; bool CanCompileSources() const; @@ -271,7 +273,9 @@ public: std::string NormalGetFullPath(const std::string& config, cmStateEnums::ArtifactType artifact, bool realname) const; - std::string NormalGetRealName(const std::string& config) const; + std::string NormalGetRealName(const std::string& config, + cmStateEnums::ArtifactType artifact = + cmStateEnums::RuntimeBinaryArtifact) const; /** Get the names of an object library's object files underneath its object file directory. */ @@ -346,7 +350,9 @@ public: const std::string* GetExportMacro() const; /** Get the soname of the target. Allowed only for a shared library. */ - std::string GetSOName(const std::string& config) const; + std::string GetSOName(const std::string& config, + cmStateEnums::ArtifactType artifact = + cmStateEnums::RuntimeBinaryArtifact) const; struct NameComponents { @@ -387,6 +393,11 @@ public: ModuleDefinitionInfo const* GetModuleDefinitionInfo( std::string const& config) const; + /** Return whether or not we are targeting AIX. */ + bool IsAIX() const; + /** Return whether or not we are targeting Apple. */ + bool IsApple() const; + /** Return whether or not the target is for a DLL platform. */ bool IsDLLPlatform() const; @@ -733,6 +744,8 @@ public: std::string Base; std::string Output; std::string Real; + std::string ImportOutput; + std::string ImportReal; std::string ImportLibrary; std::string PDB; std::string SharedObject; @@ -779,6 +792,10 @@ public: bool IsExecutableWithExports() const; + /* Return whether this target is a shared library with capability to generate + * a file describing symbols exported (for example, .tbd file on Apple). */ + bool IsSharedLibraryWithExports() const; + /** Return whether or not the target has a DLL import library. */ bool HasImportLibrary(std::string const& config) const; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 4cfec22..72eed69 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1327,6 +1327,16 @@ void cmGlobalGenerator::Configure() this->BinaryDirectories.insert( this->CMakeInstance->GetHomeOutputDirectory()); + if (this->ExtraGenerator && !this->CMakeInstance->GetIsInTryCompile()) { + this->CMakeInstance->IssueMessage( + MessageType::DEPRECATION_WARNING, + cmStrCat("Support for \"Extra Generators\" like\n ", + this->ExtraGenerator->GetName(), + "\nis deprecated and will be removed from a future version " + "of CMake. IDEs may use the cmake-file-api(7) to view " + "CMake-generated project build trees.")); + } + // now do it this->ConfigureDoneCMP0026AndCMP0024 = false; dirMf->Configure(); @@ -2911,7 +2921,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( singleLine.push_back(cfgArg); cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)"; } else { - cfgArg += *mf->GetDefinition("CMAKE_CFG_INTDIR"); + cfgArg += this->GetCMakeCFGIntDir(); } singleLine.push_back(cfgArg); } diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 3da15f6..b1f2b4a 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -22,7 +22,6 @@ #include "cmLocalGhsMultiGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmPolicies.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateTypes.h" @@ -717,7 +716,6 @@ bool cmGlobalGhsMultiGenerator::AddCheckTarget() cc->SetDepends(listFiles); cc->SetCommandLines(commandLines); cc->SetComment("Checking Build System"); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetStdPipesUTF8(true); @@ -747,7 +745,6 @@ void cmGlobalGhsMultiGenerator::AddAllTarget() // Use no actual command lines so that the target itself is not // considered always out of date. auto cc = cm::make_unique<cmCustomCommand>(); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetComment("Build all projects"); cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(), diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 93aa30a..d29c086 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -318,12 +318,6 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, } } - if (build.Variables.count("dyndep") > 0) { - // The ninja 'cleandead' operation does not account for outputs - // discovered by 'dyndep' bindings. Avoid removing them. - this->DisableCleandead = true; - } - os << buildStr << arguments << assignments << "\n"; } @@ -577,6 +571,7 @@ void cmGlobalNinjaGenerator::Generate() msg.str()); return; } + this->InitOutputPathPrefix(); if (!this->OpenBuildFileStreams()) { return; } @@ -588,10 +583,8 @@ void cmGlobalNinjaGenerator::Generate() it.second.TargetDependsClosureLocalOutputs.clear(); } - this->InitOutputPathPrefix(); this->TargetAll = this->NinjaOutputPath("all"); this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt"); - this->DisableCleandead = false; this->DiagnosedCxxModuleNinjaSupport = false; this->ClangTidyExportFixesDirs.clear(); this->ClangTidyExportFixesFiles.clear(); @@ -1277,6 +1270,10 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs( } CM_FALLTHROUGH; case cmStateEnums::EXECUTABLE: { + if (target->IsApple() && target->HasImportLibrary(config)) { + outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath( + config, cmStateEnums::ImportLibraryArtifact, realname))); + } outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath( config, cmStateEnums::RuntimeBinaryArtifact, realname))); break; @@ -2082,9 +2079,10 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) build.Outputs.front() = this->BuildAlias( this->NinjaOutputPath(this->GetCleanTargetName()), config); if (this->IsMultiConfig()) { - build.Variables["TARGETS"] = - cmStrCat(this->BuildAlias(GetByproductsForCleanTargetName(), config), - " ", GetByproductsForCleanTargetName()); + build.Variables["TARGETS"] = cmStrCat( + this->BuildAlias( + this->NinjaOutputPath(GetByproductsForCleanTargetName()), config), + " ", this->NinjaOutputPath(GetByproductsForCleanTargetName())); } build.ExplicitDeps.clear(); if (additionalFiles) { @@ -2099,7 +2097,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) if (this->IsMultiConfig()) { build.Variables["FILE_ARG"] = cmStrCat( "-f ", - cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig)); + this->NinjaOutputPath( + cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig))); } this->WriteBuild(*this->GetImplFileStream(fileConfig), build); } @@ -2121,8 +2120,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) std::vector<std::string> byproducts; byproducts.reserve(this->CrossConfigs.size()); for (auto const& config : this->CrossConfigs) { - byproducts.push_back( - this->BuildAlias(GetByproductsForCleanTargetName(), config)); + byproducts.push_back(this->BuildAlias( + this->NinjaOutputPath(GetByproductsForCleanTargetName()), config)); } byproducts.emplace_back(GetByproductsForCleanTargetName()); build.Variables["TARGETS"] = cmJoin(byproducts, " "); @@ -2130,7 +2129,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) for (auto const& fileConfig : configs) { build.Variables["FILE_ARG"] = cmStrCat( "-f ", - cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig)); + this->NinjaOutputPath( + cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig))); this->WriteBuild(*this->GetImplFileStream(fileConfig), build); } } @@ -2913,7 +2913,8 @@ bool cmGlobalNinjaMultiGenerator::OpenBuildFileStreams() *this->DefaultFileStream << "# Build using rules for '" << this->DefaultFileConfig << "'.\n\n" << "include " - << GetNinjaImplFilename(this->DefaultFileConfig) + << this->NinjaOutputPath( + GetNinjaImplFilename(this->DefaultFileConfig)) << "\n\n"; // Write a comment about this file. @@ -2946,7 +2947,8 @@ bool cmGlobalNinjaMultiGenerator::OpenBuildFileStreams() *this->ConfigFileStreams[config] << "# This file contains aliases specific to the \"" << config << "\"\n# configuration.\n\n" - << "include " << GetNinjaImplFilename(config) << "\n\n"; + << "include " << this->NinjaOutputPath(GetNinjaImplFilename(config)) + << "\n\n"; return true; }); diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 1436c83..6d23e89 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -596,7 +596,6 @@ private: std::string OutputPathPrefix; std::string TargetAll; std::string CMakeCacheFile; - bool DisableCleandead = false; struct ByConfig { diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index d483135..5de3a55 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -309,6 +309,26 @@ void cmGlobalVisualStudio7Generator::Generate() 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::VS11 && !this->CMakeInstance->GetIsInTryCompile()) { std::string cmakeWarnVS11; diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 647fc2d..2e2c8b6 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -169,7 +169,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() cm::static_reference_cast<cmLocalVisualStudio7Generator>(generators[0]); auto cc = cm::make_unique<cmCustomCommand>(); - cc->SetCMP0116Status(cmPolicies::NEW); cmTarget* tgt = lg.AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false, std::move(cc)); @@ -225,7 +224,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() cc->SetByproducts(byproducts); cc->SetCommandLines(verifyCommandLines); cc->SetComment("Checking File Globs"); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetStdPipesUTF8(stdPipesUTF8); lg.AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCustomCommandType::PRE_BUILD, @@ -260,7 +258,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() cc->SetDepends(listFiles); cc->SetCommandLines(commandLines); cc->SetComment("Checking Build System"); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetStdPipesUTF8(stdPipesUTF8); if (cmSourceFile* file = diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx index 9f6550b..e396405 100644 --- a/Source/cmGlobalVisualStudio9Generator.cxx +++ b/Source/cmGlobalVisualStudio9Generator.cxx @@ -64,7 +64,7 @@ public: cmDocumentationEntry GetDocumentation() const override { return { std::string(vs9generatorName) + " [arch]", - "Generates Visual Studio 2008 project files. " + "Deprecated. Generates Visual Studio 2008 project files. " "Optional [arch] can be \"Win64\" or \"IA64\"." }; } diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 31f6f77..702199d 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -201,7 +201,6 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets() // Use no actual command lines so that the target itself is not // considered always out of date. auto cc = cm::make_unique<cmCustomCommand>(); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetComment("Build all projects"); cmTarget* allBuild = @@ -545,12 +544,12 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile, cmSystemTools::ConvertToUnixSlashes(s1); std::string keyname; - HKEY hkey = NULL; + HKEY hkey = nullptr; LONG result = ERROR_SUCCESS; DWORD index = 0; keyname = regKeyBase + "\\OtherProjects7"; - hkey = NULL; + hkey = nullptr; result = RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(), 0, KEY_READ, &hkey); @@ -568,7 +567,7 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile, RegEnumKeyExW(hkey, index, subkeyname, &cch_subkeyname, 0, keyclass, &cch_keyclass, &lastWriteTime)) { // Open the subkey and query the values of interest: - HKEY hsubkey = NULL; + HKEY hsubkey = nullptr; result = RegOpenKeyExW(hkey, subkeyname, 0, KEY_READ, &hsubkey); if (ERROR_SUCCESS == result) { DWORD valueType = REG_SZ; @@ -642,7 +641,7 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile, nextAvailableSubKeyName = std::to_string(index); keyname = regKeyBase + "\\RecordingProject7"; - hkey = NULL; + hkey = nullptr; result = RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(), 0, KEY_READ, &hkey); @@ -688,13 +687,13 @@ void WriteVSMacrosFileRegistryEntry(const std::string& nextAvailableSubKeyName, const std::string& regKeyBase) { std::string keyname = regKeyBase + "\\OtherProjects7"; - HKEY hkey = NULL; + HKEY hkey = nullptr; LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(), 0, KEY_READ | KEY_WRITE, &hkey); if (ERROR_SUCCESS == result) { // Create the subkey and set the values of interest: - HKEY hsubkey = NULL; + HKEY hsubkey = nullptr; wchar_t lpClass[] = L""; result = RegCreateKeyExW( hkey, cmsys::Encoding::ToWide(nextAvailableSubKeyName).c_str(), 0, @@ -961,13 +960,13 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand( static bool OpenSolution(std::string const& sln) { HRESULT comInitialized = - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (FAILED(comInitialized)) { return false; } - HINSTANCE hi = - ShellExecuteA(NULL, "open", sln.c_str(), NULL, NULL, SW_SHOWNORMAL); + HINSTANCE hi = ShellExecuteA(nullptr, "open", sln.c_str(), nullptr, nullptr, + SW_SHOWNORMAL); CoUninitialize(); diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 4746507..5b3ac60 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -615,7 +615,6 @@ void cmGlobalXCodeGenerator::AddExtraTargets( auto cc = cm::make_unique<cmCustomCommand>(); cc->SetCommandLines( cmMakeSingleCommandLine({ "echo", "Build all projects" })); - cc->SetCMP0116Status(cmPolicies::NEW); cmTarget* allbuild = root->AddUtilityCommand("ALL_BUILD", true, std::move(cc)); @@ -655,7 +654,6 @@ void cmGlobalXCodeGenerator::AddExtraTargets( cmSystemTools::ReplaceString(file, "\\ ", " "); cc = cm::make_unique<cmCustomCommand>(); cc->SetCommandLines(cmMakeSingleCommandLine({ "make", "-f", file })); - cc->SetCMP0116Status(cmPolicies::NEW); cmTarget* check = root->AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc)); @@ -687,7 +685,6 @@ void cmGlobalXCodeGenerator::AddExtraTargets( cc->SetCommandLines(legacyDependHelperCommandLines); cc->SetComment("Depend check for xcode"); cc->SetWorkingDirectory(legacyDependHelperDir.c_str()); - cc->SetCMP0116Status(cmPolicies::NEW); gen->AddCustomCommandToTarget( target->GetName(), cmCustomCommandType::POST_BUILD, std::move(cc), cmObjectLibraryCommands::Accept); @@ -1742,7 +1739,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( std::string str_so_file = cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>'); std::string str_link_file = - cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>'); + cmStrCat("$<TARGET_LINKER_LIBRARY_FILE:", gtgt->GetName(), '>'); cmCustomCommandLines cmd = cmMakeSingleCommandLine( { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library", str_file, str_so_file, str_link_file }); @@ -1757,6 +1754,27 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( postbuild.push_back(std::move(command)); } + if (gtgt->HasImportLibrary("") && !gtgt->IsFrameworkOnApple()) { + // create symbolic links for .tbd file + std::string file = cmStrCat("$<TARGET_IMPORT_FILE:", gtgt->GetName(), '>'); + std::string soFile = + cmStrCat("$<TARGET_SONAME_IMPORT_FILE:", gtgt->GetName(), '>'); + std::string linkFile = + cmStrCat("$<TARGET_LINKER_IMPORT_FILE:", gtgt->GetName(), '>'); + cmCustomCommandLines symlink_command = cmMakeSingleCommandLine( + { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library", file, + soFile, linkFile }); + + cmCustomCommand command; + command.SetCommandLines(symlink_command); + command.SetComment("Creating import symlinks"); + command.SetWorkingDirectory(""); + command.SetBacktrace(this->CurrentMakefile->GetBacktrace()); + command.SetStdPipesUTF8(true); + + postbuild.push_back(std::move(command)); + } + cmXCodeObject* legacyCustomCommandsBuildPhase = nullptr; cmXCodeObject* preBuildPhase = nullptr; cmXCodeObject* preLinkPhase = nullptr; @@ -2685,6 +2703,12 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, buildSettings->AddAttribute("LIBRARY_STYLE", this->CreateString("DYNAMIC")); + + if (gtgt->HasImportLibrary(configName)) { + // Request .tbd file generation + buildSettings->AddAttribute("GENERATE_TEXT_BASED_STUBS", + this->CreateString("YES")); + } break; } case cmStateEnums::EXECUTABLE: { diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx index 9242344..3564cf7 100644 --- a/Source/cmIncludeCommand.cxx +++ b/Source/cmIncludeCommand.cxx @@ -20,7 +20,9 @@ bool cmIncludeCommand(std::vector<std::string> const& args, { static std::map<std::string, cmPolicies::PolicyID> DeprecatedModules; if (DeprecatedModules.empty()) { + DeprecatedModules["Dart"] = cmPolicies::CMP0145; DeprecatedModules["Documentation"] = cmPolicies::CMP0106; + DeprecatedModules["FindDart"] = cmPolicies::CMP0145; DeprecatedModules["WriteCompilerDetectionHeader"] = cmPolicies::CMP0120; } diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 82adca8..40230d9 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -359,13 +359,15 @@ bool HandleScriptMode(std::vector<std::string> const& args, } else if (doing_script) { doing_script = false; std::string script = arg; - if (!cmSystemTools::FileIsFullPath(script)) { - script = - cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg); - } - if (cmSystemTools::FileIsDirectory(script)) { - status.SetError("given a directory as value of SCRIPT argument."); - return false; + if (!cmHasLiteralPrefix(script, "$<INSTALL_PREFIX>")) { + if (!cmSystemTools::FileIsFullPath(script)) { + script = + cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg); + } + if (cmSystemTools::FileIsDirectory(script)) { + status.SetError("given a directory as value of SCRIPT argument."); + return false; + } } helper.Makefile->AddInstallGenerator( cm::make_unique<cmInstallScriptGenerator>( @@ -551,34 +553,35 @@ bool HandleTargetsMode(std::vector<std::string> const& args, // Enforce argument rules too complex to specify for the // general-purpose parser. - if (archiveArgs.GetNamelinkOnly() || runtimeArgs.GetNamelinkOnly() || - objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() || - bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() || + if (runtimeArgs.GetNamelinkOnly() || objectArgs.GetNamelinkOnly() || + frameworkArgs.GetNamelinkOnly() || bundleArgs.GetNamelinkOnly() || + privateHeaderArgs.GetNamelinkOnly() || publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) -> bool { return fileSetArg.GetNamelinkOnly(); }) || cxxModuleBmiArgs.GetNamelinkOnly()) { status.SetError( - "TARGETS given NAMELINK_ONLY option not in LIBRARY group. " - "The NAMELINK_ONLY option may be specified only following LIBRARY."); + "TARGETS given NAMELINK_ONLY option not in LIBRARY or ARCHIVE group. " + "The NAMELINK_ONLY option may be specified only following LIBRARY or " + "ARCHIVE."); return false; } - if (archiveArgs.GetNamelinkSkip() || runtimeArgs.GetNamelinkSkip() || - objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() || - bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() || + if (runtimeArgs.GetNamelinkSkip() || objectArgs.GetNamelinkSkip() || + frameworkArgs.GetNamelinkSkip() || bundleArgs.GetNamelinkSkip() || + privateHeaderArgs.GetNamelinkSkip() || publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) -> bool { return fileSetArg.GetNamelinkSkip(); }) || cxxModuleBmiArgs.GetNamelinkSkip()) { status.SetError( - "TARGETS given NAMELINK_SKIP option not in LIBRARY group. " - "The NAMELINK_SKIP option may be specified only following LIBRARY."); + "TARGETS given NAMELINK_SKIP option not in LIBRARY or ARCHIVE group. " + "The NAMELINK_SKIP option may be specified only following LIBRARY or " + "ARCHIVE."); return false; } - if (archiveArgs.HasNamelinkComponent() || - runtimeArgs.HasNamelinkComponent() || + if (runtimeArgs.HasNamelinkComponent() || objectArgs.HasNamelinkComponent() || frameworkArgs.HasNamelinkComponent() || bundleArgs.HasNamelinkComponent() || @@ -590,9 +593,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args, -> bool { return fileSetArg.HasNamelinkComponent(); }) || cxxModuleBmiArgs.HasNamelinkComponent()) { status.SetError( - "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " - "The NAMELINK_COMPONENT option may be specified only following " - "LIBRARY."); + "TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE " + "group. The NAMELINK_COMPONENT option may be specified only following " + "LIBRARY or ARCHIVE."); return false; } if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) { @@ -672,6 +675,14 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } else if (libraryArgs.GetNamelinkSkip()) { namelinkMode = cmInstallTargetGenerator::NamelinkModeSkip; } + // Select the mode for installing symlinks to versioned imported libraries. + cmInstallTargetGenerator::NamelinkModeType importlinkMode = + cmInstallTargetGenerator::NamelinkModeNone; + if (archiveArgs.GetNamelinkOnly()) { + importlinkMode = cmInstallTargetGenerator::NamelinkModeOnly; + } else if (archiveArgs.GetNamelinkSkip()) { + importlinkMode = cmInstallTargetGenerator::NamelinkModeSkip; + } // Check if there is something to do. if (targetList.empty()) { @@ -723,6 +734,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, bool installsArchive = false; bool installsLibrary = false; bool installsNamelink = false; + bool installsImportlink = false; bool installsRuntime = false; bool installsObject = false; bool installsFramework = false; @@ -740,6 +752,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, std::unique_ptr<cmInstallTargetGenerator> archiveGenerator; std::unique_ptr<cmInstallTargetGenerator> libraryGenerator; std::unique_ptr<cmInstallTargetGenerator> namelinkGenerator; + std::unique_ptr<cmInstallTargetGenerator> importlinkGenerator; std::unique_ptr<cmInstallTargetGenerator> runtimeGenerator; std::unique_ptr<cmInstallTargetGenerator> objectGenerator; std::unique_ptr<cmInstallTargetGenerator> frameworkGenerator; @@ -883,6 +896,32 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } namelinkOnly = (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly); + + if (target.GetMakefile()->PlatformSupportsAppleTextStubs() && + target.IsSharedLibraryWithExports()) { + // Apple .tbd files use the ARCHIVE properties + if (!archiveArgs.GetDestination().empty()) { + artifactsSpecified = true; + } + if (importlinkMode != + cmInstallTargetGenerator::NamelinkModeOnly) { + archiveGenerator = CreateInstallTargetGenerator( + target, archiveArgs, true, helper.Makefile->GetBacktrace(), + helper.GetLibraryDestination(&archiveArgs)); + archiveGenerator->SetImportlinkMode( + cmInstallTargetGenerator::NamelinkModeSkip); + } + if (importlinkMode != + cmInstallTargetGenerator::NamelinkModeSkip) { + importlinkGenerator = CreateInstallTargetGenerator( + target, archiveArgs, true, helper.Makefile->GetBacktrace(), + helper.GetLibraryDestination(&archiveArgs), false, true); + importlinkGenerator->SetImportlinkMode( + cmInstallTargetGenerator::NamelinkModeOnly); + } + namelinkOnly = + (importlinkMode == cmInstallTargetGenerator::NamelinkModeOnly); + } } if (runtimeDependencySet && libraryGenerator) { runtimeDependencySet->AddLibrary(libraryGenerator.get()); @@ -1155,6 +1194,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, installsArchive = installsArchive || archiveGenerator; installsLibrary = installsLibrary || libraryGenerator; installsNamelink = installsNamelink || namelinkGenerator; + installsImportlink = installsImportlink || importlinkGenerator; installsRuntime = installsRuntime || runtimeGenerator; installsObject = installsObject || objectGenerator; installsFramework = installsFramework || frameworkGenerator; @@ -1167,6 +1207,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, helper.Makefile->AddInstallGenerator(std::move(archiveGenerator)); helper.Makefile->AddInstallGenerator(std::move(libraryGenerator)); helper.Makefile->AddInstallGenerator(std::move(namelinkGenerator)); + helper.Makefile->AddInstallGenerator(std::move(importlinkGenerator)); helper.Makefile->AddInstallGenerator(std::move(runtimeGenerator)); helper.Makefile->AddInstallGenerator(std::move(objectGenerator)); helper.Makefile->AddInstallGenerator(std::move(frameworkGenerator)); @@ -1201,6 +1242,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, helper.Makefile->GetGlobalGenerator()->AddInstallComponent( libraryArgs.GetNamelinkComponent()); } + if (installsImportlink) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + archiveArgs.GetNamelinkComponent()); + } if (installsRuntime) { helper.Makefile->GetGlobalGenerator()->AddInstallComponent( runtimeArgs.GetComponent()); diff --git a/Source/cmInstallScriptGenerator.cxx b/Source/cmInstallScriptGenerator.cxx index a5625fe..af531f2 100644 --- a/Source/cmInstallScriptGenerator.cxx +++ b/Source/cmInstallScriptGenerator.cxx @@ -56,12 +56,12 @@ bool cmInstallScriptGenerator::Compute(cmLocalGenerator* lg) std::string cmInstallScriptGenerator::GetScript( std::string const& config) const { - std::string script; + std::string script = this->Script; if (this->AllowGenex && this->ActionsPerConfig) { - script = cmGeneratorExpression::Evaluate(this->Script, - this->LocalGenerator, config); - } else { - script = this->Script; + cmGeneratorExpression::ReplaceInstallPrefix(script, + "${CMAKE_INSTALL_PREFIX}"); + script = + cmGeneratorExpression::Evaluate(script, this->LocalGenerator, config); } return script; } diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 16c5002..6c31da6 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -4,12 +4,15 @@ #include <algorithm> #include <cassert> +#include <functional> #include <map> #include <set> #include <sstream> #include <utility> #include <vector> +#include <cm/optional> + #include "cmComputeLinkInformation.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -40,6 +43,84 @@ std::string computeInstallObjectDir(cmGeneratorTarget* gt, objectDir += gt->GetName(); return objectDir; } + +void computeFilesToInstall( + cmInstallTargetGenerator::Files& files, + cmInstallTargetGenerator::NamelinkModeType namelinkMode, + std::string const& fromDirConfig, std::string const& output, + std::string const& library, std::string const& real, + cm::optional<std::function<void(std::string const&)>> GNUToMS = cm::nullopt) +{ + bool haveNamelink = false; + auto convert = [&GNUToMS](std::string const& file) { + if (GNUToMS) { + (*GNUToMS)(file); + } + }; + + // Library link name. + std::string fromName = cmStrCat(fromDirConfig, output); + std::string toName = output; + + // Library interface name. + std::string fromSOName; + std::string toSOName; + if (library != output) { + haveNamelink = true; + fromSOName = cmStrCat(fromDirConfig, library); + toSOName = library; + } + + // Library implementation name. + std::string fromRealName; + std::string toRealName; + if (real != output && real != library) { + haveNamelink = true; + fromRealName = cmStrCat(fromDirConfig, real); + toRealName = real; + } + + // Add the names based on the current namelink mode. + if (haveNamelink) { + files.NamelinkMode = namelinkMode; + // With a namelink we need to check the mode. + if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) { + // Install the namelink only. + files.From.emplace_back(fromName); + files.To.emplace_back(toName); + convert(output); + } else { + // Install the real file if it has its own name. + if (!fromRealName.empty()) { + files.From.emplace_back(fromRealName); + files.To.emplace_back(toRealName); + convert(real); + } + + // Install the soname link if it has its own name. + if (!fromSOName.empty()) { + files.From.emplace_back(fromSOName); + files.To.emplace_back(toSOName); + convert(library); + } + + // Install the namelink if it is not to be skipped. + if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) { + files.From.emplace_back(fromName); + files.To.emplace_back(toName); + convert(output); + } + } + } else { + // Without a namelink there will be only one file. Install it + // if this is not a namelink-only rule. + if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) { + files.From.emplace_back(fromName); + files.To.emplace_back(toName); + convert(output); + } + } +} } cmInstallTargetGenerator::cmInstallTargetGenerator( @@ -56,6 +137,7 @@ cmInstallTargetGenerator::cmInstallTargetGenerator( { this->ActionsPerConfig = true; this->NamelinkMode = NamelinkModeNone; + this->ImportlinkMode = NamelinkModeNone; } cmInstallTargetGenerator::~cmInstallTargetGenerator() = default; @@ -247,18 +329,21 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles( this->Target->GetLibraryNames(config); if (this->ImportLibrary) { // There is a bug in cmInstallCommand if this fails. - assert(this->NamelinkMode == NamelinkModeNone); + assert(this->Target->Makefile->PlatformSupportsAppleTextStubs() || + this->ImportlinkMode == NamelinkModeNone); + + auto GNUToMS = [this, &config, &files, + &fromDirConfig](const std::string& lib) { + std::string importLib; + if (this->Target->GetImplibGNUtoMS(config, lib, importLib)) { + files.From.emplace_back(fromDirConfig + importLib); + files.To.emplace_back(importLib); + } + }; - std::string from1 = fromDirConfig + targetNames.ImportLibrary; - std::string to1 = targetNames.ImportLibrary; - files.From.emplace_back(std::move(from1)); - files.To.emplace_back(std::move(to1)); - std::string targetNameImportLib; - if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, - targetNameImportLib)) { - files.From.emplace_back(fromDirConfig + targetNameImportLib); - files.To.emplace_back(targetNameImportLib); - } + computeFilesToInstall( + files, this->ImportlinkMode, fromDirConfig, targetNames.ImportOutput, + targetNames.ImportLibrary, targetNames.ImportReal, GNUToMS); // An import library looks like a static library. files.Type = cmInstallType_STATIC_LIBRARY; @@ -318,66 +403,9 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles( files.From.emplace_back(std::move(from1)); files.To.emplace_back(std::move(to1)); } else { - bool haveNamelink = false; - - // Library link name. - std::string fromName = fromDirConfig + targetNames.Output; - std::string toName = targetNames.Output; - - // Library interface name. - std::string fromSOName; - std::string toSOName; - if (targetNames.SharedObject != targetNames.Output) { - haveNamelink = true; - fromSOName = fromDirConfig + targetNames.SharedObject; - toSOName = targetNames.SharedObject; - } - - // Library implementation name. - std::string fromRealName; - std::string toRealName; - if (targetNames.Real != targetNames.Output && - targetNames.Real != targetNames.SharedObject) { - haveNamelink = true; - fromRealName = fromDirConfig + targetNames.Real; - toRealName = targetNames.Real; - } - - // Add the names based on the current namelink mode. - if (haveNamelink) { - files.NamelinkMode = this->NamelinkMode; - // With a namelink we need to check the mode. - if (this->NamelinkMode == NamelinkModeOnly) { - // Install the namelink only. - files.From.emplace_back(fromName); - files.To.emplace_back(toName); - } else { - // Install the real file if it has its own name. - if (!fromRealName.empty()) { - files.From.emplace_back(fromRealName); - files.To.emplace_back(toRealName); - } - - // Install the soname link if it has its own name. - if (!fromSOName.empty()) { - files.From.emplace_back(fromSOName); - files.To.emplace_back(toSOName); - } - - // Install the namelink if it is not to be skipped. - if (this->NamelinkMode != NamelinkModeSkip) { - files.From.emplace_back(fromName); - files.To.emplace_back(toName); - } - } - } else { - // Without a namelink there will be only one file. Install it - // if this is not a namelink-only rule. - if (this->NamelinkMode != NamelinkModeOnly) { - files.From.emplace_back(fromName); - files.To.emplace_back(toName); - } - } + computeFilesToInstall(files, this->NamelinkMode, fromDirConfig, + targetNames.Output, targetNames.SharedObject, + targetNames.Real); } } @@ -425,6 +453,12 @@ std::string cmInstallTargetGenerator::GetInstallFilename( "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { fname = targetNames.ImportLibrary; } + } else if (nameType == NameImplibReal) { + // Use the import library name. + if (!target->GetImplibGNUtoMS(config, targetNames.ImportReal, fname, + "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { + fname = targetNames.ImportReal; + } } else if (nameType == NameReal) { // Use the canonical name. fname = targetNames.Real; @@ -434,11 +468,14 @@ std::string cmInstallTargetGenerator::GetInstallFilename( } } else { cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config); - if (nameType == NameImplib) { + if (nameType == NameImplib || nameType == NameImplibReal) { + const auto& importName = nameType == NameImplib + ? targetNames.ImportLibrary + : targetNames.ImportReal; // Use the import library name. - if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname, + if (!target->GetImplibGNUtoMS(config, importName, fname, "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { - fname = targetNames.ImportLibrary; + fname = importName; } } else if (nameType == NameSO) { // Use the soname. @@ -784,7 +821,7 @@ void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent, } // Don't handle OSX Bundles. - if (this->Target->Target->GetMakefile()->IsOn("APPLE") && + if (this->Target->IsApple() && this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) { return; } @@ -796,7 +833,7 @@ void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent, std::string stripArgs; // macOS 'strip' is picky, executables need '-u -r' and dylibs need '-x'. - if (this->Target->Target->GetMakefile()->IsOn("APPLE")) { + if (this->Target->IsApple()) { if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY || this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) { stripArgs = "-x "; @@ -822,7 +859,7 @@ void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent, // Perform post-installation processing on the file depending // on its type. - if (!this->Target->Target->GetMakefile()->IsOn("APPLE")) { + if (!this->Target->IsApple()) { return; } diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index 3fc4b59..2f41163 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -39,6 +39,10 @@ public: NamelinkModeSkip }; void SetNamelinkMode(NamelinkModeType mode) { this->NamelinkMode = mode; } + void SetImportlinkMode(NamelinkModeType mode) + { + this->ImportlinkMode = mode; + } std::string GetInstallFilename(const std::string& config) const; @@ -50,7 +54,8 @@ public: NameNormal, NameImplib, NameSO, - NameReal + NameReal, + NameImplibReal }; static std::string GetInstallFilename(const cmGeneratorTarget* target, @@ -121,6 +126,7 @@ protected: cmGeneratorTarget* Target = nullptr; std::string const FilePermissions; NamelinkModeType NamelinkMode; + NamelinkModeType ImportlinkMode; bool const ImportLibrary; bool const Optional; }; diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 75ec694..bd7eb3f 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -85,6 +85,7 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER", "CMAKE_RANLIB", "CMAKE_LINKER", "CMAKE_MT", + "CMAKE_TAPI", "CMAKE_CUDA_HOST_COMPILER", "CMAKE_CUDA_HOST_LINK_LAUNCHER", "CMAKE_CL_SHOWINCLUDES_PREFIX" }; @@ -134,6 +135,13 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); } + // OSX SYSROOT can be required by some tools, like tapi + { + cmValue osxSysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); + this->VariableMappings["CMAKE_OSX_SYSROOT"] = + osxSysroot.IsEmpty() ? "/" : this->EscapeForShell(*osxSysroot, true); + } + if (cmValue appleArchSysroots = this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) { std::string const& appleArchs = @@ -827,13 +835,18 @@ cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const return this->Makefile->GetStateSnapshot(); } -cmValue cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target, - const std::string& prop) +std::string cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target, + const std::string& prop, + const std::string& config) { + cmValue value = this->Makefile->GetProperty(prop); if (target) { - return target->GetProperty(prop); + value = target->GetProperty(prop); + } + if (value) { + return cmGeneratorExpression::Evaluate(*value, this, config, target); } - return this->Makefile->GetProperty(prop); + return ""; } std::string cmLocalGenerator::ConvertToIncludeReference( @@ -1639,7 +1652,7 @@ static std::string GetFrameworkFlags(const std::string& lang, cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* mf = lg->GetMakefile(); - if (!mf->IsOn("APPLE")) { + if (!target->IsApple()) { return std::string(); } @@ -1813,7 +1826,7 @@ std::string cmLocalGenerator::GetLinkLibsCMP0065( // OLD behavior is to always add the flags, except on AIX where // we compute symbol exports if ENABLE_EXPORTS is on. add_shlib_flags = - !(tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS")); + !(tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS")); break; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: @@ -1825,7 +1838,7 @@ std::string cmLocalGenerator::GetLinkLibsCMP0065( // NEW behavior is to only add the flags if ENABLE_EXPORTS is on, // except on AIX where we compute symbol exports. add_shlib_flags = - !tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS"); + !tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS"); break; } @@ -1859,7 +1872,7 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, const std::string& filterArch) { // Only add Apple specific flags on Apple platforms - if (this->Makefile->IsOn("APPLE") && this->EmitUniversalBinaryFlags) { + if (target->IsApple() && this->EmitUniversalBinaryFlags) { std::vector<std::string> archs; target->GetAppleArchs(config, archs); if (!archs.empty() && @@ -2841,7 +2854,6 @@ void cmLocalGenerator::CopyPchCompilePdb( auto cc = cm::make_unique<cmCustomCommand>(); cc->SetCommandLines(commandLines); cc->SetComment(no_message); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetStdPipesUTF8(true); if (this->GetGlobalGenerator()->IsVisualStudio()) { diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 20f23de..bda82bc 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -532,7 +532,9 @@ public: void CreateEvaluationFileOutputs(const std::string& config); void ProcessEvaluationFiles(std::vector<std::string>& generatedFiles); - cmValue GetRuleLauncher(cmGeneratorTarget* target, const std::string& prop); + std::string GetRuleLauncher(cmGeneratorTarget* target, + const std::string& prop, + const std::string& config); protected: // The default implementation converts to a Windows shortpath to diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 1e2ea2a..f8027c0 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -586,32 +586,34 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( cmNinjaDeps orderOnlyDeps; - // A custom command may appear on multiple targets. However, some build - // systems exist where the target dependencies on some of the targets are - // overspecified, leading to a dependency cycle. If we assume all target - // dependencies are a superset of the true target dependencies for this - // custom command, we can take the set intersection of all target - // dependencies to obtain a correct dependency list. - // - // FIXME: This won't work in certain obscure scenarios involving indirect - // dependencies. - auto j = targets.begin(); - assert(j != targets.end()); - this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure( - *j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1); - std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); - ++j; - - for (; j != targets.end(); ++j) { - std::vector<std::string> jDeps; - std::vector<std::string> depsIntersection; + if (!cc->GetDependsExplicitOnly()) { + // A custom command may appear on multiple targets. However, some build + // systems exist where the target dependencies on some of the targets are + // overspecified, leading to a dependency cycle. If we assume all target + // dependencies are a superset of the true target dependencies for this + // custom command, we can take the set intersection of all target + // dependencies to obtain a correct dependency list. + // + // FIXME: This won't work in certain obscure scenarios involving indirect + // dependencies. + auto j = targets.begin(); + assert(j != targets.end()); this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure( - *j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1); - std::sort(jDeps.begin(), jDeps.end()); - std::set_intersection(orderOnlyDeps.begin(), orderOnlyDeps.end(), - jDeps.begin(), jDeps.end(), - std::back_inserter(depsIntersection)); - orderOnlyDeps = depsIntersection; + *j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1); + std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); + ++j; + + for (; j != targets.end(); ++j) { + std::vector<std::string> jDeps; + std::vector<std::string> depsIntersection; + this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure( + *j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1); + std::sort(jDeps.begin(), jDeps.end()); + std::set_intersection(orderOnlyDeps.begin(), orderOnlyDeps.end(), + jDeps.begin(), jDeps.end(), + std::back_inserter(depsIntersection)); + orderOnlyDeps = depsIntersection; + } } const std::vector<std::string>& outputs = ccg.GetOutputs(); diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 7172d34..56a41b1 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1003,7 +1003,9 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand( std::string launcher; // Short-circuit if there is no launcher. - cmValue val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM"); + std::string val = this->GetRuleLauncher( + target, "RULE_LAUNCH_CUSTOM", + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); if (cmNonempty(val)) { // Expand rule variables referenced in the given launcher command. cmRulePlaceholderExpander::RuleVariables vars; @@ -1022,7 +1024,7 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand( } vars.Output = output.c_str(); - launcher = *val; + launcher = val; rulePlaceholderExpander->ExpandRuleVariables(this, launcher, vars); if (!launcher.empty()) { launcher += " "; diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index ded1647..6806a5b 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -141,7 +141,6 @@ void cmLocalVisualStudio7Generator::FixGlobalTargets() cc->SetOutputs(force); cc->SetCommandLines(force_commands); cc->SetComment(" "); - cc->SetCMP0116Status(cmPolicies::NEW); if (cmSourceFile* file = this->AddCustomCommandToOutput(std::move(cc), true)) { l->AddSource(file->ResolveFullPath()); @@ -269,7 +268,6 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() cc->SetDepends(listFiles); cc->SetCommandLines(commandLines); cc->SetComment(comment.c_str()); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetStdPipesUTF8(true); this->AddCustomCommandToOutput(std::move(cc), true); diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index d26f383..33843e2 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -208,32 +208,47 @@ bool cmMakefile::CheckCMP0037(std::string const& targetName, return true; } -void cmMakefile::MaybeWarnCMP0074(std::string const& pkg) +void cmMakefile::MaybeWarnCMP0074(std::string const& rootVar, cmValue rootDef, + cm::optional<std::string> const& rootEnv) { - // Warn if a <pkg>_ROOT variable we may use is set. - std::string const varName = pkg + "_ROOT"; - cmValue var = this->GetDefinition(varName); - std::string env; - cmSystemTools::GetEnv(varName, env); - - bool const haveVar = cmNonempty(var); - bool const haveEnv = !env.empty(); - if ((haveVar || haveEnv) && this->WarnedCMP0074.insert(varName).second) { + // Warn if a <PackageName>_ROOT variable we may use is set. + if ((rootDef || rootEnv) && this->WarnedCMP0074.insert(rootVar).second) { std::ostringstream w; w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; - if (haveVar) { - w << "CMake variable " << varName << " is set to:\n" - << " " << *var << "\n"; + if (rootDef) { + w << "CMake variable " << rootVar << " is set to:\n" + << " " << *rootDef << "\n"; } - if (haveEnv) { - w << "Environment variable " << varName << " is set to:\n" - << " " << env << "\n"; + if (rootEnv) { + w << "Environment variable " << rootVar << " is set to:\n" + << " " << *rootEnv << "\n"; } w << "For compatibility, CMake is ignoring the variable."; this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); } } +void cmMakefile::MaybeWarnCMP0144(std::string const& rootVAR, cmValue rootDEF, + cm::optional<std::string> const& rootENV) +{ + // Warn if a <PACKAGENAME>_ROOT variable we may use is set. + if ((rootDEF || rootENV) && this->WarnedCMP0144.insert(rootVAR).second) { + std::ostringstream w; + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0144) << "\n"; + if (rootDEF) { + w << "CMake variable " << rootVAR << " is set to:\n" + << " " << *rootDEF << "\n"; + } + if (rootENV) { + w << "Environment variable " << rootVAR << " is set to:\n" + << " " << *rootENV << "\n"; + } + w << "For compatibility, find_package is ignoring the variable, but " + "code in a .cmake module might still use it."; + this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + } +} + cmBTStringRange cmMakefile::GetIncludeDirectoriesEntries() const { return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries(); @@ -1117,7 +1132,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget( // Always create the byproduct sources and mark them generated. this->CreateGeneratedOutputs(byproducts); - cc->SetCMP0116Status(this->GetPolicyStatus(cmPolicies::CMP0116)); + cc->RecordPolicyValues(this->GetStateSnapshot()); // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( @@ -1156,7 +1171,7 @@ void cmMakefile::AddCustomCommandToOutput( this->CreateGeneratedOutputs(outputs); this->CreateGeneratedOutputs(byproducts); - cc->SetCMP0116Status(this->GetPolicyStatus(cmPolicies::CMP0116)); + cc->RecordPolicyValues(this->GetStateSnapshot()); // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( @@ -1274,7 +1289,7 @@ cmTarget* cmMakefile::AddUtilityCommand(const std::string& utilityName, // Always create the byproduct sources and mark them generated. this->CreateGeneratedOutputs(byproducts); - cc->SetCMP0116Status(this->GetPolicyStatus(cmPolicies::CMP0116)); + cc->RecordPolicyValues(this->GetStateSnapshot()); // Dispatch command creation to allow generator expressions in outputs. this->AddGeneratorAction( @@ -2115,12 +2130,21 @@ cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type, return &this->CreateNewTarget(name, type).first; } +cmTarget* cmMakefile::AddSynthesizedTarget(cmStateEnums::TargetType type, + const std::string& name) +{ + return &this + ->CreateNewTarget(name, type, cmTarget::PerConfig::Yes, + cmTarget::Visibility::Generated) + .first; +} + std::pair<cmTarget&, bool> cmMakefile::CreateNewTarget( const std::string& name, cmStateEnums::TargetType type, - cmTarget::PerConfig perConfig) + cmTarget::PerConfig perConfig, cmTarget::Visibility vis) { - auto ib = this->Targets.emplace( - name, cmTarget(name, type, cmTarget::VisibilityNormal, this, perConfig)); + auto ib = + this->Targets.emplace(name, cmTarget(name, type, vis, this, perConfig)); auto it = ib.first; if (!ib.second) { return std::make_pair(std::ref(it->second), false); @@ -2469,6 +2493,11 @@ bool cmMakefile::PlatformIsAppleEmbedded() const return this->GetAppleSDKType() != AppleSDK::MacOS; } +bool cmMakefile::PlatformSupportsAppleTextStubs() const +{ + return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI"); +} + const char* cmMakefile::GetSONameFlag(const std::string& language) const { std::string name = "CMAKE_SHARED_LIBRARY_SONAME"; @@ -4203,8 +4232,8 @@ cmTarget* cmMakefile::AddImportedTarget(const std::string& name, // Create the target. std::unique_ptr<cmTarget> target( new cmTarget(name, type, - global ? cmTarget::VisibilityImportedGlobally - : cmTarget::VisibilityImported, + global ? cmTarget::Visibility::ImportedGlobally + : cmTarget::Visibility::Imported, this, cmTarget::PerConfig::Yes)); // Add to the set of available imported targets. @@ -4486,7 +4515,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0108 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0114 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 3866aca..6f04937 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -241,10 +241,13 @@ public: std::pair<cmTarget&, bool> CreateNewTarget( const std::string& name, cmStateEnums::TargetType type, - cmTarget::PerConfig perConfig = cmTarget::PerConfig::Yes); + cmTarget::PerConfig perConfig = cmTarget::PerConfig::Yes, + cmTarget::Visibility vis = cmTarget::Visibility::Normal); cmTarget* AddNewTarget(cmStateEnums::TargetType type, const std::string& name); + cmTarget* AddSynthesizedTarget(cmStateEnums::TargetType type, + const std::string& name); /** Create a target instance for the utility. */ cmTarget* AddNewUtilityTarget(const std::string& utilityName, @@ -559,6 +562,10 @@ public: /** Return whether the target platform is Apple iOS. */ bool PlatformIsAppleEmbedded() const; + /** Return whether the target platform supports generation of text base stubs + (.tbd file) describing exports (Apple specific). */ + bool PlatformSupportsAppleTextStubs() const; + /** Retrieve soname flag for the specified language if supported */ const char* GetSONameFlag(const std::string& language) const; @@ -1008,7 +1015,10 @@ public: bool GetDebugFindPkgMode() const; - void MaybeWarnCMP0074(std::string const& pkg); + void MaybeWarnCMP0074(std::string const& rootVar, cmValue rootDef, + cm::optional<std::string> const& rootEnv); + void MaybeWarnCMP0144(std::string const& rootVAR, cmValue rootDEF, + cm::optional<std::string> const& rootENV); void MaybeWarnUninitialized(std::string const& variable, const char* sourceFilename) const; bool IsProjectFile(const char* filename) const; @@ -1186,6 +1196,7 @@ private: bool CheckSystemVars; bool CheckCMP0000; std::set<std::string> WarnedCMP0074; + std::set<std::string> WarnedCMP0144; bool IsSourceFileTryCompile; mutable bool SuppressSideEffects; ImportedTargetScope CurrentImportedTargetScope = ImportedTargetScope::Local; diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index e53d28c..1960073 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -185,14 +185,15 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( std::string linkLibs; this->CreateLinkLibs( linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, - cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); + linkLanguage, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; this->CreateObjectLists( useLinkScript, false, useResponseFileForObjects, buildObjs, depends, - false, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); + false, linkLanguage, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); cmRulePlaceholderExpander::RuleVariables vars; std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); @@ -222,10 +223,11 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( std::string launcher; - cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, - "RULE_LAUNCH_LINK"); + std::string val = this->LocalGenerator->GetRuleLauncher( + this->GeneratorTarget, "RULE_LAUNCH_LINK", + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -502,13 +504,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) // Collect up flags to link in needed libraries. std::string linkLibs; this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + useResponseFileForLibs, depends, linkLanguage); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects, - buildObjs, depends, useWatcomQuote); + buildObjs, depends, useWatcomQuote, linkLanguage); if (!this->DeviceLinkObject.empty()) { buildObjs += " " + this->LocalGenerator->ConvertToOutputFormat( @@ -587,10 +589,11 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) std::string launcher; - cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, - "RULE_LAUNCH_LINK"); + std::string val = this->LocalGenerator->GetRuleLauncher( + this->GeneratorTarget, "RULE_LAUNCH_LINK", + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 9669293..3e4a08e 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -327,14 +327,14 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( this->CreateLinkLibs( linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, - cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); + linkLanguage, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; this->CreateObjectLists( useLinkScript, false, // useArchiveRules - useResponseFileForObjects, buildObjs, depends, false, + useResponseFileForObjects, buildObjs, depends, false, linkLanguage, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); @@ -362,10 +362,11 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( vars.TargetCompilePDB = targetOutPathCompilePDB.c_str(); std::string launcher; - cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, - "RULE_LAUNCH_LINK"); + std::string val = this->LocalGenerator->GetRuleLauncher( + this->GeneratorTarget, "RULE_LAUNCH_LINK", + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -464,9 +465,20 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( std::string outpathImp; if (this->GeneratorTarget->IsFrameworkOnApple()) { outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName()); + cmOSXBundleGenerator::SkipParts bundleSkipParts; + if (this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) { + bundleSkipParts.TextStubs = false; + } this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output, - outpath, this->GetConfigName()); + outpath, this->GetConfigName(), + bundleSkipParts); outpath += '/'; + if (!this->TargetNames.ImportLibrary.empty()) { + outpathImp = this->GeneratorTarget->GetDirectory( + this->GetConfigName(), cmStateEnums::ImportLibraryArtifact); + cmSystemTools::MakeDirectory(outpathImp); + outpathImp += '/'; + } } else if (this->GeneratorTarget->IsCFBundleOnApple()) { outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName()); this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, outpath, @@ -678,11 +690,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( } // Expand the rule variables. + std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( + this->LocalGenerator->CreateRulePlaceholderExpander()); + bool useWatcomQuote = + this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE"); std::vector<std::string> real_link_commands; { - bool useWatcomQuote = - this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE"); - // Set path conversion for link script shells. this->LocalGenerator->SetLinkScriptShell(useLinkScript); @@ -699,7 +712,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( linkLineComputer->SetRelink(relink); this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + useResponseFileForLibs, depends, linkLanguage); } // Construct object file lists that may be needed to expand the @@ -707,7 +720,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( std::string buildObjs; this->CreateObjectLists(useLinkScript, useArchiveRules, useResponseFileForObjects, buildObjs, depends, - useWatcomQuote); + useWatcomQuote, linkLanguage); if (!this->DeviceLinkObject.empty()) { buildObjs += " " + this->LocalGenerator->ConvertToOutputFormat( @@ -808,14 +821,13 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( } std::string launcher; - cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget, - "RULE_LAUNCH_LINK"); + std::string val = this->LocalGenerator->GetRuleLauncher( + this->GeneratorTarget, "RULE_LAUNCH_LINK", + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } - std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( - this->LocalGenerator->CreateRulePlaceholderExpander()); // Construct the main link rule and expand placeholders. rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport); if (useArchiveRules) { @@ -948,6 +960,86 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, commands, false); + // Add rule to generate text-based stubs, if required + if (this->GeneratorTarget->IsApple() && + this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) { + auto genStubsRule = + this->Makefile->GetDefinition("CMAKE_CREATE_TEXT_STUBS"); + auto genStubs_commands = cmExpandedList(genStubsRule); + + std::string TBDFullPath = + cmStrCat(outpathImp, this->TargetNames.ImportOutput); + std::string TBDFullPathReal = + cmStrCat(outpathImp, this->TargetNames.ImportReal); + std::string TBDFullPathSO = + cmStrCat(outpathImp, this->TargetNames.ImportLibrary); + + // Expand placeholders. + cmRulePlaceholderExpander::RuleVariables vars; + std::string target = this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal), + cmOutputConverter::SHELL, useWatcomQuote); + vars.Target = target.c_str(); + std::string TBDOutPathReal = this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathReal), + cmOutputConverter::SHELL, useWatcomQuote); + rulePlaceholderExpander->SetTargetImpLib(TBDOutPathReal); + for (std::string& command : genStubs_commands) { + rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, + command, vars); + } + outputs.clear(); + outputs.push_back(TBDFullPathReal); + if (this->TargetNames.ImportLibrary != this->TargetNames.ImportReal) { + outputs.push_back(TBDFullPathSO); + } + if (this->TargetNames.ImportOutput != this->TargetNames.ImportLibrary && + this->TargetNames.ImportOutput != this->TargetNames.ImportReal) { + outputs.push_back(TBDFullPath); + } + this->ExtraFiles.insert(TBDFullPath); + + depends.clear(); + depends.push_back(targetFullPathReal); + + // Add a rule to create necessary symlinks for the library. + // Frameworks are handled by cmOSXBundleGenerator. + if (TBDFullPath != TBDFullPathReal && + !this->GeneratorTarget->IsFrameworkOnApple()) { + auto TBDOutPathSO = this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathSO), + cmOutputConverter::SHELL, useWatcomQuote); + auto TBDOutPath = this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPath), + cmOutputConverter::SHELL, useWatcomQuote); + + std::string symlink = + cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_library ", TBDOutPathReal, + ' ', TBDOutPathSO, ' ', TBDOutPath); + commands1.push_back(std::move(symlink)); + this->LocalGenerator->CreateCDCommand( + commands1, this->Makefile->GetCurrentBinaryDirectory(), + this->LocalGenerator->GetBinaryDirectory()); + cm::append(genStubs_commands, commands1); + commands1.clear(); + } + + this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, + genStubs_commands, false); + + // clean actions for apple specific outputs + // clean actions for ImportLibrary are already specified + if (this->TargetNames.ImportReal != this->TargetNames.ImportLibrary) { + libCleanFiles.insert( + this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathReal)); + } + if (this->TargetNames.ImportOutput != this->TargetNames.ImportReal && + this->TargetNames.ImportOutput != this->TargetNames.ImportLibrary) { + libCleanFiles.insert( + this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPath)); + } + } + // Write the main driver rule to build everything in this target. this->WriteTargetDriverRule(targetFullPath, relink); diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index c40d685..e217dd9 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -1044,7 +1044,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); std::string evaluatedClauncher = cmGeneratorExpression::Evaluate( - *clauncher, this->LocalGenerator, config); + *clauncher, this->LocalGenerator, config, this->GeneratorTarget, + nullptr, this->GeneratorTarget, lang); if (!evaluatedClauncher.empty()) { compilerLauncher = evaluatedClauncher; } @@ -1166,10 +1167,11 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string launcher; { - cmValue val = this->LocalGenerator->GetRuleLauncher( - this->GeneratorTarget, "RULE_LAUNCH_COMPILE"); + std::string val = this->LocalGenerator->GetRuleLauncher( + this->GeneratorTarget, "RULE_LAUNCH_COMPILE", + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } } @@ -2174,16 +2176,16 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( std::string cmMakefileTargetGenerator::CreateResponseFile( const std::string& name, std::string const& options, - std::vector<std::string>& makefile_depends) + std::vector<std::string>& makefile_depends, std::string const& language) { // FIXME: Find a better way to determine the response file encoding, // perhaps using tool-specific platform information variables. // For now, use the makefile encoding as a heuristic. codecvt::Encoding responseEncoding = this->GlobalGenerator->GetMakefileEncoding(); - // Non-MSVC tooling may not understand a BOM. + // Non-MSVC tooling doesn't understand BOM encoded files. if (responseEncoding == codecvt::UTF8_WITH_BOM && - !this->Makefile->IsOn("MSVC")) { + (language == "CUDA" || !this->Makefile->IsOn("MSVC"))) { responseEncoding = codecvt::UTF8; } @@ -2220,7 +2222,7 @@ cmMakefileTargetGenerator::CreateLinkLineComputer( void cmMakefileTargetGenerator::CreateLinkLibs( cmLinkLineComputer* linkLineComputer, std::string& linkLibs, bool useResponseFile, std::vector<std::string>& makefile_depends, - ResponseFlagFor responseMode) + std::string const& linkLanguage, ResponseFlagFor responseMode) { std::string frameworkPath; std::string linkPath; @@ -2238,8 +2240,9 @@ void cmMakefileTargetGenerator::CreateLinkLibs( // Create this response file. std::string responseFileName = (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp"; - std::string link_rsp = - this->CreateResponseFile(responseFileName, linkLibs, makefile_depends); + std::string responseLang = (responseMode == Link) ? linkLanguage : "CUDA"; + std::string link_rsp = this->CreateResponseFile( + responseFileName, linkLibs, makefile_depends, responseLang); // Reference the response file. linkLibs = cmStrCat(responseFlag, @@ -2251,7 +2254,8 @@ void cmMakefileTargetGenerator::CreateLinkLibs( void cmMakefileTargetGenerator::CreateObjectLists( bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote, ResponseFlagFor responseMode) + bool useWatcomQuote, std::string const& linkLanguage, + ResponseFlagFor responseMode) { std::string variableName; std::string variableNameExternal; @@ -2280,7 +2284,7 @@ void cmMakefileTargetGenerator::CreateObjectLists( // Create this response file. std::string objects_rsp = this->CreateResponseFile( - responseFileName, object_strings[i], makefile_depends); + responseFileName, object_strings[i], makefile_depends, linkLanguage); // Separate from previous response file references. buildObjs += sep; @@ -2332,8 +2336,8 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, } std::string name = cmStrCat("includes_", lang, ".rsp"); std::string arg = std::move(responseFlag) + - this->CreateResponseFile(name, includeFlags, - this->FlagFileDepends[lang]); + this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang], + lang); this->LocalGenerator->AppendFlags(flags, arg); } else { this->LocalGenerator->AppendFlags(flags, includeFlags); diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index 5d614fe..98c3a0e 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -160,7 +160,8 @@ protected: response file name. */ std::string CreateResponseFile(const std::string& name, std::string const& options, - std::vector<std::string>& makefile_depends); + std::vector<std::string>& makefile_depends, + std::string const& language); bool CheckUseResponseFileForObjects(std::string const& l) const; bool CheckUseResponseFileForLibraries(std::string const& l) const; @@ -175,13 +176,14 @@ protected: void CreateLinkLibs(cmLinkLineComputer* linkLineComputer, std::string& linkLibs, bool useResponseFile, std::vector<std::string>& makefile_depends, + std::string const& linkLanguage, ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Create lists of object files for linking and cleaning. */ void CreateObjectLists(bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote, + bool useWatcomQuote, std::string const& linkLanguage, ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Add commands for generate def files */ diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index a1633ca..4d68460 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -204,6 +204,15 @@ std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule( '_', config); } +std::string cmNinjaNormalTargetGenerator::TextStubsGeneratorRule( + const std::string& config) const +{ + return cmStrCat( + "TEXT_STUBS_GENERATOR__", + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), + '_', config); +} + struct cmNinjaRemoveNoOpCommands { bool operator()(std::string const& cmd) @@ -263,10 +272,10 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule( vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS"; std::string launcher; - cmValue val = this->GetLocalGenerator()->GetRuleLauncher( - this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); + std::string val = this->GetLocalGenerator()->GetRuleLauncher( + this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -458,10 +467,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, } std::string launcher; - cmValue val = this->GetLocalGenerator()->GetRuleLauncher( - this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); + std::string val = this->GetLocalGenerator()->GetRuleLauncher( + this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( @@ -527,6 +536,45 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, this->GetGlobalGenerator()->AddRule(rule); } } + + if (this->GetGeneratorTarget()->IsApple() && + this->GetGeneratorTarget()->HasImportLibrary(config)) { + cmNinjaRule rule(this->TextStubsGeneratorRule(config)); + rule.Comment = cmStrCat("Rule for generating text-based stubs for ", + this->GetVisibleTypeName(), '.'); + rule.Description = "Creating text-based stubs $out"; + + std::string cmd = + this->GetMakefile()->GetDefinition("CMAKE_CREATE_TEXT_STUBS"); + std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( + this->GetLocalGenerator()->CreateRulePlaceholderExpander()); + cmRulePlaceholderExpander::RuleVariables vars; + vars.Target = "$in"; + rulePlaceholderExpander->SetTargetImpLib("$out"); + rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), + cmd, vars); + + rule.Command = + this->GetLocalGenerator()->BuildCommandLine({ cmd }, config, config); + this->GetGlobalGenerator()->AddRule(rule); + + if (tgtNames.ImportOutput != tgtNames.ImportReal && + !this->GetGeneratorTarget()->IsFrameworkOnApple()) { + cmNinjaRule slRule("CMAKE_SYMLINK_IMPORT_LIBRARY"); + { + std::string cmakeCommand = + this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); + std::string slCmd = + cmStrCat(cmakeCommand, " -E cmake_symlink_library $in $SONAME $out"); + slRule.Command = this->GetLocalGenerator()->BuildCommandLine( + { slCmd }, config, config); + } + slRule.Description = "Creating import library symlink $out"; + slRule.Comment = "Rule for creating import library symlink."; + this->GetGlobalGenerator()->AddRule(slRule); + } + } } std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd() @@ -1030,9 +1078,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( // the current configuration has a postfix. The non-postfix configuration // Info.plist can be used by all the other configurations. if (!postFix.empty()) { - bundleSkipParts.infoPlist = true; + bundleSkipParts.InfoPlist = true; } } + if (gt->HasImportLibrary(config)) { + bundleSkipParts.TextStubs = false; + } this->OSXBundleGenerator->CreateFramework( tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts); @@ -1214,7 +1265,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator()); - if (!tgtNames.ImportLibrary.empty()) { + if (!gt->IsApple() && !tgtNames.ImportLibrary.empty()) { const std::string impLibPath = localGen.ConvertToOutputFormat( targetOutputImplib, cmOutputConverter::SHELL); vars["TARGET_IMPLIB"] = impLibPath; @@ -1471,6 +1522,55 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( // Add aliases for the file name and the target name. globalGen->AddTargetAlias(tgtNames.Output, gt, config); globalGen->AddTargetAlias(this->GetTargetName(), gt, config); + + if (this->GetGeneratorTarget()->IsApple() && + this->GetGeneratorTarget()->HasImportLibrary(config)) { + auto dirTBD = + gt->GetDirectory(config, cmStateEnums::ImportLibraryArtifact); + auto targetTBD = + this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportReal)); + this->EnsureParentDirectoryExists(targetTBD); + cmNinjaBuild build(this->TextStubsGeneratorRule(config)); + build.Comment = cmStrCat("Generate the text-based stubs file ", targetTBD); + build.Outputs.push_back(targetTBD); + build.ExplicitDeps.push_back(targetOutputReal); + globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build); + + if (tgtNames.ImportOutput != tgtNames.ImportReal && + !this->GetGeneratorTarget()->IsFrameworkOnApple()) { + auto outputTBD = + this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportOutput)); + std::string const soNameTBD = this->ConvertToNinjaPath( + cmStrCat(dirTBD, '/', tgtNames.ImportLibrary)); + + cmNinjaBuild slBuild("CMAKE_SYMLINK_IMPORT_LIBRARY"); + slBuild.Comment = cmStrCat("Create import library symlink ", outputTBD); + cmNinjaVars slVars; + + // If one link has to be created. + if (targetTBD == soNameTBD || outputTBD == soNameTBD) { + slVars["SONAME"] = this->GetLocalGenerator()->ConvertToOutputFormat( + soNameTBD, cmOutputConverter::SHELL); + } else { + slVars["SONAME"].clear(); + slBuild.Outputs.push_back(soNameTBD); + if (firstForConfig) { + globalGen->GetByproductsForCleanTarget(config).push_back(soNameTBD); + } + } + slBuild.Outputs.push_back(outputTBD); + if (firstForConfig) { + globalGen->GetByproductsForCleanTarget(config).push_back(outputTBD); + } + slBuild.ExplicitDeps.push_back(targetTBD); + slBuild.Variables = std::move(slVars); + + globalGen->WriteBuild(this->GetImplFileStream(fileConfig), slBuild); + } + + // Add alias for the import file name + globalGen->AddTargetAlias(tgtNames.ImportOutput, gt, config); + } } void cmNinjaNormalTargetGenerator::WriteObjectLibStatement( diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 30127fe..85f42a4 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -25,6 +25,7 @@ private: std::string LanguageLinkerCudaDeviceCompileRule( const std::string& config) const; std::string LanguageLinkerCudaFatbinaryRule(const std::string& config) const; + std::string TextStubsGeneratorRule(const std::string& config) const; const char* GetVisibleTypeName() const; void WriteLanguagesRules(const std::string& config); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 13782b0..8663f46 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -670,10 +670,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, cmLocalGenerator::SHELL); std::string launcher; - cmValue val = this->GetLocalGenerator()->GetRuleLauncher( - this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE"); + std::string val = this->GetLocalGenerator()->GetRuleLauncher( + this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", config); if (cmNonempty(val)) { - launcher = cmStrCat(*val, ' '); + launcher = cmStrCat(val, ' '); } std::string const cmakeCmd = @@ -886,7 +886,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER"); cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); std::string evaluatedClauncher = cmGeneratorExpression::Evaluate( - *clauncher, this->LocalGenerator, config); + *clauncher, this->LocalGenerator, config, this->GeneratorTarget, nullptr, + this->GeneratorTarget, lang); if (!evaluatedClauncher.empty()) { compilerLauncher = evaluatedClauncher; } @@ -1139,27 +1140,32 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( } } - for (auto const& langDDIFiles : this->Configs[config].DDIFiles) { - std::string const& language = langDDIFiles.first; - cmNinjaDeps const& ddiFiles = langDDIFiles.second; + for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) { + std::string const& language = langScanningFiles.first; + std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second; cmNinjaBuild build(this->LanguageDyndepRule(language, config)); build.Outputs.push_back(this->GetDyndepFilePath(language, config)); - build.ExplicitDeps = ddiFiles; + build.ImplicitOuts.push_back( + cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), + this->GetGlobalGenerator()->ConfigDirectory(config), '/', + language, "Modules.json")); + for (auto const& scanFiles : scanningFiles) { + if (!scanFiles.ScanningOutput.empty()) { + build.ExplicitDeps.push_back(scanFiles.ScanningOutput); + } + if (!scanFiles.ModuleMapFile.empty()) { + build.ImplicitOuts.push_back(scanFiles.ModuleMapFile); + } + } this->WriteTargetDependInfo(language, config); - // Make sure dyndep files for all our dependencies have already - // been generated so that the '<LANG>Modules.json' files they - // produced as side-effects are available for us to read. - // Ideally we should depend on the '<LANG>Modules.json' files - // from our dependencies directly, but we don't know which of - // our dependencies produces them. Fixing this will require - // refactoring the Ninja generator to generate targets in - // dependency order so that we can collect the needed information. - this->GetLocalGenerator()->AppendTargetDepends( - this->GeneratorTarget, build.OrderOnlyDeps, config, fileConfig, - DependOnTargetArtifact); + for (std::string const& l : + this->GetLinkedTargetDirectories(language, config)) { + build.ImplicitDeps.push_back(cmStrCat(l, '/', language, "Modules.json")); + } this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), build); @@ -1209,7 +1215,6 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, const std::string& ppFileName, bool compilePP, bool compilePPWithDefines, cmNinjaBuild& objBuild, cmNinjaVars& vars, - std::string const& modmapFormat, const std::string& objectFileName, cmLocalGenerator* lg) { @@ -1278,15 +1283,6 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, vars.erase("DEP_FILE"); } - if (!modmapFormat.empty()) { - // XXX(modmap): If changing this path construction, change - // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding - // file path. - std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap"); - scanBuild.Variables["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile; - scanBuild.ImplicitOuts.push_back(ddModmapFile); - } - return scanBuild; } } @@ -1481,7 +1477,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmNinjaBuild ppBuild = GetScanBuildStatement( scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild, - vars, modmapFormat, objectFileName, this->LocalGenerator); + vars, objectFileName, this->LocalGenerator); if (compilePP) { // In case compilation requires flags that are incompatible with @@ -1502,9 +1498,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]); } + ScanningFiles scanningFiles; + if (firstForConfig) { - std::string const ddiFile = cmStrCat(objectFileName, ".ddi"); - this->Configs[config].DDIFiles[language].push_back(ddiFile); + scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi"); } this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), @@ -1518,9 +1515,17 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( vars["dyndep"] = dyndep; if (!modmapFormat.empty()) { - std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap"); + // XXX(modmap): If changing this path construction, change + // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding file + // path. + std::string ddModmapFile = cmStrCat(objectFileName, ".modmap"); vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile; objBuild.OrderOnlyDeps.push_back(ddModmapFile); + scanningFiles.ModuleMapFile = std::move(ddModmapFile); + } + + if (!scanningFiles.IsEmpty()) { + this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); } } diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 8bf7986..8f4a764 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -222,12 +222,23 @@ protected: private: cmLocalNinjaGenerator* LocalGenerator; + struct ScanningFiles + { + bool IsEmpty() const + { + return this->ScanningOutput.empty() && this->ModuleMapFile.empty(); + } + + std::string ScanningOutput; + std::string ModuleMapFile; + }; + struct ByConfig { /// List of object files for this target. cmNinjaDeps Objects; - // Fortran Support - std::map<std::string, cmNinjaDeps> DDIFiles; + // Dyndep Support + std::map<std::string, std::vector<ScanningFiles>> ScanningInfo; // Swift Support Json::Value SwiftOutputMap; std::vector<cmCustomCommand const*> CustomCommands; diff --git a/Source/cmOSXBundleGenerator.cxx b/Source/cmOSXBundleGenerator.cxx index 674735b..4301f05 100644 --- a/Source/cmOSXBundleGenerator.cxx +++ b/Source/cmOSXBundleGenerator.cxx @@ -11,6 +11,7 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmValue.h" class cmSourceFile; @@ -77,7 +78,7 @@ void cmOSXBundleGenerator::CreateFramework( std::string frameworkVersion = this->GT->GetFrameworkVersion(); std::string name = cmSystemTools::GetFilenameName(targetName); - if (!skipParts.infoPlist) { + if (!skipParts.InfoPlist) { // Configure the Info.plist file std::string plist = newoutpath; if (!this->Makefile->PlatformIsAppleEmbedded()) { @@ -120,6 +121,17 @@ void cmOSXBundleGenerator::CreateFramework( cmSystemTools::CreateSymlink(oldName, newName); this->Makefile->AddCMakeOutputFile(newName); + if (!skipParts.TextStubs) { + // foo.tbd -> Versions/Current/foo.tbd + cmValue tbdSuffix = + this->Makefile->GetDefinition("CMAKE_APPLE_IMPORT_FILE_SUFFIX"); + oldName = cmStrCat("Versions/Current/", name, tbdSuffix); + newName = cmStrCat(contentdir, name, tbdSuffix); + cmSystemTools::RemoveFile(newName); + cmSystemTools::CreateSymlink(oldName, newName); + this->Makefile->AddCMakeOutputFile(newName); + } + // Resources -> Versions/Current/Resources if (this->MacContentFolders->find("Resources") != this->MacContentFolders->end()) { diff --git a/Source/cmOSXBundleGenerator.h b/Source/cmOSXBundleGenerator.h index c33b087..38453fd 100644 --- a/Source/cmOSXBundleGenerator.h +++ b/Source/cmOSXBundleGenerator.h @@ -20,11 +20,10 @@ public: struct SkipParts { - SkipParts() - : infoPlist(false) - { - } - bool infoPlist; // NOLINT(modernize-use-default-member-init) + SkipParts() {} // NOLINT(modernize-use-equals-default) + + bool InfoPlist = false; + bool TextStubs = true; }; // create an app bundle at a given root, and return @@ -35,7 +34,7 @@ public: // create a framework at a given root void CreateFramework(const std::string& targetName, const std::string& root, const std::string& config, - const SkipParts& skipParts = SkipParts()); + const SkipParts& skipParts = SkipParts{}); // create a cf bundle at a given root void CreateCFBundle(const std::string& targetName, const std::string& root, diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index da5f5e5..d5e5725 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -259,8 +259,7 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, { // Warn about policy versions for which support will be removed. if (warnCompat == WarnCompat::On && - (majorVer < 2 || (majorVer == 2 && minorVer < 8) || - (majorVer == 2 && minorVer == 8 && patchVer < 12)) && + (majorVer < 3 || (majorVer == 3 && minorVer < 5)) && // Avoid warning on calls generated by install(EXPORT) // in CMake versions prior to 3.18. !(majorVer == 2 && minorVer == 6 && patchVer == 0 && @@ -269,7 +268,7 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, "cmake_policy") == 0)) { mf->IssueMessage( MessageType::DEPRECATION_WARNING, - "Compatibility with CMake < 2.8.12 will be removed from " + "Compatibility with CMake < 3.5 will be removed from " "a future version of CMake.\n" "Update the VERSION argument <min> value or use a ...<max> suffix " "to tell CMake that the project does not need compatibility with " diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index fa24f57..830e14b 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -434,7 +434,12 @@ class cmMakefile; 3, 25, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0143, \ "Global property USE_FOLDERS treated as ON by default", 3, 26, 0, \ - cmPolicies::WARN) + cmPolicies::WARN) \ + SELECT(POLICY, CMP0144, \ + "find_package uses upper-case <PACKAGENAME>_ROOT variables.", 3, 27, \ + 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0145, "The Dart and FindDart modules are removed.", 3, \ + 27, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -474,6 +479,8 @@ class cmMakefile; F(CMP0131) \ F(CMP0142) +#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) F(CMP0116) + /** \class cmPolicies * \brief Handles changes in CMake behavior and policies * diff --git a/Source/cmProcessOutput.cxx b/Source/cmProcessOutput.cxx index 10c4215..e1df661 100644 --- a/Source/cmProcessOutput.cxx +++ b/Source/cmProcessOutput.cxx @@ -85,7 +85,7 @@ bool cmProcessOutput::DecodeText(std::string raw, std::string& decoded, rawparts[id - 1] += *(raw.end() - 1); raw.resize(raw.size() - 1); } - success = DoDecodeText(raw, decoded, NULL); + success = DoDecodeText(raw, decoded, nullptr); } else { bool restoreDecoded = false; std::string firstDecoded = decoded; @@ -114,7 +114,7 @@ bool cmProcessOutput::DecodeText(std::string raw, std::string& decoded, } } } else { - success = DoDecodeText(raw, decoded, NULL); + success = DoDecodeText(raw, decoded, nullptr); } } return success; @@ -143,7 +143,7 @@ bool cmProcessOutput::DoDecodeText(std::string raw, std::string& decoded, { bool success = false; const int wlength = - MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), NULL, 0); + MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), nullptr, 0); auto wdata = cm::make_unique<wchar_t[]>(wlength); int r = MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), wdata.get(), wlength); @@ -156,10 +156,10 @@ bool cmProcessOutput::DoDecodeText(std::string raw, std::string& decoded, } } int length = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength, - NULL, 0, NULL, NULL); + nullptr, 0, nullptr, nullptr); auto data = cm::make_unique<char[]>(length); r = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength, - data.get(), length, NULL, NULL); + data.get(), length, nullptr, nullptr); if (r > 0) { decoded = std::string(data.get(), length); success = true; diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index b7ea7d6..9e3fe7f 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -13,7 +13,6 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmPolicies.h" #include "cmProcessOutput.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenInitializer.h" @@ -173,7 +172,6 @@ void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget( // Create utility target auto cc = cm::make_unique<cmCustomCommand>(); cc->SetWorkingDirectory(makefile->GetHomeOutputDirectory().c_str()); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetComment(comment.c_str()); cmTarget* target = localGen->AddUtilityCommand(name, true, std::move(cc)); diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 66e591e..410330a 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -1238,7 +1238,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() cc->SetDepends(uicDependencies); cc->SetComment(""); cc->SetWorkingDirectory(this->Dir.Work.c_str()); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetStdPipesUTF8(stdPipesUTF8); this->LocalGen->AddCustomCommandToOutput(std::move(cc)); @@ -1332,7 +1331,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() cc->SetByproducts(timestampTargetProvides); cc->SetDepends(dependencies); cc->SetCommandLines(timestampTargetCommandLines); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand( timestampTargetName, true, std::move(cc)); @@ -1371,7 +1369,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() cc->SetCommandLines(commandLines); cc->SetComment(autogenComment.c_str()); cc->SetWorkingDirectory(this->Dir.Work.c_str()); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetDepfile(this->AutogenTarget.DepFile); cc->SetStdPipesUTF8(stdPipesUTF8); @@ -1391,7 +1388,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() cc->SetByproducts(autogenByproducts); cc->SetDepends(dependencies); cc->SetCommandLines(commandLines); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetEscapeOldStyle(false); cc->SetComment(autogenComment.c_str()); cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand( @@ -1472,7 +1468,6 @@ bool cmQtAutoGenInitializer::InitRccTargets() auto cc = cm::make_unique<cmCustomCommand>(); cc->SetWorkingDirectory(this->Dir.Work.c_str()); cc->SetCommandLines(commandLines); - cc->SetCMP0116Status(cmPolicies::NEW); cc->SetComment(ccComment.c_str()); cc->SetStdPipesUTF8(true); diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx index fa9d4cc..71b77e0 100644 --- a/Source/cmRST.cxx +++ b/Source/cmRST.cxx @@ -20,7 +20,7 @@ cmRST::cmRST(std::ostream& os, std::string docroot) : OS(os) , DocRoot(std::move(docroot)) , CMakeDirective("^.. (cmake:)?(" - "command|envvar|genex|variable" + "command|envvar|genex|signature|variable" ")::[ \t]+([^ \t\n]+)$") , CMakeModuleDirective("^.. cmake-module::[ \t]+([^ \t\n]+)$") , ParsedLiteralDirective("^.. parsed-literal::[ \t]*(.*)$") diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index 3fa0051..6224d0e 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -4,6 +4,9 @@ #include <utility> +#include <cm/string_view> +#include <cmext/string_view> + #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" @@ -221,6 +224,11 @@ bool cmSourceFile::FindFullPath(std::string* error, case cmPolicies::NEW: break; } + if (lPath == "FILE_SET"_s) { + err += "\nHint: the FILE_SET keyword may only appear after a visibility " + "specifier or another FILE_SET within the target_sources() " + "command."; + } if (error != nullptr) { *error = std::move(err); } else { diff --git a/Source/cmState.h b/Source/cmState.h index 9a17b22..0a42df0 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -253,18 +253,6 @@ public: private: friend class cmake; - void AddCacheEntry(const std::string& key, const char* value, - const char* helpString, cmStateEnums::CacheEntryType type) - { - this->AddCacheEntry(key, - value ? cmValue(std::string(value)) : cmValue(nullptr), - helpString, type); - } - void AddCacheEntry(const std::string& key, const std::string& value, - const char* helpString, cmStateEnums::CacheEntryType type) - { - this->AddCacheEntry(key, cmValue(value), helpString, type); - } void AddCacheEntry(const std::string& key, cmValue value, const char* helpString, cmStateEnums::CacheEntryType type); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 0b29b0d..1fb0079 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -956,7 +956,7 @@ std::string cmSystemTools::GetRealPathResolvingWindowsSubst( // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found std::string resolved_path; uv_fs_t req; - int err = uv_fs_realpath(NULL, &req, path.c_str(), NULL); + int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr); if (!err) { resolved_path = std::string((char*)req.ptr); cmSystemTools::ConvertToUnixSlashes(resolved_path); @@ -967,12 +967,12 @@ std::string cmSystemTools::GetRealPathResolvingWindowsSubst( } else if (err == UV_ENOSYS) { resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage); } else if (errorMessage) { - LPSTR message = NULL; + LPSTR message = nullptr; DWORD size = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message, 0, - NULL); + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message, + 0, nullptr); *errorMessage = std::string(message, size); LocalFree(message); @@ -1329,32 +1329,33 @@ std::string cmSystemTools::ComputeCertificateThumbprint( std::string thumbprint; CRYPT_INTEGER_BLOB cryptBlob; - HCERTSTORE certStore = NULL; - PCCERT_CONTEXT certContext = NULL; + HCERTSTORE certStore = nullptr; + PCCERT_CONTEXT certContext = nullptr; HANDLE certFile = CreateFileW( cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ, - FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (certFile != INVALID_HANDLE_VALUE && certFile != NULL) { - DWORD fileSize = GetFileSize(certFile, NULL); + if (certFile != INVALID_HANDLE_VALUE && certFile != nullptr) { + DWORD fileSize = GetFileSize(certFile, nullptr); if (fileSize != INVALID_FILE_SIZE) { auto certData = cm::make_unique<BYTE[]>(fileSize); - if (certData != NULL) { + if (certData != nullptr) { DWORD dwRead = 0; - if (ReadFile(certFile, certData.get(), fileSize, &dwRead, NULL)) { + if (ReadFile(certFile, certData.get(), fileSize, &dwRead, nullptr)) { cryptBlob.cbData = fileSize; cryptBlob.pbData = certData.get(); // Verify that this is a valid cert if (PFXIsPFXBlob(&cryptBlob)) { // Open the certificate as a store - certStore = PFXImportCertStore(&cryptBlob, NULL, CRYPT_EXPORTABLE); - if (certStore != NULL) { + certStore = + PFXImportCertStore(&cryptBlob, nullptr, CRYPT_EXPORTABLE); + if (certStore != nullptr) { // There should only be 1 cert. certContext = CertEnumCertificatesInStore(certStore, certContext); - if (certContext != NULL) { + if (certContext != nullptr) { // The hash is 20 bytes BYTE hashData[20]; DWORD hashLength = 20; @@ -1647,6 +1648,32 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top, return out; } +cm::optional<std::string> cmSystemTools::GetEnvVar(std::string const& var) +{ + cm::optional<std::string> result; + { + std::string value; + if (cmSystemTools::GetEnv(var, value)) { + result = std::move(value); + } + } + return result; +} + +std::vector<std::string> cmSystemTools::SplitEnvPath(std::string const& value) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + static cm::string_view sep = ";"_s; +#else + static cm::string_view sep = ":"_s; +#endif + std::vector<std::string> paths = cmTokenize(value, sep); + for (std::string& p : paths) { + SystemTools::ConvertToUnixSlashes(p); + } + return paths; +} + #ifndef CMAKE_BOOTSTRAP bool cmSystemTools::UnsetEnv(const char* value) { @@ -2375,22 +2402,22 @@ static void EnsureStdPipe(DWORD fd) } SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; + sa.lpSecurityDescriptor = nullptr; sa.bInheritHandle = TRUE; HANDLE h = CreateFileW( L"NUL", fd == STD_INPUT_HANDLE ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, NULL); + FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, nullptr); if (h == INVALID_HANDLE_VALUE) { - LPSTR message = NULL; + LPSTR message = nullptr; DWORD size = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&message, 0, NULL); + nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message, 0, nullptr); std::string msg = std::string(message, size); LocalFree(message); std::cerr << "failed to open NUL for missing stdio pipe: " << msg; @@ -2535,10 +2562,10 @@ void cmSystemTools::FindCMakeResources(const char* argv0) #if defined(_WIN32) && !defined(__CYGWIN__) (void)argv0; // ignore this on windows wchar_t modulepath[_MAX_PATH]; - ::GetModuleFileNameW(NULL, modulepath, sizeof(modulepath)); + ::GetModuleFileNameW(nullptr, modulepath, sizeof(modulepath)); std::string path = cmsys::Encoding::ToNarrow(modulepath); std::string realPath = - cmSystemTools::GetRealPathResolvingWindowsSubst(path, NULL); + cmSystemTools::GetRealPathResolvingWindowsSubst(path, nullptr); if (realPath.empty()) { realPath = path; } @@ -3196,20 +3223,41 @@ bool VersionCompare(cmSystemTools::CompareOp op, const char* lhss, { const char* endl = lhss; const char* endr = rhss; - unsigned long lhs; - unsigned long rhs; while (((*endl >= '0') && (*endl <= '9')) || ((*endr >= '0') && (*endr <= '9'))) { - // Do component-wise comparison. - lhs = strtoul(endl, const_cast<char**>(&endl), 10); - rhs = strtoul(endr, const_cast<char**>(&endr), 10); + // Do component-wise comparison, ignoring leading zeros + // (components are treated as integers, not as mantissas) + while (*endl == '0') { + endl++; + } + while (*endr == '0') { + endr++; + } + + const char* beginl = endl; + const char* beginr = endr; + + // count significant digits + while ((*endl >= '0') && (*endl <= '9')) { + endl++; + } + while ((*endr >= '0') && (*endr <= '9')) { + endr++; + } + + // compare number of digits first + ptrdiff_t r = ((endl - beginl) - (endr - beginr)); + if (r == 0) { + // compare the digits if number of digits is equal + r = strncmp(beginl, beginr, endl - beginl); + } - if (lhs < rhs) { + if (r < 0) { // lhs < rhs, so true if operation is LESS return (op & cmSystemTools::OP_LESS) != 0; } - if (lhs > rhs) { + if (r > 0) { // lhs > rhs, so true if operation is GREATER return (op & cmSystemTools::OP_GREATER) != 0; } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 09f2bf0..7d55d4b 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -399,6 +399,9 @@ public: static std::string RelativeIfUnder(std::string const& top, std::string const& in); + static cm::optional<std::string> GetEnvVar(std::string const& var); + static std::vector<std::string> SplitEnvPath(std::string const& value); + #ifndef CMAKE_BOOTSTRAP /** Remove an environment variable */ static bool UnsetEnv(const char* value); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index debf593..ec87271 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -4,7 +4,6 @@ #include <algorithm> #include <cassert> -#include <initializer_list> #include <iterator> #include <map> #include <set> @@ -273,6 +272,348 @@ struct UsageRequirementProperty std::vector<BT<std::string>> Entries; }; + +struct TargetProperty +{ + enum class InitCondition + { + // Always initialize the property. + Always, + // Never initialize the property. + Never, + // Only initialize if the target can compile sources. + CanCompileSources, + // Only apply to Xcode generators. + NeedsXcode, + // Only apply to Xcode generators on targets that can compile sources. + NeedsXcodeAndCanCompileSources, + // Needs to be a "normal" target (any non-global, non-utility target). + NormalTarget, + // Any non-imported target. + NonImportedTarget, + // Needs to be a "normal" target (any non-global, non-utility target) that + // is not `IMPORTED`. + NormalNonImportedTarget, + // Needs to be a "normal" target with an artifact (no `INTERFACE` + // libraries). + TargetWithArtifact, + // Needs to be a "normal" target with an artifact that is not an + // executable. + NonExecutableWithArtifact, + // Needs to be a linkable library target (no `OBJECT` or `MODULE` + // libraries). + LinkableLibraryTarget, + // Needs to be an executable. + ExecutableTarget, + // Needs to be a shared library (`SHARED`). + SharedLibraryTarget, + // Needs to be a target with meaningful symbol exports (`SHARED` or + // `EXECUTABLE`). + TargetWithSymbolExports, + // Targets with "commands" associated with them. Basically everything + // except global and `INTERFACE` targets. + TargetWithCommands, + }; + + enum class Repetition + { + Once, + PerConfig, + PerConfigPrefix, + }; + + TargetProperty(cm::static_string_view name) + : Name(name) + { + } + + TargetProperty(cm::static_string_view name, cm::static_string_view dflt, + InitCondition init) + : Name(name) + , Default(dflt) + , InitConditional(init) + { + } + + TargetProperty(cm::static_string_view name, InitCondition init) + : Name(name) + , InitConditional(init) + { + } + + TargetProperty(cm::static_string_view name, InitCondition init, + Repetition repeat) + : Name(name) + , InitConditional(init) + , Repeat(repeat) + { + } + + cm::static_string_view const Name; + cm::optional<cm::static_string_view> const Default = {}; + InitCondition const InitConditional = InitCondition::Always; + Repetition const Repeat = Repetition::Once; +}; + +#define IC TargetProperty::InitCondition +#define R TargetProperty::Repetition + +/* clang-format off */ +#define COMMON_LANGUAGE_PROPERTIES(lang) \ + { #lang "_COMPILER_LAUNCHER"_s, IC::CanCompileSources }, \ + { #lang "_STANDARD"_s, IC::CanCompileSources }, \ + { #lang "_STANDARD_REQUIRED"_s, IC::CanCompileSources }, \ + { #lang "_EXTENSIONS"_s, IC::CanCompileSources }, \ + { #lang "_VISIBILITY_PRESET"_s, IC::CanCompileSources } +/* clang-format on */ + +TargetProperty const StaticTargetProperties[] = { + /* clang-format off */ + // Compilation properties + { "COMPILE_WARNING_AS_ERROR"_s, IC::CanCompileSources }, + { "INTERPROCEDURAL_OPTIMIZATION"_s, IC::CanCompileSources }, + { "INTERPROCEDURAL_OPTIMIZATION_"_s, IC::TargetWithArtifact, R::PerConfig }, + { "NO_SYSTEM_FROM_IMPORTED"_s, IC::CanCompileSources }, + // Set to `True` for `SHARED` and `MODULE` targets. + { "POSITION_INDEPENDENT_CODE"_s, IC::CanCompileSources }, + { "VISIBILITY_INLINES_HIDDEN"_s, IC::CanCompileSources }, + // -- Features + // ---- PCH + { "DISABLE_PRECOMPILE_HEADERS"_s, IC::CanCompileSources }, + { "PCH_WARN_INVALID"_s, "ON"_s, IC::CanCompileSources }, + { "PCH_INSTANTIATE_TEMPLATES"_s, "ON"_s, IC::CanCompileSources }, + // -- Platforms + // ---- Android + { "ANDROID_API"_s, IC::CanCompileSources }, + { "ANDROID_API_MIN"_s, IC::CanCompileSources }, + { "ANDROID_ARCH"_s, IC::CanCompileSources }, + { "ANDROID_ASSETS_DIRECTORIES"_s, IC::CanCompileSources }, + { "ANDROID_JAVA_SOURCE_DIR"_s, IC::CanCompileSources }, + { "ANDROID_STL_TYPE"_s, IC::CanCompileSources }, + // ---- macOS + { "OSX_ARCHITECTURES"_s, IC::CanCompileSources }, + // ---- Windows + { "MSVC_DEBUG_INFORMATION_FORMAT"_s, IC::CanCompileSources }, + { "MSVC_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, + { "VS_JUST_MY_CODE_DEBUGGING"_s, IC::CanCompileSources }, + // ---- OpenWatcom + { "WATCOM_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, + // -- Language + // ---- C + COMMON_LANGUAGE_PROPERTIES(C), + // ---- C++ + COMMON_LANGUAGE_PROPERTIES(CXX), + // ---- CSharp + { "DOTNET_SDK"_s, IC::NonImportedTarget }, + { "DOTNET_TARGET_FRAMEWORK"_s, IC::TargetWithCommands }, + { "DOTNET_TARGET_FRAMEWORK_VERSION"_s, IC::TargetWithCommands }, + // ---- CUDA + COMMON_LANGUAGE_PROPERTIES(CUDA), + { "CUDA_SEPARABLE_COMPILATION"_s, IC::CanCompileSources }, + { "CUDA_ARCHITECTURES"_s, IC::CanCompileSources }, + // ---- Fortran + { "Fortran_FORMAT"_s, IC::CanCompileSources }, + { "Fortran_MODULE_DIRECTORY"_s, IC::CanCompileSources }, + { "Fortran_COMPILER_LAUNCHER"_s, IC::CanCompileSources }, + { "Fortran_PREPRPOCESS"_s, IC::CanCompileSources }, + { "Fortran_VISIBILITY_PRESET"_s, IC::CanCompileSources }, + // ---- HIP + COMMON_LANGUAGE_PROPERTIES(HIP), + { "HIP_ARCHITECTURES"_s, IC::CanCompileSources }, + // ---- ISPC + { "ISPC_COMPILER_LAUNCHER"_s, IC::CanCompileSources }, + { "ISPC_HEADER_DIRECTORY"_s, IC::CanCompileSources }, + { "ISPC_HEADER_SUFFIX"_s, "_ispc.h"_s, IC::CanCompileSources }, + { "ISPC_INSTRUCTION_SETS"_s, IC::CanCompileSources }, + // ---- Objective C + COMMON_LANGUAGE_PROPERTIES(OBJC), + // ---- Objective C++ + COMMON_LANGUAGE_PROPERTIES(OBJCXX), + // ---- Swift + { "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources }, + { "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources }, + // ---- moc + { "AUTOMOC"_s, IC::CanCompileSources }, + { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources }, + { "AUTOMOC_MACRO_NAMES"_s, IC::CanCompileSources }, + { "AUTOMOC_MOC_OPTIONS"_s, IC::CanCompileSources }, + { "AUTOMOC_PATH_PREFIX"_s, IC::CanCompileSources }, + // ---- uic + { "AUTOUIC"_s, IC::CanCompileSources }, + { "AUTOUIC_OPTIONS"_s, IC::CanCompileSources }, + { "AUTOUIC_SEARCH_PATHS"_s, IC::CanCompileSources }, + // ---- rcc + { "AUTORCC"_s, IC::CanCompileSources }, + { "AUTORCC_OPTIONS"_s, IC::CanCompileSources }, + + // Linking properties + { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports }, + { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget }, + { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources }, + { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources }, + // Initialize per-configuration name postfix property from the variable only + // for non-executable targets. This preserves compatibility with previous + // CMake versions in which executables did not support this variable. + // Projects may still specify the property directly. + { "_POSTFIX"_s, IC::NonExecutableWithArtifact, R::PerConfigPrefix }, + // -- Dependent library lookup + { "MACOSX_RPATH"_s, IC::CanCompileSources }, + // ---- Build + { "BUILD_RPATH"_s, IC::CanCompileSources }, + { "BUILD_RPATH_USE_ORIGIN"_s, IC::CanCompileSources }, + { "SKIP_BUILD_RPATH"_s, "OFF"_s, IC::CanCompileSources }, + { "BUILD_WITH_INSTALL_RPATH"_s, "OFF"_s, IC::CanCompileSources }, + { "BUILD_WITH_INSTALL_NAME_DIR"_s, IC::CanCompileSources }, + // ---- Install + { "INSTALL_NAME_DIR"_s, IC::CanCompileSources }, + { "INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, IC::CanCompileSources }, + { "INSTALL_RPATH"_s, ""_s, IC::CanCompileSources }, + { "INSTALL_RPATH_USE_LINK_PATH"_s, "OFF"_s, IC::CanCompileSources }, + // -- Platforms + // ---- AIX + { "AIX_EXPORT_ALL_SYMBOLS"_s, IC::TargetWithSymbolExports }, + // ---- Android + { "ANDROID_GUI"_s, IC::ExecutableTarget }, + { "ANDROID_JAR_DIRECTORIES"_s, IC::CanCompileSources }, + { "ANDROID_JAR_DEPENDENCIES"_s, IC::CanCompileSources }, + { "ANDROID_NATIVE_LIB_DIRECTORIES"_s, IC::CanCompileSources }, + { "ANDROID_NATIVE_LIB_DEPENDENCIES"_s, IC::CanCompileSources }, + { "ANDROID_PROGUARD"_s, IC::CanCompileSources }, + { "ANDROID_PROGUARD_CONFIG_PATH"_s, IC::CanCompileSources }, + { "ANDROID_SECURE_PROPS_PATH"_s, IC::CanCompileSources }, + // ---- iOS + { "IOS_INSTALL_COMBINED"_s, IC::CanCompileSources }, + // ---- macOS + { "FRAMEWORK_MULTI_CONFIG_POSTFIX_"_s, IC::LinkableLibraryTarget, R::PerConfig }, + // ---- Windows + { "DLL_NAME_WITH_SOVERSION"_s, IC::SharedLibraryTarget }, + { "GNUtoMS"_s, IC::CanCompileSources }, + { "WIN32_EXECUTABLE"_s, IC::CanCompileSources }, + { "WINDOWS_EXPORT_ALL_SYMBOLS"_s, IC::TargetWithSymbolExports }, + // -- Languages + // ---- C + { "C_LINKER_LAUNCHER"_s, IC::CanCompileSources }, + // ---- C++ + { "CXX_LINKER_LAUNCHER"_s, IC::CanCompileSources }, + // ---- CUDA + { "CUDA_RESOLVE_DEVICE_SYMBOLS"_s, IC::CanCompileSources }, + { "CUDA_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, + // ---- HIP + { "HIP_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, + // ---- Objective C + { "OBJC_LINKER_LAUNCHER"_s, IC::CanCompileSources }, + // ---- Objective C++ + { "OBJCXX_LINKER_LAUNCHER"_s, IC::CanCompileSources }, + + // Static analysis + // -- C + { "C_CLANG_TIDY"_s, IC::CanCompileSources }, + { "C_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources }, + { "C_CPPLINT"_s, IC::CanCompileSources }, + { "C_CPPCHECK"_s, IC::CanCompileSources }, + { "C_INCLUDE_WHAT_YOU_USE"_s, IC::CanCompileSources }, + // -- C++ + { "CXX_CLANG_TIDY"_s, IC::CanCompileSources }, + { "CXX_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources }, + { "CXX_CPPLINT"_s, IC::CanCompileSources }, + { "CXX_CPPCHECK"_s, IC::CanCompileSources }, + { "CXX_INCLUDE_WHAT_YOU_USE"_s, IC::CanCompileSources }, + // -- Objective C + { "OBJC_CLANG_TIDY"_s, IC::CanCompileSources }, + { "OBJC_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources }, + // -- Objective C++ + { "OBJCXX_CLANG_TIDY"_s, IC::CanCompileSources }, + { "OBJCXX_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources }, + // -- Linking + { "LINK_WHAT_YOU_USE"_s, IC::CanCompileSources }, + + // Build graph properties + { "LINK_DEPENDS_NO_SHARED"_s, IC::CanCompileSources }, + { "UNITY_BUILD"_s, IC::CanCompileSources }, + { "UNITY_BUILD_UNIQUE_ID"_s, IC::CanCompileSources }, + { "UNITY_BUILD_BATCH_SIZE"_s, "8"_s, IC::CanCompileSources }, + { "UNITY_BUILD_MODE"_s, "BATCH"_s, IC::CanCompileSources }, + { "OPTIMIZE_DEPENDENCIES"_s, IC::CanCompileSources }, + { "VERIFY_INTERFACE_HEADER_SETS"_s }, + // -- Android + { "ANDROID_ANT_ADDITIONAL_OPTIONS"_s, IC::CanCompileSources }, + { "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources }, + { "ANDROID_SKIP_ANT_STEP"_s, IC::CanCompileSources }, + // -- Autogen + { "AUTOGEN_ORIGIN_DEPENDS"_s, IC::CanCompileSources }, + { "AUTOGEN_PARALLEL"_s, IC::CanCompileSources }, + // -- moc + { "AUTOMOC_DEPEND_FILTERS"_s, IC::CanCompileSources }, + // -- C++ + { "CXX_SCAN_FOR_MODULES"_s, IC::CanCompileSources }, + // -- Ninja + { "JOB_POOL_COMPILE"_s, IC::CanCompileSources }, + { "JOB_POOL_LINK"_s, IC::CanCompileSources }, + { "JOB_POOL_PRECOMPILE_HEADER"_s, IC::CanCompileSources }, + // -- Visual Studio + { "VS_NO_COMPILE_BATCHING"_s, IC::CanCompileSources }, + + // Output location properties + { "ARCHIVE_OUTPUT_DIRECTORY"_s, IC::CanCompileSources }, + { "ARCHIVE_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig }, + { "COMPILE_PDB_OUTPUT_DIRECTORY"_s, IC::CanCompileSources }, + { "COMPILE_PDB_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig }, + { "LIBRARY_OUTPUT_DIRECTORY"_s, IC::CanCompileSources }, + { "LIBRARY_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig }, + { "PDB_OUTPUT_DIRECTORY"_s, IC::CanCompileSources }, + { "PDB_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig }, + { "RUNTIME_OUTPUT_DIRECTORY"_s, IC::CanCompileSources }, + { "RUNTIME_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig }, + + // macOS bundle properties + { "FRAMEWORK"_s, IC::CanCompileSources }, + { "FRAMEWORK_MULTI_CONFIG_POSTFIX"_s, IC::CanCompileSources }, + { "MACOSX_BUNDLE"_s, IC::CanCompileSources }, + + // Usage requirement properties + { "LINK_INTERFACE_LIBRARIES"_s, IC::CanCompileSources }, + { "MAP_IMPORTED_CONFIG_"_s, IC::NormalTarget, R::PerConfig }, + + // Metadata + { "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget }, + { "EXPORT_COMPILE_COMMANDS"_s, IC::CanCompileSources }, + { "FOLDER"_s }, + + // Xcode properties + { "XCODE_GENERATE_SCHEME"_s, IC::NeedsXcode }, + +#ifdef __APPLE__ + { "XCODE_SCHEME_ADDRESS_SANITIZER"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_THREAD_SANITIZER"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_THREAD_SANITIZER_STOP"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_LAUNCH_CONFIGURATION"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_WORKING_DIRECTORY"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_MALLOC_SCRIBBLE"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_MALLOC_GUARD_EDGES"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_GUARD_MALLOC"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_LAUNCH_MODE"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_ZOMBIE_OBJECTS"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_MALLOC_STACK"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_SCHEME_ENVIRONMENT"_s, IC::NeedsXcodeAndCanCompileSources }, + { "XCODE_LINK_BUILD_PHASE_MODE"_s, "NONE"_s, IC::NeedsXcodeAndCanCompileSources }, +#endif + /* clang-format on */ +}; + +#undef COMMON_LANGUAGE_PROPERTIES +#undef IC +#undef R } class cmTargetInternals @@ -289,11 +630,11 @@ public: bool HaveInstallRule; bool IsDLLPlatform; bool IsAIX; + bool IsApple; bool IsAndroid; - bool IsImportedTarget; - bool ImportedGloballyVisible; bool BuildInterfaceIncludesAppended; bool PerConfig; + cmTarget::Visibility TargetVisibility; std::set<BT<std::pair<std::string, bool>>> Utilities; std::vector<cmCustomCommand> PreBuildCommands; std::vector<cmCustomCommand> PreLinkCommands; @@ -328,6 +669,8 @@ public: cmTargetInternals(); + bool IsImported() const; + bool CheckImportedLibName(std::string const& prop, std::string const& value) const; @@ -562,15 +905,6 @@ std::pair<bool, cmValue> UsageRequirementProperty::Read( return { did_read, value }; } -namespace { -#define SETUP_COMMON_LANGUAGE_PROPERTIES(lang) \ - initProp(#lang "_COMPILER_LAUNCHER"); \ - initProp(#lang "_STANDARD"); \ - initProp(#lang "_STANDARD_REQUIRED"); \ - initProp(#lang "_EXTENSIONS"); \ - initProp(#lang "_VISIBILITY_PRESET") -} - cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, Visibility vis, cmMakefile* mf, PerConfig perConfig) : impl(cm::make_unique<cmTargetInternals>()) @@ -583,10 +917,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->impl->HaveInstallRule = false; this->impl->IsDLLPlatform = false; this->impl->IsAIX = false; + this->impl->IsApple = false; this->impl->IsAndroid = false; - this->impl->IsImportedTarget = - (vis == VisibilityImported || vis == VisibilityImportedGlobally); - this->impl->ImportedGloballyVisible = vis == VisibilityImportedGlobally; + this->impl->TargetVisibility = vis; this->impl->BuildInterfaceIncludesAppended = false; this->impl->PerConfig = (perConfig == PerConfig::Yes); @@ -602,306 +935,17 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->impl->IsAIX = (systemName == "AIX" || systemName == "OS400"); } + // Check whether we are targeting Apple. + this->impl->IsApple = this->impl->Makefile->IsOn("APPLE"); + // Check whether we are targeting an Android platform. this->impl->IsAndroid = (this->impl->Makefile->GetSafeDefinition( "CMAKE_SYSTEM_NAME") == "Android"); - std::string defKey; - defKey.reserve(128); - defKey += "CMAKE_"; - auto initProp = [this, mf, &defKey](const std::string& property) { - // Replace everything after "CMAKE_" - defKey.replace(defKey.begin() + 6, defKey.end(), property); - if (cmValue value = mf->GetDefinition(defKey)) { - this->SetProperty(property, value); - } - }; - auto initPropValue = [this, mf, &defKey](const std::string& property, - const char* default_value) { - // Replace everything after "CMAKE_" - defKey.replace(defKey.begin() + 6, defKey.end(), property); - if (cmValue value = mf->GetDefinition(defKey)) { - this->SetProperty(property, value); - } else if (default_value) { - this->SetProperty(property, default_value); - } - }; - - // Setup default property values. - if (this->CanCompileSources()) { - - // Compilation properties - initProp("INTERPROCEDURAL_OPTIMIZATION"); - // initProp("INTERPROCEDURAL_OPTIMIZATION_<CONFIG>"); (per-config block) - initProp("NO_SYSTEM_FROM_IMPORTED"); - initProp("VISIBILITY_INLINES_HIDDEN"); - initProp("COMPILE_WARNING_AS_ERROR"); - // -- Features - // ---- PCH - initProp("DISABLE_PRECOMPILE_HEADERS"); - initPropValue("PCH_WARN_INVALID", "ON"); - initPropValue("PCH_INSTANTIATE_TEMPLATES", "ON"); - // -- Platforms - // ---- Android - initProp("ANDROID_API"); - initProp("ANDROID_API_MIN"); - initProp("ANDROID_ARCH"); - initProp("ANDROID_ASSETS_DIRECTORIES"); - initProp("ANDROID_JAVA_SOURCE_DIR"); - initProp("ANDROID_STL_TYPE"); - // ---- macOS - initProp("OSX_ARCHITECTURES"); - // ---- Windows - initProp("MSVC_DEBUG_INFORMATION_FORMAT"); - initProp("MSVC_RUNTIME_LIBRARY"); - initProp("VS_JUST_MY_CODE_DEBUGGING"); - // ---- OpenWatcom - initProp("WATCOM_RUNTIME_LIBRARY"); - // -- Language - // ---- C - SETUP_COMMON_LANGUAGE_PROPERTIES(C); - // ---- C++ - SETUP_COMMON_LANGUAGE_PROPERTIES(CXX); - // ---- CUDA - SETUP_COMMON_LANGUAGE_PROPERTIES(CUDA); - initProp("CUDA_SEPARABLE_COMPILATION"); - initProp("CUDA_ARCHITECTURES"); - // ---- Fortran - initProp("Fortran_FORMAT"); - initProp("Fortran_MODULE_DIRECTORY"); - initProp("Fortran_COMPILER_LAUNCHER"); - initProp("Fortran_PREPROCESS"); - initProp("Fortran_VISIBILITY_PRESET"); - // ---- HIP - SETUP_COMMON_LANGUAGE_PROPERTIES(HIP); - initProp("HIP_ARCHITECTURES"); - // ---- ISPC - initProp("ISPC_COMPILER_LAUNCHER"); - initProp("ISPC_HEADER_DIRECTORY"); - initPropValue("ISPC_HEADER_SUFFIX", "_ispc.h"); - initProp("ISPC_INSTRUCTION_SETS"); - // ---- Objective C - SETUP_COMMON_LANGUAGE_PROPERTIES(OBJC); - // ---- Objective C++ - SETUP_COMMON_LANGUAGE_PROPERTIES(OBJCXX); - // ---- Swift - initProp("Swift_LANGUAGE_VERSION"); - initProp("Swift_MODULE_DIRECTORY"); - // ---- moc - initProp("AUTOMOC"); - initProp("AUTOMOC_COMPILER_PREDEFINES"); - initProp("AUTOMOC_MACRO_NAMES"); - initProp("AUTOMOC_MOC_OPTIONS"); - initProp("AUTOMOC_PATH_PREFIX"); - // ---- uic - initProp("AUTOUIC"); - initProp("AUTOUIC_OPTIONS"); - initProp("AUTOUIC_SEARCH_PATHS"); - // ---- rcc - initProp("AUTORCC"); - initProp("AUTORCC_OPTIONS"); - - // Linking properties - initProp("LINK_SEARCH_START_STATIC"); - initProp("LINK_SEARCH_END_STATIC"); - // -- Dependent library lookup - initProp("MACOSX_RPATH"); - // ---- Build - initProp("BUILD_RPATH"); - initProp("BUILD_RPATH_USE_ORIGIN"); - initPropValue("SKIP_BUILD_RPATH", "OFF"); - initPropValue("BUILD_WITH_INSTALL_RPATH", "OFF"); - initProp("BUILD_WITH_INSTALL_NAME_DIR"); - // ---- Install - initProp("INSTALL_NAME_DIR"); - initProp("INSTALL_REMOVE_ENVIRONMENT_RPATH"); - initPropValue("INSTALL_RPATH", ""); - initPropValue("INSTALL_RPATH_USE_LINK_PATH", "OFF"); - // -- Platforms - // ---- Android - initProp("ANDROID_JAR_DIRECTORIES"); - initProp("ANDROID_JAR_DEPENDENCIES"); - initProp("ANDROID_NATIVE_LIB_DIRECTORIES"); - initProp("ANDROID_NATIVE_LIB_DEPENDENCIES"); - initProp("ANDROID_PROGUARD"); - initProp("ANDROID_PROGUARD_CONFIG_PATH"); - initProp("ANDROID_SECURE_PROPS_PATH"); - // ---- iOS - initProp("IOS_INSTALL_COMBINED"); - // ---- Windows - initProp("GNUtoMS"); - initProp("WIN32_EXECUTABLE"); - // -- Languages - // ---- C - initProp("C_LINKER_LAUNCHER"); - // ---- C++ - initProp("CXX_LINKER_LAUNCHER"); - // ---- CUDA - initProp("CUDA_RESOLVE_DEVICE_SYMBOLS"); - initProp("CUDA_RUNTIME_LIBRARY"); - // ---- HIP - initProp("HIP_RUNTIME_LIBRARY"); - // ---- Objective C - initProp("OBJC_LINKER_LAUNCHER"); - // ---- Objective C++ - initProp("OBJCXX_LINKER_LAUNCHER"); - - // Static analysis - // -- C - initProp("C_CLANG_TIDY"); - initProp("C_CLANG_TIDY_EXPORT_FIXES_DIR"); - initProp("C_CPPLINT"); - initProp("C_CPPCHECK"); - initProp("C_INCLUDE_WHAT_YOU_USE"); - // -- C++ - initProp("CXX_CLANG_TIDY"); - initProp("CXX_CLANG_TIDY_EXPORT_FIXES_DIR"); - initProp("CXX_CPPLINT"); - initProp("CXX_CPPCHECK"); - initProp("CXX_INCLUDE_WHAT_YOU_USE"); - // -- Objective C - initProp("OBJC_CLANG_TIDY"); - initProp("OBJC_CLANG_TIDY_EXPORT_FIXES_DIR"); - // -- Objective C++ - initProp("OBJCXX_CLANG_TIDY"); - initProp("OBJCXX_CLANG_TIDY_EXPORT_FIXES_DIR"); - // -- Linking - initProp("LINK_WHAT_YOU_USE"); - - // Build graph properties - initProp("LINK_DEPENDS_NO_SHARED"); - initProp("UNITY_BUILD"); - initProp("UNITY_BUILD_UNIQUE_ID"); - initPropValue("UNITY_BUILD_BATCH_SIZE", "8"); - initPropValue("UNITY_BUILD_MODE", "BATCH"); - initProp("OPTIMIZE_DEPENDENCIES"); - // -- Android - initProp("ANDROID_ANT_ADDITIONAL_OPTIONS"); - initProp("ANDROID_PROCESS_MAX"); - initProp("ANDROID_SKIP_ANT_STEP"); - // -- Autogen - initProp("AUTOGEN_ORIGIN_DEPENDS"); - initProp("AUTOGEN_PARALLEL"); - // -- moc - initProp("AUTOMOC_DEPEND_FILTERS"); - // -- C++ - initProp("CXX_SCAN_FOR_MODULES"); - // -- Ninja - initProp("JOB_POOL_COMPILE"); - initProp("JOB_POOL_LINK"); - initProp("JOB_POOL_PRECOMPILE_HEADER"); - // -- Visual Studio - initProp("VS_NO_COMPILE_BATCHING"); - - // Output location properties - initProp("ARCHIVE_OUTPUT_DIRECTORY"); - initProp("LIBRARY_OUTPUT_DIRECTORY"); - initProp("RUNTIME_OUTPUT_DIRECTORY"); - initProp("PDB_OUTPUT_DIRECTORY"); - initProp("COMPILE_PDB_OUTPUT_DIRECTORY"); - - // -- macOS bundle properties - initProp("FRAMEWORK"); - initProp("FRAMEWORK_MULTI_CONFIG_POSTFIX"); - initProp("MACOSX_BUNDLE"); - - // Usage requirement properties - initProp("LINK_INTERFACE_LIBRARIES"); - - // Metadata - initProp("EXPORT_COMPILE_COMMANDS"); - -#ifdef __APPLE__ - if (this->GetGlobalGenerator()->IsXcode()) { - initProp("XCODE_SCHEME_ADDRESS_SANITIZER"); - initProp("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN"); - initProp("XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING"); - initProp("XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE"); - initProp("XCODE_SCHEME_THREAD_SANITIZER"); - initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP"); - initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"); - initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"); - initProp("XCODE_SCHEME_LAUNCH_CONFIGURATION"); - initProp("XCODE_SCHEME_ENABLE_GPU_API_VALIDATION"); - initProp("XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION"); - initProp("XCODE_SCHEME_WORKING_DIRECTORY"); - initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); - initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"); - initProp("XCODE_SCHEME_MALLOC_SCRIBBLE"); - initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES"); - initProp("XCODE_SCHEME_GUARD_MALLOC"); - initProp("XCODE_SCHEME_LAUNCH_MODE"); - initProp("XCODE_SCHEME_ZOMBIE_OBJECTS"); - initProp("XCODE_SCHEME_MALLOC_STACK"); - initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); - initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"); - initProp("XCODE_SCHEME_ENVIRONMENT"); - initPropValue("XCODE_LINK_BUILD_PHASE_MODE", "NONE"); - } -#endif - } - - initProp("FOLDER"); - initProp("VERIFY_INTERFACE_HEADER_SETS"); - - if (this->GetGlobalGenerator()->IsXcode()) { - initProp("XCODE_GENERATE_SCHEME"); - } - - // Setup per-configuration property default values. - if (this->GetType() != cmStateEnums::UTILITY && - this->GetType() != cmStateEnums::GLOBAL_TARGET) { - static const auto configProps = { - /* clang-format needs this comment to break after the opening brace */ - "ARCHIVE_OUTPUT_DIRECTORY_"_s, "LIBRARY_OUTPUT_DIRECTORY_"_s, - "RUNTIME_OUTPUT_DIRECTORY_"_s, "PDB_OUTPUT_DIRECTORY_"_s, - "COMPILE_PDB_OUTPUT_DIRECTORY_"_s, "MAP_IMPORTED_CONFIG_"_s, - "INTERPROCEDURAL_OPTIMIZATION_"_s - }; - // Collect the set of configuration types. - std::vector<std::string> configNames = - mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); - for (std::string const& configName : configNames) { - std::string configUpper = cmSystemTools::UpperCase(configName); - for (auto const& prop : configProps) { - // Interface libraries have no output locations, so honor only - // the configuration map. - if (this->impl->TargetType == cmStateEnums::INTERFACE_LIBRARY && - prop != "MAP_IMPORTED_CONFIG_") { - continue; - } - std::string property = cmStrCat(prop, configUpper); - initProp(property); - } - - // Initialize per-configuration name postfix property from the - // variable only for non-executable targets. This preserves - // compatibility with previous CMake versions in which executables - // did not support this variable. Projects may still specify the - // property directly. - if (this->impl->TargetType != cmStateEnums::EXECUTABLE && - this->impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) { - std::string property = - cmStrCat(cmSystemTools::UpperCase(configName), "_POSTFIX"); - initProp(property); - } - - if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY || - this->impl->TargetType == cmStateEnums::STATIC_LIBRARY) { - std::string property = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_", - cmSystemTools::UpperCase(configName)); - initProp(property); - } - } - if (!this->IsImported()) { - initProp("LINK_LIBRARIES_ONLY_TARGETS"); - } - } - // Save the backtrace of target construction. this->impl->Backtrace = this->impl->Makefile->GetBacktrace(); - if (!this->IsImported()) { + if (this->IsNormal()) { // Initialize the INCLUDE_DIRECTORIES property based on the current value // of the same directory property: this->impl->IncludeDirectories.CopyFromDirectory( @@ -921,23 +965,6 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->impl->Makefile->GetLinkDirectoriesEntries()); } - if (this->impl->TargetType == cmStateEnums::EXECUTABLE) { - initProp("ANDROID_GUI"); - initProp("CROSSCOMPILING_EMULATOR"); - initProp("ENABLE_EXPORTS"); - } - if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY || - this->impl->TargetType == cmStateEnums::MODULE_LIBRARY) { - this->SetProperty("POSITION_INDEPENDENT_CODE", "True"); - } else if (this->CanCompileSources()) { - initProp("POSITION_INDEPENDENT_CODE"); - } - if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY || - this->impl->TargetType == cmStateEnums::EXECUTABLE) { - initProp("AIX_EXPORT_ALL_SYMBOLS"); - initProp("WINDOWS_EXPORT_ALL_SYMBOLS"); - } - // Record current policies for later use. this->impl->Makefile->RecordPolicies(this->impl->PolicyMap); @@ -949,13 +976,133 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->impl->PolicyMap.Set(cmPolicies::CMP0022, cmPolicies::NEW); } + std::set<TargetProperty::InitCondition> metConditions; + metConditions.insert(TargetProperty::InitCondition::Always); + if (this->CanCompileSources()) { + metConditions.insert(TargetProperty::InitCondition::CanCompileSources); + } + if (this->GetGlobalGenerator()->IsXcode()) { + metConditions.insert(TargetProperty::InitCondition::NeedsXcode); + if (this->CanCompileSources()) { + metConditions.insert( + TargetProperty::InitCondition::NeedsXcodeAndCanCompileSources); + } + } if (!this->IsImported()) { - initProp("DOTNET_SDK"); + metConditions.insert(TargetProperty::InitCondition::NonImportedTarget); + } + if (this->impl->TargetType != cmStateEnums::UTILITY && + this->impl->TargetType != cmStateEnums::GLOBAL_TARGET) { + metConditions.insert(TargetProperty::InitCondition::NormalTarget); + if (this->IsNormal()) { + metConditions.insert( + TargetProperty::InitCondition::NormalNonImportedTarget); + } + if (this->impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) { + metConditions.insert(TargetProperty::InitCondition::TargetWithArtifact); + if (this->impl->TargetType != cmStateEnums::EXECUTABLE) { + metConditions.insert( + TargetProperty::InitCondition::NonExecutableWithArtifact); + } + } + if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY || + this->impl->TargetType == cmStateEnums::STATIC_LIBRARY) { + metConditions.insert( + TargetProperty::InitCondition::LinkableLibraryTarget); + } + if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY) { + metConditions.insert(TargetProperty::InitCondition::SharedLibraryTarget); + } + } + if (this->impl->TargetType == cmStateEnums::EXECUTABLE) { + metConditions.insert(TargetProperty::InitCondition::ExecutableTarget); + } + if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY || + this->impl->TargetType == cmStateEnums::EXECUTABLE) { + metConditions.insert( + TargetProperty::InitCondition::TargetWithSymbolExports); } - if (this->impl->TargetType <= cmStateEnums::GLOBAL_TARGET) { - initProp("DOTNET_TARGET_FRAMEWORK"); - initProp("DOTNET_TARGET_FRAMEWORK_VERSION"); + metConditions.insert(TargetProperty::InitCondition::TargetWithCommands); + } + + std::vector<std::string> configNames = + mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (auto& config : configNames) { + config = cmSystemTools::UpperCase(config); + } + + std::string defKey; + defKey.reserve(128); + defKey += "CMAKE_"; + auto initProperty = [this, mf, &defKey](const std::string& property, + const char* default_value) { + // special init for ENABLE_EXPORTS + // For SHARED_LIBRARY, only CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS variable + // is used + // For EXECUTABLE, CMAKE_EXECUTABLE_ENABLE_EXPORTS or else + // CMAKE_ENABLE_EXPORTS variables are used + if (property == "ENABLE_EXPORTS"_s) { + // Replace everything after "CMAKE_" + defKey.replace( + defKey.begin() + 6, defKey.end(), + cmStrCat(this->impl->TargetType == cmStateEnums::EXECUTABLE + ? "EXECUTABLE" + : "SHARED_LIBRARY", + '_', property)); + if (cmValue value = mf->GetDefinition(defKey)) { + this->SetProperty(property, value); + return; + } + if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY) { + if (default_value) { + this->SetProperty(property, default_value); + } + return; + } + } + + // Replace everything after "CMAKE_" + defKey.replace(defKey.begin() + 6, defKey.end(), property); + if (cmValue value = mf->GetDefinition(defKey)) { + this->SetProperty(property, value); + } else if (default_value) { + this->SetProperty(property, default_value); + } + }; + + std::string dflt_storage; + for (auto const& tp : StaticTargetProperties) { + // Ignore properties that we have not met the condition for. + if (!metConditions.count(tp.InitConditional)) { + continue; + } + + const char* dflt = nullptr; + if (tp.Default) { + dflt_storage = std::string(*tp.Default); + dflt = dflt_storage.c_str(); + } + + if (tp.Repeat == TargetProperty::Repetition::Once) { + initProperty(std::string(tp.Name), dflt); + } else { + std::string propertyName; + for (auto const& configName : configNames) { + if (tp.Repeat == TargetProperty::Repetition::PerConfig) { + propertyName = cmStrCat(tp.Name, configName); + } else if (tp.Repeat == TargetProperty::Repetition::PerConfigPrefix) { + propertyName = cmStrCat(configName, tp.Name); + } + initProperty(propertyName, dflt); + } + } + } + + // Clean up some property defaults. + if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY || + this->impl->TargetType == cmStateEnums::MODULE_LIBRARY) { + this->SetProperty("POSITION_INDEPENDENT_CODE", "True"); } // check for "CMAKE_VS_GLOBALS" variable and set up target properties @@ -972,13 +1119,13 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (assignment != std::string::npos) { const std::string propName = vsGlobal + i.substr(0, assignment); const std::string propValue = i.substr(assignment + 1); - initPropValue(propName, propValue.c_str()); + initProperty(propName, propValue.c_str()); } } } } - if (this->IsImported() || mf->GetPropertyAsBool("SYSTEM")) { + if (!this->IsNormal() || mf->GetPropertyAsBool("SYSTEM")) { this->SetProperty("SYSTEM", "ON"); } @@ -1089,18 +1236,22 @@ bool cmTarget::IsExecutableWithExports() const this->GetPropertyAsBool("ENABLE_EXPORTS")); } +bool cmTarget::IsSharedLibraryWithExports() const +{ + return (this->GetType() == cmStateEnums::SHARED_LIBRARY && + this->GetPropertyAsBool("ENABLE_EXPORTS")); +} + bool cmTarget::IsFrameworkOnApple() const { return ((this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::STATIC_LIBRARY) && - this->impl->Makefile->IsOn("APPLE") && - this->GetPropertyAsBool("FRAMEWORK")); + this->IsApple() && this->GetPropertyAsBool("FRAMEWORK")); } bool cmTarget::IsAppBundleOnApple() const { - return (this->GetType() == cmStateEnums::EXECUTABLE && - this->impl->Makefile->IsOn("APPLE") && + return (this->GetType() == cmStateEnums::EXECUTABLE && this->IsApple() && this->GetPropertyAsBool("MACOSX_BUNDLE")); } @@ -1662,7 +1813,6 @@ std::string ConvertToString<cmValue>(cmValue value) { return std::string(*value); } - } template <typename ValueType> @@ -1750,8 +1900,8 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value) return; } /* no need to change anything if value does not change */ - if (!this->impl->ImportedGloballyVisible) { - this->impl->ImportedGloballyVisible = true; + if (!this->IsImportedGloballyVisible()) { + this->impl->TargetVisibility = Visibility::ImportedGlobally; this->GetGlobalGenerator()->IndexTarget(this); } } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME") && @@ -2441,15 +2591,70 @@ bool cmTarget::IsAIX() const { return this->impl->IsAIX; } +bool cmTarget::IsApple() const +{ + return this->impl->IsApple; +} + +bool cmTarget::IsNormal() const +{ + switch (this->impl->TargetVisibility) { + case Visibility::Normal: + return true; + case Visibility::Generated: + case Visibility::Imported: + case Visibility::ImportedGlobally: + return false; + } + assert(false && "unknown visibility (IsNormal)"); + return false; +} + +bool cmTarget::IsSynthetic() const +{ + switch (this->impl->TargetVisibility) { + case Visibility::Generated: + return true; + case Visibility::Normal: + case Visibility::Imported: + case Visibility::ImportedGlobally: + return false; + } + assert(false && "unknown visibility (IsSynthetic)"); + return false; +} + +bool cmTargetInternals::IsImported() const +{ + switch (this->TargetVisibility) { + case cmTarget::Visibility::Imported: + case cmTarget::Visibility::ImportedGlobally: + return true; + case cmTarget::Visibility::Normal: + case cmTarget::Visibility::Generated: + return false; + } + assert(false && "unknown visibility (IsImported)"); + return false; +} bool cmTarget::IsImported() const { - return this->impl->IsImportedTarget; + return this->impl->IsImported(); } bool cmTarget::IsImportedGloballyVisible() const { - return this->impl->ImportedGloballyVisible; + switch (this->impl->TargetVisibility) { + case Visibility::ImportedGlobally: + return true; + case Visibility::Normal: + case Visibility::Generated: + case Visibility::Imported: + return false; + } + assert(false && "unknown visibility (IsImportedGloballyVisible)"); + return false; } bool cmTarget::IsPerConfig() const @@ -2489,7 +2694,8 @@ const char* cmTarget::GetSuffixVariableInternal( case cmStateEnums::RuntimeBinaryArtifact: return "CMAKE_SHARED_LIBRARY_SUFFIX"; case cmStateEnums::ImportLibraryArtifact: - return "CMAKE_IMPORT_LIBRARY_SUFFIX"; + return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_SUFFIX" + : "CMAKE_IMPORT_LIBRARY_SUFFIX"; } break; case cmStateEnums::MODULE_LIBRARY: @@ -2530,7 +2736,8 @@ const char* cmTarget::GetPrefixVariableInternal( case cmStateEnums::RuntimeBinaryArtifact: return "CMAKE_SHARED_LIBRARY_PREFIX"; case cmStateEnums::ImportLibraryArtifact: - return "CMAKE_IMPORT_LIBRARY_PREFIX"; + return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_PREFIX" + : "CMAKE_IMPORT_LIBRARY_PREFIX"; } break; case cmStateEnums::MODULE_LIBRARY: @@ -2745,7 +2952,7 @@ bool cmTargetInternals::CheckImportedLibName(std::string const& prop, std::string const& value) const { if (this->TargetType != cmStateEnums::INTERFACE_LIBRARY || - !this->IsImportedTarget) { + !this->IsImported()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, prop + @@ -2805,7 +3012,9 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config, cmValue& loc, bool allowImp = (this->IsDLLPlatform() && (this->GetType() == cmStateEnums::SHARED_LIBRARY || this->IsExecutableWithExports())) || - (this->IsAIX() && this->IsExecutableWithExports()); + (this->IsAIX() && this->IsExecutableWithExports()) || + (this->GetMakefile()->PlatformSupportsAppleTextStubs() && + this->IsSharedLibraryWithExports()); // If a mapping was found, check its configurations. for (auto mci = mappedConfigs.begin(); diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 38bd036..24f6fcd 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -46,11 +46,12 @@ class BTs; class cmTarget { public: - enum Visibility + enum class Visibility { - VisibilityNormal, - VisibilityImported, - VisibilityImportedGlobally + Normal, + Generated, + Imported, + ImportedGlobally, }; enum class PerConfig @@ -204,7 +205,11 @@ public: //! Return whether or not we are targeting AIX. bool IsAIX() const; + //! Return whether or not we are targeting Apple. + bool IsApple() const; + bool IsNormal() const; + bool IsSynthetic() const; bool IsImported() const; bool IsImportedGloballyVisible() const; bool IsPerConfig() const; @@ -216,6 +221,10 @@ public: //! Return whether this target is an executable with symbol exports enabled. bool IsExecutableWithExports() const; + //! Return whether this target is a shared library with symbol exports + //! enabled. + bool IsSharedLibraryWithExports() const; + //! Return whether this target is a shared library Framework on Apple. bool IsFrameworkOnApple() const; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 52de6ff..d9d3fff 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -955,7 +955,11 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( "executables.")); return; } - outputType = "Exe"; + if (cmIsOn(win32)) { + outputType = "WinExe"; + } else { + outputType = "Exe"; + } } break; case cmStateEnums::UTILITY: case cmStateEnums::INTERFACE_LIBRARY: @@ -3105,6 +3109,17 @@ std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes( return includes; } +std::string cmVisualStudio10TargetGenerator::GetTargetOutputName() const +{ + std::string config; + if (!this->Configurations.empty()) { + config = this->Configurations[0]; + } + const auto& nameComponents = + this->GeneratorTarget->GetFullNameComponents(config); + return nameComponents.prefix + nameComponents.base; +} + bool cmVisualStudio10TargetGenerator::ComputeClOptions() { return std::all_of( @@ -5054,8 +5069,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80(Elem& e1) this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); ConvertToWindowsSlash(artifactDir); std::string artifactDirXML = cmVS10EscapeXML(artifactDir); - std::string targetNameXML = - cmVS10EscapeXML(this->GeneratorTarget->GetName()); + const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName()); cmGeneratedFileStream fout(manifestFile); fout.SetCopyIfDifferent(true); @@ -5137,8 +5151,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP81(Elem& e1) this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); ConvertToWindowsSlash(artifactDir); std::string artifactDirXML = cmVS10EscapeXML(artifactDir); - std::string targetNameXML = - cmVS10EscapeXML(this->GeneratorTarget->GetName()); + const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName()); cmGeneratedFileStream fout(manifestFile); fout.SetCopyIfDifferent(true); @@ -5200,8 +5213,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS80(Elem& e1) this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); ConvertToWindowsSlash(artifactDir); std::string artifactDirXML = cmVS10EscapeXML(artifactDir); - std::string targetNameXML = - cmVS10EscapeXML(this->GeneratorTarget->GetName()); + const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName()); cmGeneratedFileStream fout(manifestFile); fout.SetCopyIfDifferent(true); @@ -5255,8 +5267,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS81(Elem& e1) this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); ConvertToWindowsSlash(artifactDir); std::string artifactDirXML = cmVS10EscapeXML(artifactDir); - std::string targetNameXML = - cmVS10EscapeXML(this->GeneratorTarget->GetName()); + const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName()); cmGeneratedFileStream fout(manifestFile); fout.SetCopyIfDifferent(true); @@ -5315,8 +5326,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS10_0(Elem& e1) this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); ConvertToWindowsSlash(artifactDir); std::string artifactDirXML = cmVS10EscapeXML(artifactDir); - std::string targetNameXML = - cmVS10EscapeXML(this->GeneratorTarget->GetName()); + const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName()); cmGeneratedFileStream fout(manifestFile); fout.SetCopyIfDifferent(true); diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index e00f692..97ae69f 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -117,6 +117,7 @@ private: std::vector<std::string> GetIncludes(std::string const& config, std::string const& lang) const; + std::string GetTargetOutputName() const; bool ComputeClOptions(); bool ComputeClOptions(std::string const& configName); diff --git a/Source/cm_codecvt.cxx b/Source/cm_codecvt.cxx index 2d2a377..12877b8 100644 --- a/Source/cm_codecvt.cxx +++ b/Source/cm_codecvt.cxx @@ -171,7 +171,7 @@ std::codecvt_base::result codecvt::Decode(mbstate_t& state, int size, } int tlen = WideCharToMultiByte(m_codepage, 0, wbuf, wlen, to_next, - to_end - to_next, NULL, NULL); + to_end - to_next, nullptr, nullptr); if (tlen <= 0) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { return std::codecvt_base::partial; @@ -206,7 +206,7 @@ std::codecvt_base::result codecvt::DecodePartial(mbstate_t& state, } int tlen = WideCharToMultiByte(m_codepage, 0, wbuf, wlen, to_next, - to_end - to_next, NULL, NULL); + to_end - to_next, nullptr, nullptr); if (tlen <= 0) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { return std::codecvt_base::partial; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 468ff73..dbf961d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -964,7 +964,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) return true; }; - auto ToolsetLamda = [&](std::string const& value, cmake* state) -> bool { + auto ToolsetLambda = [&](std::string const& value, cmake* state) -> bool { if (haveToolset) { cmSystemTools::Error("Multiple -T options not allowed"); return false; @@ -1016,7 +1016,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::RequiresSeparator::No, PlatformLambda }, CommandArgument{ "-T", "No toolset specified for -T", CommandArgument::Values::One, - CommandArgument::RequiresSeparator::No, ToolsetLamda }, + CommandArgument::RequiresSeparator::No, ToolsetLambda }, CommandArgument{ "--toolchain", "No file specified for --toolchain", CommandArgument::Values::One, IgnoreAndTrueLambda }, CommandArgument{ "--install-prefix", diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 21d0cc9..a2a9e09 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -50,10 +50,10 @@ #endif #include <array> +#include <chrono> #include <cstdio> #include <cstdlib> #include <cstring> -#include <ctime> #include <iostream> #include <memory> #include <sstream> @@ -1104,27 +1104,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, if (args[1] == "time" && args.size() > 2) { std::vector<std::string> command(args.begin() + 2, args.end()); - clock_t clock_start; - clock_t clock_finish; - time_t time_start; - time_t time_finish; - - time(&time_start); - clock_start = clock(); int ret = 0; + auto time_start = std::chrono::steady_clock::now(); cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret); + auto time_finish = std::chrono::steady_clock::now(); - clock_finish = clock(); - time(&time_finish); - - double clocks_per_sec = static_cast<double>(CLOCKS_PER_SEC); - std::cout << "Elapsed time: " - << static_cast<long>(time_finish - time_start) << " s. (time)" - << ", " - << static_cast<double>(clock_finish - clock_start) / - clocks_per_sec - << " s. (clock)" - << "\n"; + std::chrono::duration<double> time_elapsed = time_finish - time_start; + std::cout << "Elapsed time (seconds): " << time_elapsed.count() << "\n"; return ret; } @@ -2343,6 +2329,9 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 || cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) { this->Incremental = true; + } else if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:NO") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:NO") == 0) { + this->Incremental = false; } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 || cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) { this->LinkGeneratesManifest = false; @@ -2367,17 +2356,11 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, // pass it to the link command. this->ManifestFileRC = intDir + "/manifest.rc"; this->ManifestFileRes = intDir + "/manifest.res"; - } else if (this->UserManifests.empty()) { - // Prior to support for user-specified manifests CMake placed the - // linker-generated manifest next to the binary (as if it were not to be - // embedded) when not linking incrementally. Preserve this behavior. - this->ManifestFile = this->TargetFile + ".manifest"; - this->LinkerManifestFile = this->ManifestFile; - } - if (this->LinkGeneratesManifest) { - this->LinkCommand.emplace_back("/MANIFEST"); - this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile); + if (this->LinkGeneratesManifest) { + this->LinkCommand.emplace_back("/MANIFEST"); + this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile); + } } return true; @@ -2511,20 +2494,23 @@ int cmVSLink::LinkIncremental() int cmVSLink::LinkNonIncremental() { - // Run the link command (possibly generates intermediate manifest). - if (!RunCommand("LINK", this->LinkCommand, this->Verbose, FORMAT_DECIMAL)) { - return -1; - } + // Sort out any manifests. + if (this->LinkGeneratesManifest || !this->UserManifests.empty()) { + std::string opt = + std::string("/MANIFEST:EMBED,ID=") + (this->Type == 1 ? '1' : '2'); + this->LinkCommand.emplace_back(opt); - // If we have no manifest files we are done. - if (!this->LinkGeneratesManifest && this->UserManifests.empty()) { - return 0; + for (auto const& m : this->UserManifests) { + opt = "/MANIFESTINPUT:" + m; + this->LinkCommand.emplace_back(opt); + } } - // Run the manifest tool to embed the final manifest in the binary. - std::string mtOut = - "/outputresource:" + this->TargetFile + (this->Type == 1 ? ";#1" : ";#2"); - return this->RunMT(mtOut, false); + // Run the link command. + if (!RunCommand("LINK", this->LinkCommand, this->Verbose, FORMAT_DECIMAL)) { + return -1; + } + return 0; } int cmVSLink::RunMT(std::string const& out, bool notify) diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index af02f7f..2b8eedd 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -201,11 +201,7 @@ endif() # Enable testing if building standalone. if(KWSYS_STANDALONE) - include(Dart) - mark_as_advanced(BUILD_TESTING DART_ROOT TCL_TCLSH) - if(BUILD_TESTING) - enable_testing() - endif() + include(CTest) endif() # Choose default shared/static build if not specified. @@ -630,8 +626,8 @@ endif() # Build a list of classes and headers we need to implement the # selected components. Initialize with required components. set(KWSYS_CLASSES) -set(KWSYS_H_FILES Configure SharedForward) -set(KWSYS_HXX_FILES Configure String) +set(KWSYS_H_FILES Configure) +set(KWSYS_HXX_FILES Configure) # Add selected C++ classes. set(cppclasses @@ -1038,6 +1034,10 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR) set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY CXX_INCLUDE_WHAT_YOU_USE "") set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY LABELS ${KWSYS_LABELS_EXE}) target_link_libraries(${KWSYS_NAMESPACE}TestsCxx ${KWSYS_TARGET_LINK}) + get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(_isMultiConfig) + set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx APPEND PROPERTY COMPILE_DEFINITIONS BUILD_CONFIG="$<CONFIG>") + endif() set(TEST_SYSTEMTOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(TEST_SYSTEMTOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") @@ -1118,16 +1118,6 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR) endif() set_property(SOURCE testProcess.c PROPERTY COMPILE_FLAGS "${testProcess_COMPILE_FLAGS}") - # Test SharedForward - configure_file(${PROJECT_SOURCE_DIR}/testSharedForward.c.in - ${PROJECT_BINARY_DIR}/testSharedForward.c @ONLY IMMEDIATE) - add_executable(${KWSYS_NAMESPACE}TestSharedForward - ${PROJECT_BINARY_DIR}/testSharedForward.c) - set_property(TARGET ${KWSYS_NAMESPACE}TestSharedForward PROPERTY LABELS ${KWSYS_LABELS_EXE}) - add_dependencies(${KWSYS_NAMESPACE}TestSharedForward ${KWSYS_TARGET_C_LINK}) - add_test(kwsys.testSharedForward ${EXEC_DIR}/${KWSYS_NAMESPACE}TestSharedForward 1) - set_property(TEST kwsys.testSharedForward PROPERTY LABELS ${KWSYS_LABELS_TEST}) - # Configure some test properties. if(KWSYS_STANDALONE) # We expect test to fail diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx index ccd5f6d..50171dd 100644 --- a/Source/kwsys/CommandLineArguments.cxx +++ b/Source/kwsys/CommandLineArguments.cxx @@ -4,20 +4,19 @@ #include KWSYS_HEADER(CommandLineArguments.hxx) #include KWSYS_HEADER(Configure.hxx) -#include KWSYS_HEADER(String.hxx) // Work-around CMake dependency scanning limitation. This must // duplicate the above list of headers. #if 0 # include "CommandLineArguments.hxx.in" # include "Configure.hxx.in" -# include "String.hxx.in" #endif #include <iostream> #include <map> #include <set> #include <sstream> +#include <string> #include <vector> #include <cstdio> @@ -52,14 +51,14 @@ struct CommandLineArgumentsCallbackStructure const char* Help; }; -class CommandLineArgumentsVectorOfStrings : public std::vector<kwsys::String> +class CommandLineArgumentsVectorOfStrings : public std::vector<std::string> { }; -class CommandLineArgumentsSetOfStrings : public std::set<kwsys::String> +class CommandLineArgumentsSetOfStrings : public std::set<std::string> { }; class CommandLineArgumentsMapOfStrucs - : public std::map<kwsys::String, CommandLineArgumentsCallbackStructure> + : public std::map<std::string, CommandLineArgumentsCallbackStructure> { }; @@ -70,7 +69,7 @@ public: using VectorOfStrings = CommandLineArgumentsVectorOfStrings; using CallbacksMap = CommandLineArgumentsMapOfStrucs; - using String = kwsys::String; + using String = std::string; using SetOfStrings = CommandLineArgumentsSetOfStrings; VectorOfStrings Argv; @@ -306,7 +305,7 @@ void CommandLineArguments::GetUnusedArguments(int* argc, char*** argv) // Copy everything after the LastArgument, since that was not parsed. for (cc = 0; cc < this->Internals->UnusedArguments.size(); cc++) { - kwsys::String& str = this->Internals->UnusedArguments[cc]; + std::string& str = this->Internals->UnusedArguments[cc]; args[cnt] = new char[str.size() + 1]; strcpy(args[cnt], str.c_str()); cnt++; diff --git a/Source/kwsys/SharedForward.h.in b/Source/kwsys/SharedForward.h.in deleted file mode 100644 index d6ae75c..0000000 --- a/Source/kwsys/SharedForward.h.in +++ /dev/null @@ -1,873 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ -#ifndef @KWSYS_NAMESPACE@_SharedForward_h -# define @KWSYS_NAMESPACE@_SharedForward_h - -/* - This header is used to create a forwarding executable sets up the - shared library search path and replaces itself with a real - executable. This is useful when creating installations on UNIX with - shared libraries that will run from any install directory. Typical - usage: - - #if defined(CMAKE_INTDIR) - # define CONFIG_DIR_PRE CMAKE_INTDIR "/" - # define CONFIG_DIR_POST "/" CMAKE_INTDIR - #else - # define CONFIG_DIR_PRE "" - # define CONFIG_DIR_POST "" - #endif - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD "/path/to/foo-build/bin" - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD "." CONFIG_DIR_POST - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL "../lib/foo-1.2" - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD CONFIG_DIR_PRE "foo-real" - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL - "../lib/foo-1.2/foo-real" - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND "--command" - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT "--print" - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD "--ldd" - #if defined(CMAKE_INTDIR) - # define @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR - #endif - #include <@KWSYS_NAMESPACE@/SharedForward.h> - int main(int argc, char** argv) - { - return @KWSYS_NAMESPACE@_shared_forward_to_real(argc, argv); - } - - Specify search and executable paths relative to the forwarding - executable location or as full paths. Include no trailing slash. - In the case of a multi-configuration build, when CMAKE_INTDIR is - defined, the DIR_BUILD setting should point at the directory above - the executable (the one containing the per-configuration - subdirectory specified by CMAKE_INTDIR). Then PATH_BUILD entries - and EXE_BUILD should be specified relative to this location and use - CMAKE_INTDIR as necessary. In the above example imagine appending - the PATH_BUILD or EXE_BUILD setting to the DIR_BUILD setting. The - result should form a valid path with per-configuration subdirectory. - - Additional paths may be specified in the PATH_BUILD and PATH_INSTALL - variables by using comma-separated strings. For example: - - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD \ - "." CONFIG_DIR_POST, "/path/to/bar-build" CONFIG_DIR_POST - #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL \ - "../lib/foo-1.2", "../lib/bar-4.5" - - See the comments below for specific explanations of each macro. -*/ - -/* Disable -Wcast-qual warnings since they are too hard to fix in a - cross-platform way. */ -# if defined(__clang__) && defined(__has_warning) -# if __has_warning("-Wcast-qual") -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wcast-qual" -# endif -# endif - -/* Full path to the directory in which this executable is built. Do - not include a trailing slash. */ -# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD) -# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD" -# endif -# if !defined(KWSYS_SHARED_FORWARD_DIR_BUILD) -# define KWSYS_SHARED_FORWARD_DIR_BUILD \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD -# endif - -/* Library search path for build tree. */ -# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD) -# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD" -# endif -# if !defined(KWSYS_SHARED_FORWARD_PATH_BUILD) -# define KWSYS_SHARED_FORWARD_PATH_BUILD \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD -# endif - -/* Library search path for install tree. */ -# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL) -# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL" -# endif -# if !defined(KWSYS_SHARED_FORWARD_PATH_INSTALL) -# define KWSYS_SHARED_FORWARD_PATH_INSTALL \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL -# endif - -/* The real executable to which to forward in the build tree. */ -# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD) -# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD" -# endif -# if !defined(KWSYS_SHARED_FORWARD_EXE_BUILD) -# define KWSYS_SHARED_FORWARD_EXE_BUILD \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD -# endif - -/* The real executable to which to forward in the install tree. */ -# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL) -# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL" -# endif -# if !defined(KWSYS_SHARED_FORWARD_EXE_INSTALL) -# define KWSYS_SHARED_FORWARD_EXE_INSTALL \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL -# endif - -/* The configuration name with which this executable was built (Debug/Release). - */ -# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME) -# define KWSYS_SHARED_FORWARD_CONFIG_NAME \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME -# else -# undef KWSYS_SHARED_FORWARD_CONFIG_NAME -# endif - -/* Create command line option to replace executable. */ -# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND) -# if !defined(KWSYS_SHARED_FORWARD_OPTION_COMMAND) -# define KWSYS_SHARED_FORWARD_OPTION_COMMAND \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND -# endif -# else -# undef KWSYS_SHARED_FORWARD_OPTION_COMMAND -# endif - -/* Create command line option to print environment setting and exit. */ -# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT) -# if !defined(KWSYS_SHARED_FORWARD_OPTION_PRINT) -# define KWSYS_SHARED_FORWARD_OPTION_PRINT \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT -# endif -# else -# undef KWSYS_SHARED_FORWARD_OPTION_PRINT -# endif - -/* Create command line option to run ldd or equivalent. */ -# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD) -# if !defined(KWSYS_SHARED_FORWARD_OPTION_LDD) -# define KWSYS_SHARED_FORWARD_OPTION_LDD \ - @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD -# endif -# else -# undef KWSYS_SHARED_FORWARD_OPTION_LDD -# endif - -/* Include needed system headers. */ - -# include <errno.h> -# include <limits.h> -# include <stddef.h> /* size_t */ -# include <stdio.h> -# include <stdlib.h> -# include <string.h> - -# if defined(_WIN32) && !defined(__CYGWIN__) -# include <windows.h> - -# include <io.h> -# include <process.h> -# define KWSYS_SHARED_FORWARD_ESCAPE_ARGV /* re-escape argv for execvp */ -# else -# include <sys/stat.h> -# include <unistd.h> -# endif - -/* Configuration for this platform. */ - -/* The path separator for this platform. */ -# if defined(_WIN32) && !defined(__CYGWIN__) -# define KWSYS_SHARED_FORWARD_PATH_SEP ';' -# define KWSYS_SHARED_FORWARD_PATH_SLASH '\\' -# else -# define KWSYS_SHARED_FORWARD_PATH_SEP ':' -# define KWSYS_SHARED_FORWARD_PATH_SLASH '/' -# endif -static const char kwsys_shared_forward_path_sep[2] = { - KWSYS_SHARED_FORWARD_PATH_SEP, 0 -}; -static const char kwsys_shared_forward_path_slash[2] = { - KWSYS_SHARED_FORWARD_PATH_SLASH, 0 -}; - -/* The maximum length of a file name. */ -# if defined(PATH_MAX) -# define KWSYS_SHARED_FORWARD_MAXPATH PATH_MAX -# elif defined(MAXPATHLEN) -# define KWSYS_SHARED_FORWARD_MAXPATH MAXPATHLEN -# else -# define KWSYS_SHARED_FORWARD_MAXPATH 16384 -# endif - -/* Select the environment variable holding the shared library runtime - search path for this platform and build configuration. Also select - ldd command equivalent. */ - -/* Linux */ -# if defined(__linux) -# define KWSYS_SHARED_FORWARD_LDD "ldd" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" - -/* FreeBSD */ -# elif defined(__FreeBSD__) -# define KWSYS_SHARED_FORWARD_LDD "ldd" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" - -/* OpenBSD */ -# elif defined(__OpenBSD__) -# define KWSYS_SHARED_FORWARD_LDD "ldd" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" - -/* OS X */ -# elif defined(__APPLE__) -# define KWSYS_SHARED_FORWARD_LDD "otool", "-L" -# define KWSYS_SHARED_FORWARD_LDD_N 2 -# define KWSYS_SHARED_FORWARD_LDPATH "DYLD_LIBRARY_PATH" - -/* AIX */ -# elif defined(_AIX) -# define KWSYS_SHARED_FORWARD_LDD "dump", "-H" -# define KWSYS_SHARED_FORWARD_LDD_N 2 -# define KWSYS_SHARED_FORWARD_LDPATH "LIBPATH" - -/* SUN */ -# elif defined(__sun) -# define KWSYS_SHARED_FORWARD_LDD "ldd" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# include <sys/isa_defs.h> -# if defined(_ILP32) -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" -# elif defined(_LP64) -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH_64" -# endif - -/* HP-UX */ -# elif defined(__hpux) -# define KWSYS_SHARED_FORWARD_LDD "chatr" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# if defined(__LP64__) -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" -# else -# define KWSYS_SHARED_FORWARD_LDPATH "SHLIB_PATH" -# endif - -/* SGI MIPS */ -# elif defined(__sgi) && defined(_MIPS_SIM) -# define KWSYS_SHARED_FORWARD_LDD "ldd" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# if _MIPS_SIM == _ABIO32 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" -# elif _MIPS_SIM == _ABIN32 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARYN32_PATH" -# elif _MIPS_SIM == _ABI64 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY64_PATH" -# endif - -/* Cygwin */ -# elif defined(__CYGWIN__) -# define KWSYS_SHARED_FORWARD_LDD \ - "cygcheck" /* TODO: cygwin 1.7 has ldd \ - */ -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# define KWSYS_SHARED_FORWARD_LDPATH "PATH" - -/* Windows */ -# elif defined(_WIN32) -# define KWSYS_SHARED_FORWARD_LDPATH "PATH" - -/* Guess on this unknown system. */ -# else -# define KWSYS_SHARED_FORWARD_LDD "ldd" -# define KWSYS_SHARED_FORWARD_LDD_N 1 -# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH" -# endif - -# ifdef KWSYS_SHARED_FORWARD_ESCAPE_ARGV -typedef struct kwsys_sf_arg_info_s -{ - const char* arg; - int size; - int quote; -} kwsys_sf_arg_info; - -static kwsys_sf_arg_info kwsys_sf_get_arg_info(const char* in) -{ - /* Initialize information. */ - kwsys_sf_arg_info info; - - /* String iterator. */ - const char* c; - - /* Keep track of how many backslashes have been encountered in a row. */ - int windows_backslashes = 0; - - /* Start with the length of the original argument, plus one for - either a terminating null or a separating space. */ - info.arg = in; - info.size = (int)strlen(in) + 1; - info.quote = 0; - - /* Scan the string for characters that require escaping or quoting. */ - for (c = in; *c; ++c) { - /* Check whether this character needs quotes. */ - if (strchr(" \t?'#&<>|^", *c)) { - info.quote = 1; - } - - /* On Windows only backslashes and double-quotes need escaping. */ - if (*c == '\\') { - /* Found a backslash. It may need to be escaped later. */ - ++windows_backslashes; - } else if (*c == '"') { - /* Found a double-quote. We need to escape it and all - immediately preceding backslashes. */ - info.size += windows_backslashes + 1; - windows_backslashes = 0; - } else { - /* Found another character. This eliminates the possibility - that any immediately preceding backslashes will be - escaped. */ - windows_backslashes = 0; - } - } - - /* Check whether the argument needs surrounding quotes. */ - if (info.quote) { - /* Surrounding quotes are needed. Allocate space for them. */ - info.size += 2; - - /* We must escape all ending backslashes when quoting on windows. */ - info.size += windows_backslashes; - } - - return info; -} - -static char* kwsys_sf_get_arg(kwsys_sf_arg_info info, char* out) -{ - /* String iterator. */ - const char* c; - - /* Keep track of how many backslashes have been encountered in a row. */ - int windows_backslashes = 0; - - /* Whether the argument must be quoted. */ - if (info.quote) { - /* Add the opening quote for this argument. */ - *out++ = '"'; - } - - /* Scan the string for characters that require escaping or quoting. */ - for (c = info.arg; *c; ++c) { - /* On Windows only backslashes and double-quotes need escaping. */ - if (*c == '\\') { - /* Found a backslash. It may need to be escaped later. */ - ++windows_backslashes; - } else if (*c == '"') { - /* Found a double-quote. Escape all immediately preceding - backslashes. */ - while (windows_backslashes > 0) { - --windows_backslashes; - *out++ = '\\'; - } - - /* Add the backslash to escape the double-quote. */ - *out++ = '\\'; - } else { - /* We encountered a normal character. This eliminates any - escaping needed for preceding backslashes. */ - windows_backslashes = 0; - } - - /* Store this character. */ - *out++ = *c; - } - - if (info.quote) { - /* Add enough backslashes to escape any trailing ones. */ - while (windows_backslashes > 0) { - --windows_backslashes; - *out++ = '\\'; - } - - /* Add the closing quote for this argument. */ - *out++ = '"'; - } - - /* Store a terminating null without incrementing. */ - *out = 0; - - return out; -} -# endif - -/* Function to convert a logical or relative path to a physical full path. */ -static int kwsys_shared_forward_realpath(const char* in_path, char* out_path) -{ -# if defined(_WIN32) && !defined(__CYGWIN__) - /* Implementation for Windows. */ - DWORD n = - GetFullPathNameA(in_path, KWSYS_SHARED_FORWARD_MAXPATH, out_path, 0); - return n > 0 && n <= KWSYS_SHARED_FORWARD_MAXPATH; -# else - /* Implementation for UNIX. */ - return realpath(in_path, out_path) != 0; -# endif -} - -static int kwsys_shared_forward_samepath(const char* file1, const char* file2) -{ -# if defined(_WIN32) - int result = 0; - HANDLE h1 = CreateFileA(file1, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - HANDLE h2 = CreateFileA(file2, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h1 != INVALID_HANDLE_VALUE && h2 != INVALID_HANDLE_VALUE) { - BY_HANDLE_FILE_INFORMATION fi1; - BY_HANDLE_FILE_INFORMATION fi2; - GetFileInformationByHandle(h1, &fi1); - GetFileInformationByHandle(h2, &fi2); - result = (fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber && - fi1.nFileIndexHigh == fi2.nFileIndexHigh && - fi1.nFileIndexLow == fi2.nFileIndexLow); - } - CloseHandle(h1); - CloseHandle(h2); - return result; -# else - struct stat fs1, fs2; - return (stat(file1, &fs1) == 0 && stat(file2, &fs2) == 0 && - memcmp(&fs2.st_dev, &fs1.st_dev, sizeof(fs1.st_dev)) == 0 && - memcmp(&fs2.st_ino, &fs1.st_ino, sizeof(fs1.st_ino)) == 0 && - fs2.st_size == fs1.st_size); -# endif -} - -/* Function to report a system error message. */ -static void kwsys_shared_forward_strerror(char* message) -{ -# if defined(_WIN32) && !defined(__CYGWIN__) - /* Implementation for Windows. */ - DWORD original = GetLastError(); - DWORD length = - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - 0, original, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - message, KWSYS_SHARED_FORWARD_MAXPATH, 0); - if (length < 1 || length > KWSYS_SHARED_FORWARD_MAXPATH) { - /* FormatMessage failed. Use a default message. */ - snprintf(message, KWSYS_SHARED_FORWARD_MAXPATH, - "Error 0x%lX (FormatMessage failed with error 0x%lX)", original, - GetLastError()); - } -# else - /* Implementation for UNIX. */ - strcpy(message, strerror(errno)); -# endif -} - -/* Functions to execute a child process. */ -static void kwsys_shared_forward_execvp(const char* cmd, - char const* const* argv) -{ -# ifdef KWSYS_SHARED_FORWARD_ESCAPE_ARGV - /* Count the number of arguments. */ - int argc = 0; - { - char const* const* argvc; - for (argvc = argv; *argvc; ++argvc, ++argc) { - } - } - - /* Create the escaped arguments. */ - { - char** nargv = (char**)malloc((argc + 1) * sizeof(char*)); - int i; - for (i = 0; i < argc; ++i) { - kwsys_sf_arg_info info = kwsys_sf_get_arg_info(argv[i]); - nargv[i] = (char*)malloc(info.size); - kwsys_sf_get_arg(info, nargv[i]); - } - nargv[argc] = 0; - - /* Replace the command line to be used. */ - argv = (char const* const*)nargv; - } -# endif - -/* Invoke the child process. */ -# if defined(_MSC_VER) - _execvp(cmd, argv); -# elif defined(__MINGW32__) && !defined(__MINGW64__) - execvp(cmd, argv); -# else - execvp(cmd, (char* const*)argv); -# endif -} - -/* Function to get the directory containing the given file or directory. */ -static void kwsys_shared_forward_dirname(const char* begin, char* result) -{ - /* Find the location of the last slash. */ - int last_slash_index = -1; - const char* end = begin + strlen(begin); - for (; begin <= end && last_slash_index < 0; --end) { - if (*end == '/' || *end == '\\') { - last_slash_index = (int)(end - begin); - } - } - - /* Handle each case of the index of the last slash. */ - if (last_slash_index < 0) { - /* No slashes. */ - strcpy(result, "."); - } else if (last_slash_index == 0) { - /* Only one leading slash. */ - strcpy(result, kwsys_shared_forward_path_slash); - } -# if defined(_WIN32) - else if (last_slash_index == 2 && begin[1] == ':') { - /* Only one leading drive letter and slash. */ - strncpy(result, begin, (size_t)last_slash_index); - result[last_slash_index] = KWSYS_SHARED_FORWARD_PATH_SLASH; - result[last_slash_index + 1] = 0; - } -# endif - else { - /* A non-leading slash. */ - strncpy(result, begin, (size_t)last_slash_index); - result[last_slash_index] = 0; - } -} - -/* Function to check if a file exists and is executable. */ -static int kwsys_shared_forward_is_executable(const char* f) -{ -# if defined(_MSC_VER) -# define KWSYS_SHARED_FORWARD_ACCESS _access -# else -# define KWSYS_SHARED_FORWARD_ACCESS access -# endif -# if defined(X_OK) -# define KWSYS_SHARED_FORWARD_ACCESS_OK X_OK -# else -# define KWSYS_SHARED_FORWARD_ACCESS_OK 04 -# endif - if (KWSYS_SHARED_FORWARD_ACCESS(f, KWSYS_SHARED_FORWARD_ACCESS_OK) == 0) { - return 1; - } else { - return 0; - } -} - -/* Function to locate the executable currently running. */ -static int kwsys_shared_forward_self_path(const char* argv0, char* result) -{ - /* Check whether argv0 has a slash. */ - int has_slash = 0; - const char* p = argv0; - for (; *p && !has_slash; ++p) { - if (*p == '/' || *p == '\\') { - has_slash = 1; - } - } - - if (has_slash) { - /* There is a slash. Use the dirname of the given location. */ - kwsys_shared_forward_dirname(argv0, result); - return 1; - } else { - /* There is no slash. Search the PATH for the executable. */ - const char* path = getenv("PATH"); - const char* begin = path; - const char* end = begin + (begin ? strlen(begin) : 0); - const char* first = begin; - while (first != end) { - /* Store the end of this path entry. */ - const char* last; - - /* Skip all path separators. */ - for (; *first && *first == KWSYS_SHARED_FORWARD_PATH_SEP; ++first) - ; - - /* Find the next separator. */ - for (last = first; *last && *last != KWSYS_SHARED_FORWARD_PATH_SEP; - ++last) - ; - - /* If we got a non-empty directory, look for the executable there. */ - if (first < last) { - /* Determine the length without trailing slash. */ - size_t length = (size_t)(last - first); - if (*(last - 1) == '/' || *(last - 1) == '\\') { - --length; - } - - /* Construct the name of the executable in this location. */ - strncpy(result, first, length); - result[length] = KWSYS_SHARED_FORWARD_PATH_SLASH; - strcpy(result + (length) + 1, argv0); - - /* Check if it exists and is executable. */ - if (kwsys_shared_forward_is_executable(result)) { - /* Found it. */ - result[length] = 0; - return 1; - } - } - - /* Move to the next directory in the path. */ - first = last; - } - } - - /* We could not find the executable. */ - return 0; -} - -/* Function to convert a specified path to a full path. If it is not - already full, it is taken relative to the self path. */ -static int kwsys_shared_forward_fullpath(const char* self_path, - const char* in_path, char* result, - const char* desc) -{ - /* Check the specified path type. */ - if (in_path[0] == '/') { - /* Already a full path. */ - strcpy(result, in_path); - } -# if defined(_WIN32) - else if (in_path[0] && in_path[1] == ':') { - /* Already a full path. */ - strcpy(result, in_path); - } -# endif - else { - /* Relative to self path. */ - char temp_path[KWSYS_SHARED_FORWARD_MAXPATH]; - strcpy(temp_path, self_path); - strcat(temp_path, kwsys_shared_forward_path_slash); - strcat(temp_path, in_path); - if (!kwsys_shared_forward_realpath(temp_path, result)) { - if (desc) { - char msgbuf[KWSYS_SHARED_FORWARD_MAXPATH]; - kwsys_shared_forward_strerror(msgbuf); - fprintf(stderr, "Error converting %s \"%s\" to real path: %s\n", desc, - temp_path, msgbuf); - } - return 0; - } - } - return 1; -} - -/* Function to compute the library search path and executable name - based on the self path. */ -static int kwsys_shared_forward_get_settings(const char* self_path, - char* ldpath, char* exe) -{ - /* Possible search paths. */ - static const char* search_path_build[] = { KWSYS_SHARED_FORWARD_PATH_BUILD, - 0 }; - static const char* search_path_install[] = { - KWSYS_SHARED_FORWARD_PATH_INSTALL, 0 - }; - - /* Chosen paths. */ - const char** search_path; - const char* exe_path; - -/* Get the real name of the build and self paths. */ -# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME) - char build_path[] = - KWSYS_SHARED_FORWARD_DIR_BUILD "/" KWSYS_SHARED_FORWARD_CONFIG_NAME; - char self_path_logical[KWSYS_SHARED_FORWARD_MAXPATH]; -# else - char build_path[] = KWSYS_SHARED_FORWARD_DIR_BUILD; - const char* self_path_logical = self_path; -# endif - char build_path_real[KWSYS_SHARED_FORWARD_MAXPATH]; - char self_path_real[KWSYS_SHARED_FORWARD_MAXPATH]; - if (!kwsys_shared_forward_realpath(self_path, self_path_real)) { - char msgbuf[KWSYS_SHARED_FORWARD_MAXPATH]; - kwsys_shared_forward_strerror(msgbuf); - fprintf(stderr, "Error converting self path \"%s\" to real path: %s\n", - self_path, msgbuf); - return 0; - } - - /* Check whether we are running in the build tree or an install tree. */ - if (kwsys_shared_forward_realpath(build_path, build_path_real) && - kwsys_shared_forward_samepath(self_path_real, build_path_real)) { - /* Running in build tree. Use the build path and exe. */ - search_path = search_path_build; -# if defined(_WIN32) - exe_path = KWSYS_SHARED_FORWARD_EXE_BUILD ".exe"; -# else - exe_path = KWSYS_SHARED_FORWARD_EXE_BUILD; -# endif - -# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME) - /* Remove the configuration directory from self_path. */ - kwsys_shared_forward_dirname(self_path, self_path_logical); -# endif - } else { - /* Running in install tree. Use the install path and exe. */ - search_path = search_path_install; -# if defined(_WIN32) - exe_path = KWSYS_SHARED_FORWARD_EXE_INSTALL ".exe"; -# else - exe_path = KWSYS_SHARED_FORWARD_EXE_INSTALL; -# endif - -# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME) - /* Use the original self path directory. */ - strcpy(self_path_logical, self_path); -# endif - } - - /* Construct the runtime search path. */ - { - const char** dir; - for (dir = search_path; *dir; ++dir) { - /* Add separator between path components. */ - if (dir != search_path) { - strcat(ldpath, kwsys_shared_forward_path_sep); - } - - /* Add this path component. */ - if (!kwsys_shared_forward_fullpath(self_path_logical, *dir, - ldpath + strlen(ldpath), - "runtime path entry")) { - return 0; - } - } - } - - /* Construct the executable location. */ - if (!kwsys_shared_forward_fullpath(self_path_logical, exe_path, exe, - "executable file")) { - return 0; - } - return 1; -} - -/* Function to print why execution of a command line failed. */ -static void kwsys_shared_forward_print_failure(char const* const* argv) -{ - char msg[KWSYS_SHARED_FORWARD_MAXPATH]; - char const* const* arg = argv; - kwsys_shared_forward_strerror(msg); - fprintf(stderr, "Error running"); - for (; *arg; ++arg) { - fprintf(stderr, " \"%s\"", *arg); - } - fprintf(stderr, ": %s\n", msg); -} - -/* Static storage space to store the updated environment variable. */ -static char kwsys_shared_forward_ldpath[65535] = - KWSYS_SHARED_FORWARD_LDPATH "="; - -/* Main driver function to be called from main. */ -static int @KWSYS_NAMESPACE@_shared_forward_to_real(int argc, char** argv_in) -{ - char const** argv = (char const**)argv_in; - /* Get the directory containing this executable. */ - char self_path[KWSYS_SHARED_FORWARD_MAXPATH]; - if (kwsys_shared_forward_self_path(argv[0], self_path)) { - /* Found this executable. Use it to get the library directory. */ - char exe[KWSYS_SHARED_FORWARD_MAXPATH]; - if (kwsys_shared_forward_get_settings(self_path, - kwsys_shared_forward_ldpath, exe)) { - /* Append the old runtime search path. */ - const char* old_ldpath = getenv(KWSYS_SHARED_FORWARD_LDPATH); - if (old_ldpath) { - strcat(kwsys_shared_forward_ldpath, kwsys_shared_forward_path_sep); - strcat(kwsys_shared_forward_ldpath, old_ldpath); - } - - /* Store the environment variable. */ - putenv(kwsys_shared_forward_ldpath); - -# if defined(KWSYS_SHARED_FORWARD_OPTION_COMMAND) - /* Look for the command line replacement option. */ - if (argc > 1 && - strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_COMMAND) == 0) { - if (argc > 2) { - /* Use the command line given. */ - strcpy(exe, argv[2]); - argv += 2; - argc -= 2; - } else { - /* The option was not given an executable. */ - fprintf(stderr, - "Option " KWSYS_SHARED_FORWARD_OPTION_COMMAND - " must be followed by a command line.\n"); - return 1; - } - } -# endif - -# if defined(KWSYS_SHARED_FORWARD_OPTION_PRINT) - /* Look for the print command line option. */ - if (argc > 1 && - strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_PRINT) == 0) { - fprintf(stdout, "%s\n", kwsys_shared_forward_ldpath); - fprintf(stdout, "%s\n", exe); - return 0; - } -# endif - -# if defined(KWSYS_SHARED_FORWARD_OPTION_LDD) - /* Look for the ldd command line option. */ - if (argc > 1 && strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_LDD) == 0) { -# if defined(KWSYS_SHARED_FORWARD_LDD) - /* Use the named ldd-like executable and arguments. */ - char const* ldd_argv[] = { KWSYS_SHARED_FORWARD_LDD, 0, 0 }; - ldd_argv[KWSYS_SHARED_FORWARD_LDD_N] = exe; - kwsys_shared_forward_execvp(ldd_argv[0], ldd_argv); - - /* Report why execution failed. */ - kwsys_shared_forward_print_failure(ldd_argv); - return 1; -# else - /* We have no ldd-like executable available on this platform. */ - fprintf(stderr, "No ldd-like tool is known to this executable.\n"); - return 1; -# endif - } -# endif - - /* Replace this process with the real executable. */ - argv[0] = exe; - kwsys_shared_forward_execvp(argv[0], argv); - - /* Report why execution failed. */ - kwsys_shared_forward_print_failure(argv); - } else { - /* Could not convert self path to the library directory. */ - } - } else { - /* Could not find this executable. */ - fprintf(stderr, "Error locating executable \"%s\".\n", argv[0]); - } - - /* Avoid unused argument warning. */ - (void)argc; - - /* Exit with failure. */ - return 1; -} - -/* Restore warning stack. */ -# if defined(__clang__) && defined(__has_warning) -# if __has_warning("-Wcast-qual") -# pragma clang diagnostic pop -# endif -# endif - -#else -# error "@KWSYS_NAMESPACE@/SharedForward.h should be included only once." -#endif diff --git a/Source/kwsys/String.hxx.in b/Source/kwsys/String.hxx.in deleted file mode 100644 index c36f4ce..0000000 --- a/Source/kwsys/String.hxx.in +++ /dev/null @@ -1,57 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ -#ifndef @KWSYS_NAMESPACE@_String_hxx -#define @KWSYS_NAMESPACE@_String_hxx - -#include <string> - -namespace @KWSYS_NAMESPACE@ { - -/** \class String - * \brief Short-name version of the STL basic_string class template. - * - * The standard library "string" type is actually a typedef for - * "basic_string<..long argument list..>". This string class is - * simply a subclass of this type with the same interface so that the - * name is shorter in debugging symbols and error messages. - */ -class String : public std::string -{ - /** The original string type. */ - typedef std::string stl_string; - -public: - /** String member types. */ - typedef stl_string::value_type value_type; - typedef stl_string::pointer pointer; - typedef stl_string::reference reference; - typedef stl_string::const_reference const_reference; - typedef stl_string::size_type size_type; - typedef stl_string::difference_type difference_type; - typedef stl_string::iterator iterator; - typedef stl_string::const_iterator const_iterator; - typedef stl_string::reverse_iterator reverse_iterator; - typedef stl_string::const_reverse_iterator const_reverse_iterator; - - /** String constructors. */ - String() - : stl_string() - { - } - String(const value_type* s) - : stl_string(s) - { - } - String(const value_type* s, size_type n) - : stl_string(s, n) - { - } - String(const stl_string& s, size_type pos = 0, size_type n = npos) - : stl_string(s, pos, n) - { - } -}; // End Class: String - -} // namespace @KWSYS_NAMESPACE@ - -#endif diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index a3ab51a..3bb7869 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -2528,6 +2528,12 @@ SystemTools::CopyStatus SystemTools::CloneFileContent( return status; #elif defined(__APPLE__) && \ defined(KWSYS_SYSTEMTOOLS_HAVE_MACOS_COPYFILE_CLONE) + // When running as root, copyfile() copies more metadata than we + // want, such as ownership. Pretend it is not available. + if (getuid() == 0) { + return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath }; + } + // NOTE: we cannot use `clonefile` as the {a,c,m}time for the file needs to // be updated by `copy_file_if_different` and `copy_file`. if (copyfile(source.c_str(), destination.c_str(), nullptr, @@ -3418,9 +3424,7 @@ bool SystemTools::SplitProgramPath(const std::string& in_name, } bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut, - std::string& errorMsg, const char* exeName, - const char* buildDir, - const char* installPrefix) + std::string& errorMsg) { std::vector<std::string> failures; std::string self = argv0 ? argv0 : ""; @@ -3428,34 +3432,9 @@ bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut, SystemTools::ConvertToUnixSlashes(self); self = SystemTools::FindProgram(self); if (!SystemTools::FileIsExecutable(self)) { - if (buildDir) { - std::string intdir = "."; -#ifdef CMAKE_INTDIR - intdir = CMAKE_INTDIR; -#endif - self = buildDir; - self += "/bin/"; - self += intdir; - self += "/"; - self += exeName; - self += SystemTools::GetExecutableExtension(); - } - } - if (installPrefix) { - if (!SystemTools::FileIsExecutable(self)) { - failures.push_back(self); - self = installPrefix; - self += "/bin/"; - self += exeName; - } - } - if (!SystemTools::FileIsExecutable(self)) { failures.push_back(self); std::ostringstream msg; msg << "Can not find the command line program "; - if (exeName) { - msg << exeName; - } msg << "\n"; if (argv0) { msg << " argv[0] = \"" << argv0 << "\"\n"; diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index 56b65fd..729928e 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -395,10 +395,7 @@ public: * installPrefix is a possibly null pointer to the install directory. */ static bool FindProgramPath(const char* argv0, std::string& pathOut, - std::string& errorMsg, - const char* exeName = nullptr, - const char* buildDir = nullptr, - const char* installPrefix = nullptr); + std::string& errorMsg); /** * Given a path to a file or directory, convert it to a full path. diff --git a/Source/kwsys/kwsysPlatformTests.cmake b/Source/kwsys/kwsysPlatformTests.cmake index 89be4b8..6c006bc 100644 --- a/Source/kwsys/kwsysPlatformTests.cmake +++ b/Source/kwsys/kwsysPlatformTests.cmake @@ -8,9 +8,6 @@ macro(KWSYS_PLATFORM_TEST lang var description invert) if(NOT DEFINED ${var}_COMPILED) message(STATUS "${description}") set(maybe_cxx_standard "") - if(CMAKE_VERSION VERSION_LESS 3.8 AND CMAKE_CXX_STANDARD) - set(maybe_cxx_standard "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}") - endif() try_compile(${var}_COMPILED ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${KWSYS_PLATFORM_TEST_FILE_${lang}} @@ -18,14 +15,16 @@ macro(KWSYS_PLATFORM_TEST lang var description invert) CMAKE_FLAGS "-DLINK_LIBRARIES:STRING=${KWSYS_PLATFORM_TEST_LINK_LIBRARIES}" ${maybe_cxx_standard} OUTPUT_VARIABLE OUTPUT) - if(${var}_COMPILED) - file(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "${description} compiled with the following output:\n${OUTPUT}\n\n") - else() - file(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + if(CMAKE_VERSION VERSION_LESS 3.26) + if(${var}_COMPILED) + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "${description} compiled with the following output:\n${OUTPUT}\n\n") + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + endif() endif() if(${invert} MATCHES INVERT) if(${var}_COMPILED) @@ -67,19 +66,23 @@ macro(KWSYS_PLATFORM_TEST_RUN lang var description invert) # Note that ${var} will be a 0 return value on success. if(${var}_COMPILED) - if(${var}) + if(CMAKE_VERSION VERSION_LESS 3.26) + if(${var}) + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} compiled but failed to run with the following output:\n${OUTPUT}\n\n") + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "${description} compiled and ran with the following output:\n${OUTPUT}\n\n") + endif() + endif() + else() + if(CMAKE_VERSION VERSION_LESS 3.26) file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "${description} compiled but failed to run with the following output:\n${OUTPUT}\n\n") - else() - file(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "${description} compiled and ran with the following output:\n${OUTPUT}\n\n") + "${description} failed to compile with the following output:\n${OUTPUT}\n\n") endif() - else() - file(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "${description} failed to compile with the following output:\n${OUTPUT}\n\n") set(${var} -1 CACHE INTERNAL "${description} failed to compile.") endif() @@ -188,14 +191,16 @@ macro(KWSYS_PLATFORM_INFO_TEST lang var description) OUTPUT_VARIABLE OUTPUT COPY_FILE ${KWSYS_PLATFORM_INFO_FILE} ) - if(${var}_COMPILED) - file(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "${description} compiled with the following output:\n${OUTPUT}\n\n") - else() - file(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + if(CMAKE_VERSION VERSION_LESS 3.26) + if(${var}_COMPILED) + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "${description} compiled with the following output:\n${OUTPUT}\n\n") + else() + file(APPEND + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "${description} failed to compile with the following output:\n${OUTPUT}\n\n") + endif() endif() if(${var}_COMPILED) message(STATUS "${description} - compiled") diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx index 806c01a..a5095a5 100644 --- a/Source/kwsys/testDynamicLoader.cxx +++ b/Source/kwsys/testDynamicLoader.cxx @@ -53,9 +53,9 @@ static std::string GetLibName(const char* lname, const char* subdir = nullptr) slname += "/"; slname += subdir; } -#ifdef CMAKE_INTDIR +#ifdef BUILD_CONFIG slname += "/"; - slname += CMAKE_INTDIR; + slname += BUILD_CONFIG; #endif slname += "/"; slname += kwsys::DynamicLoader::LibPrefix(); diff --git a/Source/kwsys/testSharedForward.c.in b/Source/kwsys/testSharedForward.c.in deleted file mode 100644 index e909458..0000000 --- a/Source/kwsys/testSharedForward.c.in +++ /dev/null @@ -1,31 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ -#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__OpenBSD__) -/* NOLINTNEXTLINE(bugprone-reserved-identifier) */ -# define _XOPEN_SOURCE 600 -#endif -#if defined(CMAKE_INTDIR) -# define CONFIG_DIR_PRE CMAKE_INTDIR "/" -# define CONFIG_DIR_POST "/" CMAKE_INTDIR -#else -# define CONFIG_DIR_PRE "" -# define CONFIG_DIR_POST "" -#endif -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD "@EXEC_DIR@" -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD "." CONFIG_DIR_POST -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL 0 -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD \ - CONFIG_DIR_PRE "@KWSYS_NAMESPACE@TestProcess" -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL \ - "@KWSYS_NAMESPACE@TestProcess" -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND "--command" -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT "--print" -#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD "--ldd" -#if defined(CMAKE_INTDIR) -# define @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR -#endif -#include <@KWSYS_NAMESPACE@/SharedForward.h> -int main(int argc, char** argv) -{ - return @KWSYS_NAMESPACE@_shared_forward_to_real(argc, argv); -} diff --git a/Tests/Architecture/CMakeLists.txt b/Tests/Architecture/CMakeLists.txt index 96def00..3d10ee0 100644 --- a/Tests/Architecture/CMakeLists.txt +++ b/Tests/Architecture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(Architecture C) function(test_for_xcode4 result_var) diff --git a/Tests/ArgumentExpansion/CMakeLists.txt b/Tests/ArgumentExpansion/CMakeLists.txt index da3bb4c..9ab87b2 100644 --- a/Tests/ArgumentExpansion/CMakeLists.txt +++ b/Tests/ArgumentExpansion/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ArgumentExpansion) diff --git a/Tests/BundleGeneratorTest/CMakeLists.txt b/Tests/BundleGeneratorTest/CMakeLists.txt index cf7e2ce..069fb77 100644 --- a/Tests/BundleGeneratorTest/CMakeLists.txt +++ b/Tests/BundleGeneratorTest/CMakeLists.txt @@ -1,6 +1,6 @@ project(BundleGeneratorTest) -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Build a shared library and install it in lib/ add_library(Library SHARED Library.cxx) diff --git a/Tests/BundleUtilities/CMakeLists.txt b/Tests/BundleUtilities/CMakeLists.txt index 4a95e2f..b53d499 100644 --- a/Tests/BundleUtilities/CMakeLists.txt +++ b/Tests/BundleUtilities/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(BundleUtilities) if(CMAKE_GENERATOR STREQUAL "Xcode" AND diff --git a/Tests/CFBundleTest/CMakeLists.txt b/Tests/CFBundleTest/CMakeLists.txt index 5f2e8ec..40dd887 100644 --- a/Tests/CFBundleTest/CMakeLists.txt +++ b/Tests/CFBundleTest/CMakeLists.txt @@ -1,6 +1,6 @@ #this is adapted from FireBreath (http://www.firebreath.org) -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CFBundleTest) diff --git a/Tests/CMakeCommands/add_compile_options/CMakeLists.txt b/Tests/CMakeCommands/add_compile_options/CMakeLists.txt index a6b3ffe..96f553a 100644 --- a/Tests/CMakeCommands/add_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/add_compile_options/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt index 72b3502..0c1af9e 100644 --- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(target_compile_definitions) diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt index 2e3760a..dd4fe02 100644 --- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt index 3de9ef7..d5d4970 100644 --- a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt +++ b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(target_include_directories) diff --git a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt index 52080bd..b2365ca 100644 --- a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt +++ b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt @@ -1,6 +1,6 @@ # Using 2.8 will trigger a deprecation warning. In this case it's explicitly # intentional since the tests checks various policy implementations prior to -# 2.8.12 +# 3.5 cmake_minimum_required(VERSION 2.8) if(POLICY CMP0129) diff --git a/Tests/CMakeLib/testRST.expect b/Tests/CMakeLib/testRST.expect index 5e3cdb1..076562a 100644 --- a/Tests/CMakeLib/testRST.expect +++ b/Tests/CMakeLib/testRST.expect @@ -70,6 +70,14 @@ Bracket Comment Content Generator expression $<OTHER_GENEX> description. +.. cmake:signature:: some_command(SOME_SIGNATURE) + + Command some_command SOME_SIGNATURE description. + +.. signature:: other_command(OTHER_SIGNATURE) + + Command other_command OTHER_SIGNATURE description. + .. cmake:variable:: some_var Variable some_var description. diff --git a/Tests/CMakeLib/testRST.rst b/Tests/CMakeLib/testRST.rst index 4139801..43b08da 100644 --- a/Tests/CMakeLib/testRST.rst +++ b/Tests/CMakeLib/testRST.rst @@ -73,6 +73,14 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_. Generator expression $<OTHER_GENEX> description. +.. cmake:signature:: some_command(SOME_SIGNATURE) + + Command some_command SOME_SIGNATURE description. + +.. signature:: other_command(OTHER_SIGNATURE) + + Command other_command OTHER_SIGNATURE description. + .. cmake:variable:: some_var Variable some_var description. diff --git a/Tests/CMakeLib/testSystemTools.cxx b/Tests/CMakeLib/testSystemTools.cxx index 754205e..0f0c025 100644 --- a/Tests/CMakeLib/testSystemTools.cxx +++ b/Tests/CMakeLib/testSystemTools.cxx @@ -36,6 +36,53 @@ int testSystemTools(int /*unused*/, char* /*unused*/[]) "cmSystemTools::UpperCase"); // ---------------------------------------------------------------------- + // Test cmSystemTools::VersionCompare + cmAssert(cmSystemTools::VersionCompareEqual("", ""), + "VersionCompareEqual empty string"); + cmAssert(!cmSystemTools::VersionCompareGreater("", ""), + "VersionCompareGreater empty string"); + cmAssert(cmSystemTools::VersionCompareEqual("1", "1a"), + "VersionCompareEqual letters"); + cmAssert(!cmSystemTools::VersionCompareGreater("1", "1a"), + "VersionCompareGreater letters"); + cmAssert(cmSystemTools::VersionCompareEqual("001", "1"), + "VersionCompareEqual leading zeros equal"); + cmAssert(!cmSystemTools::VersionCompareGreater("001", "1"), + "VersionCompareGreater leading zeros equal"); + cmAssert(!cmSystemTools::VersionCompareEqual("002", "1"), + "VersionCompareEqual leading zeros greater"); + cmAssert(cmSystemTools::VersionCompareGreater("002", "1"), + "VersionCompareGreater leading zeros greater"); + cmAssert(!cmSystemTools::VersionCompareEqual("6.2.1", "6.3.1"), + "VersionCompareEqual components less"); + cmAssert(!cmSystemTools::VersionCompareGreater("6.2.1", "6.3.1"), + "VersionCompareGreater components less"); + cmAssert(!cmSystemTools::VersionCompareEqual("6.2.1", "6.2"), + "VersionCompareEqual different length"); + cmAssert(cmSystemTools::VersionCompareGreater("6.2.1", "6.2"), + "VersionCompareGreater different length"); + cmAssert( + !cmSystemTools::VersionCompareEqual( + "3.14159265358979323846264338327950288419716939937510582097494459230", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareEqual long number"); + cmAssert( + !cmSystemTools::VersionCompareGreater( + "3.14159265358979323846264338327950288419716939937510582097494459230", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareGreater long number"); + cmAssert( + !cmSystemTools::VersionCompareEqual( + "3.141592653589793238462643383279502884197169399375105820974944592307", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareEqual more digits"); + cmAssert( + cmSystemTools::VersionCompareGreater( + "3.141592653589793238462643383279502884197169399375105820974944592307", + "3.14159265358979323846264338327950288419716939937510582097494459231"), + "VersionCompareGreater more digits"); + + // ---------------------------------------------------------------------- // Test cmSystemTools::strverscmp cmAssert(cmSystemTools::strverscmp("", "") == 0, "strverscmp empty string"); cmAssert(cmSystemTools::strverscmp("abc", "") > 0, diff --git a/Tests/CMakeLib/testVisualStudioSlnParser.cxx b/Tests/CMakeLib/testVisualStudioSlnParser.cxx index c1bf3d4..3485bac 100644 --- a/Tests/CMakeLib/testVisualStudioSlnParser.cxx +++ b/Tests/CMakeLib/testVisualStudioSlnParser.cxx @@ -80,7 +80,6 @@ int testVisualStudioSlnParser(int, char*[]) "cmsysProcessFwd9x", "cmsysTestDynload", "cmsysTestProcess", - "cmsysTestSharedForward", "cmsysTestsC", "cmsysTestsCxx", "cmsys_c", diff --git a/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file b/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file index 395b953..1f148fc 100644 --- a/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file +++ b/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file @@ -21,7 +21,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ALL_BUILD", "ALL_BUILD.vcxp {29D5FCAF-20D0-4DEF-8529-F035C249E996} = {29D5FCAF-20D0-4DEF-8529-F035C249E996} {A0421DCA-AC3E-42D0-94AC-379A21A1E591} = {A0421DCA-AC3E-42D0-94AC-379A21A1E591} {C6AF7E57-CE57-4462-AE1D-BF520701480E} = {C6AF7E57-CE57-4462-AE1D-BF520701480E} - {F2CAAAB3-9568-4284-B8E3-13955183A6D7} = {F2CAAAB3-9568-4284-B8E3-13955183A6D7} {D8294E4A-03C5-43D7-AE35-15603F502DC0} = {D8294E4A-03C5-43D7-AE35-15603F502DC0} {A4921D15-411F-436A-B6F3-F8381652A8E1} = {A4921D15-411F-436A-B6F3-F8381652A8E1} {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} @@ -220,12 +219,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestProcess", "Source\ {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestSharedForward", "Source\kwsys\cmsysTestSharedForward.vcxproj", "{F2CAAAB3-9568-4284-B8E3-13955183A6D7}" - ProjectSection(ProjectDependencies) = postProject - {90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044} - {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestsC", "Source\kwsys\cmsysTestsC.vcxproj", "{D8294E4A-03C5-43D7-AE35-15603F502DC0}" ProjectSection(ProjectDependencies) = postProject {90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044} @@ -528,14 +521,6 @@ Global {C6AF7E57-CE57-4462-AE1D-BF520701480E}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 {C6AF7E57-CE57-4462-AE1D-BF520701480E}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 {C6AF7E57-CE57-4462-AE1D-BF520701480E}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Debug|x64.ActiveCfg = Debug|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Debug|x64.Build.0 = Debug|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Release|x64.ActiveCfg = Release|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Release|x64.Build.0 = Release|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 - {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 {D8294E4A-03C5-43D7-AE35-15603F502DC0}.Debug|x64.ActiveCfg = Debug|x64 {D8294E4A-03C5-43D7-AE35-15603F502DC0}.Debug|x64.Build.0 = Debug|x64 {D8294E4A-03C5-43D7-AE35-15603F502DC0}.Release|x64.ActiveCfg = Release|x64 @@ -667,7 +652,6 @@ Global {29D5FCAF-20D0-4DEF-8529-F035C249E996} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {A0421DCA-AC3E-42D0-94AC-379A21A1E591} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {C6AF7E57-CE57-4462-AE1D-BF520701480E} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} - {F2CAAAB3-9568-4284-B8E3-13955183A6D7} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {D8294E4A-03C5-43D7-AE35-15603F502DC0} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {A4921D15-411F-436A-B6F3-F8381652A8E1} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54} diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index c22f704..d913e93 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -40,10 +40,12 @@ set(ENV{HOME} \"${TEST_HOME}\") endif() # Suppress generator deprecation warnings in test suite. -if(CMAKE_GENERATOR MATCHES "^Visual Studio 11 2012") - set(TEST_WARN_VS11_CODE "set(ENV{CMAKE_WARN_VS11} OFF)") +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 11 2012") + set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS11} OFF)") else() - set(TEST_WARN_VS11_CODE "") + set(TEST_WARN_VS_CODE "") endif() # 3.9 or later provides a definitive answer to whether we are multi-config @@ -744,36 +746,6 @@ if(BUILD_TESTING) ADD_LINK_FLAGS_TEST(mod_flags_config dll_flags_config) ADD_LINK_FLAGS_TEST(exe_flags_config mod_flags_config) - # If we are running right now with a Unix Makefiles or Ninja based generator, - # build the "Simple" test with the ExtraGenerators, if available - # This doesn't test whether the generated project files work (unfortunately), - # mainly it tests that cmake doesn't crash when generating these project files. - if(CMAKE_GENERATOR MATCHES "^(Unix Makefiles|Ninja)$" - AND NOT "${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") - foreach( - extraGenerator - IN ITEMS - "CodeBlocks" - "CodeLite" - "Eclipse CDT4" - "Kate" - "Sublime Text 2" - ) - string(REPLACE " " "" extraGeneratorTestName "Simple_${extraGenerator}Generator") - add_test(${extraGeneratorTestName} ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/Simple" - "${CMake_BINARY_DIR}/Tests/${extraGeneratorTestName}" - --build-two-config - --build-generator "${extraGenerator} - ${CMAKE_GENERATOR}" - --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" - --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" - --build-project Simple - --test-command Simple) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${extraGeneratorTestName}") - endforeach() - endif() - # test for correct sub-project generation # not implemented in Xcode or Ninja if(NOT CMAKE_GENERATOR MATCHES "Xcode|Ninja") diff --git a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt index c7e3105..e6ed559 100644 --- a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt index 2784e3b..24a0a86 100644 --- a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt @@ -9,7 +9,7 @@ project(CheckCXXSymbolExists CXX) -cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/../CheckSymbolExists") diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt index 2e5d8d3..0c76158 100644 --- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CheckLanguage NONE) include(CheckLanguage) diff --git a/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt b/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt index 4cbccd3..6ecd194 100644 --- a/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CheckStructHasMember) diff --git a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt index 3d65b7a..b6ed9e9 100644 --- a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt @@ -9,7 +9,7 @@ project(CheckSymbolExists C) -cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt index 8f13787..18a1ff6 100644 --- a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt +++ b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CompilerIdOBJC OBJC) foreach(v diff --git a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt index 8f41db0..76c1e4b 100644 --- a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt +++ b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CompilerIdOBJCXX OBJCXX) foreach(v diff --git a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt index d66eb06..77dadcf 100644 --- a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt +++ b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(LinkInterfaceLoop C) # Add a shared library that incorrectly names itself as a diff --git a/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt b/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt index 9f30c7d..6646825 100644 --- a/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt +++ b/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) if (NOT MAJOR_TEST_MODULE OR NOT MAJOR_TEST_VERSION) message(FATAL_ERROR "test selection variables not set up") diff --git a/Tests/CMakeOnly/TargetScope/CMakeLists.txt b/Tests/CMakeOnly/TargetScope/CMakeLists.txt index faf2250..3bcbb00 100644 --- a/Tests/CMakeOnly/TargetScope/CMakeLists.txt +++ b/Tests/CMakeOnly/TargetScope/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(TargetScope NONE) add_subdirectory(Sub) diff --git a/Tests/CMakeOnly/find_library/CMakeLists.txt b/Tests/CMakeOnly/find_library/CMakeLists.txt index b23d5e2..2d487e3 100644 --- a/Tests/CMakeOnly/find_library/CMakeLists.txt +++ b/Tests/CMakeOnly/find_library/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(FindLibraryTest NONE) set(CMAKE_FIND_DEBUG_MODE 1) diff --git a/Tests/CMakeOnly/find_path/CMakeLists.txt b/Tests/CMakeOnly/find_path/CMakeLists.txt index bf4e350..7cc08ad 100644 --- a/Tests/CMakeOnly/find_path/CMakeLists.txt +++ b/Tests/CMakeOnly/find_path/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(FindPathTest NONE) set(CMAKE_FIND_DEBUG_MODE 1) diff --git a/Tests/CMakeTests/EndStuffTestScript.cmake b/Tests/CMakeTests/EndStuffTestScript.cmake index e0d826d..bd89246 100644 --- a/Tests/CMakeTests/EndStuffTestScript.cmake +++ b/Tests/CMakeTests/EndStuffTestScript.cmake @@ -22,7 +22,7 @@ elseif(testname STREQUAL bad_endfunction) # fail do_end("endfunction()\n") elseif(testname STREQUAL bad_endif) # fail - do_end("cmake_minimum_required(VERSION 2.8.12)\nendif()\n") + do_end("cmake_minimum_required(VERSION 3.5)\nendif()\n") elseif(testname STREQUAL endif_low_min_version) # fail do_end("cmake_minimum_required(VERSION 1.2)\nendif()\n") diff --git a/Tests/COnly/CMakeLists.txt b/Tests/COnly/CMakeLists.txt index 1c24017..728ec5b 100644 --- a/Tests/COnly/CMakeLists.txt +++ b/Tests/COnly/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project (COnly C) set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix") @@ -7,11 +7,5 @@ add_library(testc1 STATIC libc1.c) add_library(testc2 SHARED libc2.c) add_executable (COnly conly.c foo.c foo.h) target_link_libraries(COnly testc1 testc2) -if(MSVC_VERSION AND NOT CMAKE_C_COMPILER_ID STREQUAL Clang OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC") - set_target_properties(COnly PROPERTIES - LINK_FLAGS " /NODEFAULTLIB:\"libcdg.lib\" /NODEFAULTLIB:\"libcmtg.lib\" /NODEFAULTLIB:\"foomsvcrt.lib\" /NODEFAULTLIB:\"libbar.lib\" /NODEFAULTLIB:\"libfooba.lib\"") -endif() -string(ASCII 35 32 67 77 97 107 101 ASCII_STRING) -message(STATUS "String: ${ASCII_STRING}") add_library(testCModule MODULE testCModule.c) diff --git a/Tests/CPackComponents/CMakeLists.txt b/Tests/CPackComponents/CMakeLists.txt index c1b348e..a886b3d 100644 --- a/Tests/CPackComponents/CMakeLists.txt +++ b/Tests/CPackComponents/CMakeLists.txt @@ -4,7 +4,7 @@ # application (mylibapp). We create a binary installer that allows # users to select which pieces will be installed: the example # application, the library binaries, and/or the header file. -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CPackComponents) # Create the mylib library diff --git a/Tests/CPackTestAllGenerators/CMakeLists.txt b/Tests/CPackTestAllGenerators/CMakeLists.txt index 95daabf..e7fed3b 100644 --- a/Tests/CPackTestAllGenerators/CMakeLists.txt +++ b/Tests/CPackTestAllGenerators/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CPackTestAllGenerators) add_subdirectory(../CTestTest/SmallAndFast SmallAndFast) install(FILES RunCPack.cmake DESTINATION .) diff --git a/Tests/CPackWiXGenerator/CMakeLists.txt b/Tests/CPackWiXGenerator/CMakeLists.txt index 2249d70..33fdc5e 100644 --- a/Tests/CPackWiXGenerator/CMakeLists.txt +++ b/Tests/CPackWiXGenerator/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CPackWiXGenerator) diff --git a/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt b/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt index ce6fac4..79e968a 100644 --- a/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt +++ b/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(TestProject CXX) diff --git a/Tests/CTestCoverageCollectGCOV/test.cmake.in b/Tests/CTestCoverageCollectGCOV/test.cmake.in index 7c7a3e5..aaf3070 100644 --- a/Tests/CTestCoverageCollectGCOV/test.cmake.in +++ b/Tests/CTestCoverageCollectGCOV/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestCoverageCollectGCOV/TestProject") set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestCoverageCollectGCOV/TestProject") set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") diff --git a/Tests/CTestLimitDashJ/CMakeLists.txt b/Tests/CTestLimitDashJ/CMakeLists.txt index d04b3ad..5bb7369 100644 --- a/Tests/CTestLimitDashJ/CMakeLists.txt +++ b/Tests/CTestLimitDashJ/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CTestLimitDashJ NONE) # This file demonstrates https://gitlab.kitware.com/cmake/cmake/-/issues/12904 diff --git a/Tests/CTestTest/SmallAndFast/CMakeLists.txt b/Tests/CTestTest/SmallAndFast/CMakeLists.txt index 06cbafd..d5b3b61 100644 --- a/Tests/CTestTest/SmallAndFast/CMakeLists.txt +++ b/Tests/CTestTest/SmallAndFast/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(SmallAndFast) include(CTest) diff --git a/Tests/CTestTest2/test.cmake.in b/Tests/CTestTest2/test.cmake.in index d5d4d2f..4f4f6cf 100644 --- a/Tests/CTestTest2/test.cmake.in +++ b/Tests/CTestTest2/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestBadExe/test.cmake.in b/Tests/CTestTestBadExe/test.cmake.in index dd180f0..e46f71b 100644 --- a/Tests/CTestTestBadExe/test.cmake.in +++ b/Tests/CTestTestBadExe/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestBadGenerator/test.cmake.in b/Tests/CTestTestBadGenerator/test.cmake.in index ae6d0b5..34003b4 100644 --- a/Tests/CTestTestBadGenerator/test.cmake.in +++ b/Tests/CTestTestBadGenerator/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestChecksum/test.cmake.in b/Tests/CTestTestChecksum/test.cmake.in index 3bac0e0..916bbbb 100644 --- a/Tests/CTestTestChecksum/test.cmake.in +++ b/Tests/CTestTestChecksum/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestCostSerial/test.cmake.in b/Tests/CTestTestCostSerial/test.cmake.in index 1c46d4c..0df9f37 100644 --- a/Tests/CTestTestCostSerial/test.cmake.in +++ b/Tests/CTestTestCostSerial/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestCrash/CMakeLists.txt b/Tests/CTestTestCrash/CMakeLists.txt index 663d2e4..c7e5b91 100644 --- a/Tests/CTestTestCrash/CMakeLists.txt +++ b/Tests/CTestTestCrash/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestCrash) include(CTest) diff --git a/Tests/CTestTestCrash/test.cmake.in b/Tests/CTestTestCrash/test.cmake.in index 916d4e9..34c9f3e 100644 --- a/Tests/CTestTestCrash/test.cmake.in +++ b/Tests/CTestTestCrash/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestCycle/CMakeLists.txt b/Tests/CTestTestCycle/CMakeLists.txt index 19f4dd7..4093111 100644 --- a/Tests/CTestTestCycle/CMakeLists.txt +++ b/Tests/CTestTestCycle/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestCycle) include(CTest) diff --git a/Tests/CTestTestCycle/test.cmake.in b/Tests/CTestTestCycle/test.cmake.in index 507d46b..78b0ebb 100644 --- a/Tests/CTestTestCycle/test.cmake.in +++ b/Tests/CTestTestCycle/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestDepends/CMakeLists.txt b/Tests/CTestTestDepends/CMakeLists.txt index 462ad8c..5a011d0 100644 --- a/Tests/CTestTestDepends/CMakeLists.txt +++ b/Tests/CTestTestDepends/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestDepends) include(CTest) diff --git a/Tests/CTestTestDepends/test.cmake.in b/Tests/CTestTestDepends/test.cmake.in index 11bc92a..ea01297 100644 --- a/Tests/CTestTestDepends/test.cmake.in +++ b/Tests/CTestTestDepends/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in b/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in index 8eb808f..3aed1ab 100644 --- a/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in +++ b/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) set(CTEST_RUN_CURRENT_SCRIPT 0) diff --git a/Tests/CTestTestFailure/CMakeLists.txt b/Tests/CTestTestFailure/CMakeLists.txt index db14b3d..b6c1e7a 100644 --- a/Tests/CTestTestFailure/CMakeLists.txt +++ b/Tests/CTestTestFailure/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestFailure) include(CTest) diff --git a/Tests/CTestTestFailure/testNoBuild.cmake.in b/Tests/CTestTestFailure/testNoBuild.cmake.in index 47d254f..505916e 100644 --- a/Tests/CTestTestFailure/testNoBuild.cmake.in +++ b/Tests/CTestTestFailure/testNoBuild.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestFailure/testNoExe.cmake.in b/Tests/CTestTestFailure/testNoExe.cmake.in index 8496c80..e3d7742 100644 --- a/Tests/CTestTestFailure/testNoExe.cmake.in +++ b/Tests/CTestTestFailure/testNoExe.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestFdSetSize/test.cmake.in b/Tests/CTestTestFdSetSize/test.cmake.in index bfe4459..73b2cfa 100644 --- a/Tests/CTestTestFdSetSize/test.cmake.in +++ b/Tests/CTestTestFdSetSize/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.10) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt b/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt index 7376a40..24da9ca 100644 --- a/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt +++ b/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(launcher_compiler_test_project) diff --git a/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt b/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt index b31f587..72764ca 100644 --- a/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt +++ b/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(launcher_custom_command_test_project) diff --git a/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt b/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt index 38980aa..7bf0362 100644 --- a/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt +++ b/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(launcher_linker_test_project) diff --git a/Tests/CTestTestLaunchers/test.cmake.in b/Tests/CTestTestLaunchers/test.cmake.in index 2db1ddd..c3edfd5 100644 --- a/Tests/CTestTestLaunchers/test.cmake.in +++ b/Tests/CTestTestLaunchers/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) set(TEST_SUCCESS TRUE) diff --git a/Tests/CTestTestMissingDependsExe/CMakeLists.txt b/Tests/CTestTestMissingDependsExe/CMakeLists.txt index 9826da6..07df194 100644 --- a/Tests/CTestTestMissingDependsExe/CMakeLists.txt +++ b/Tests/CTestTestMissingDependsExe/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CTestTestMissingDependsExe) diff --git a/Tests/CTestTestParallel/CMakeLists.txt b/Tests/CTestTestParallel/CMakeLists.txt index 819fee4..7527202 100644 --- a/Tests/CTestTestParallel/CMakeLists.txt +++ b/Tests/CTestTestParallel/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestParallel) include(CTest) diff --git a/Tests/CTestTestParallel/test.cmake.in b/Tests/CTestTestParallel/test.cmake.in index 517db72..d60d16f 100644 --- a/Tests/CTestTestParallel/test.cmake.in +++ b/Tests/CTestTestParallel/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestResourceLock/CMakeLists.txt b/Tests/CTestTestResourceLock/CMakeLists.txt index 4bc4366..683aba5 100644 --- a/Tests/CTestTestResourceLock/CMakeLists.txt +++ b/Tests/CTestTestResourceLock/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestResourceLock) include(CTest) diff --git a/Tests/CTestTestResourceLock/test.cmake.in b/Tests/CTestTestResourceLock/test.cmake.in index 826226d..dab26fc 100644 --- a/Tests/CTestTestResourceLock/test.cmake.in +++ b/Tests/CTestTestResourceLock/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestScheduler/CMakeLists.txt b/Tests/CTestTestScheduler/CMakeLists.txt index a3f0f27..91d565d 100644 --- a/Tests/CTestTestScheduler/CMakeLists.txt +++ b/Tests/CTestTestScheduler/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project (CTestTestScheduler) include (CTest) diff --git a/Tests/CTestTestScheduler/test.cmake.in b/Tests/CTestTestScheduler/test.cmake.in index 5dcfb63..3b03a7c 100644 --- a/Tests/CTestTestScheduler/test.cmake.in +++ b/Tests/CTestTestScheduler/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestSerialInDepends/CMakeLists.txt b/Tests/CTestTestSerialInDepends/CMakeLists.txt index 90e50f9..03ad4b3 100644 --- a/Tests/CTestTestSerialInDepends/CMakeLists.txt +++ b/Tests/CTestTestSerialInDepends/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CTestTestSerialInDepends) diff --git a/Tests/CTestTestSerialOrder/CMakeLists.txt b/Tests/CTestTestSerialOrder/CMakeLists.txt index 69c11fc..d46d80e 100644 --- a/Tests/CTestTestSerialOrder/CMakeLists.txt +++ b/Tests/CTestTestSerialOrder/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CTestTestSerialOrder) diff --git a/Tests/CTestTestSkipReturnCode/CMakeLists.txt b/Tests/CTestTestSkipReturnCode/CMakeLists.txt index 26c4178..1eeeec6 100644 --- a/Tests/CTestTestSkipReturnCode/CMakeLists.txt +++ b/Tests/CTestTestSkipReturnCode/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CTestTestSkipReturnCode) include(CTest) diff --git a/Tests/CTestTestSkipReturnCode/test.cmake.in b/Tests/CTestTestSkipReturnCode/test.cmake.in index 2988d2f..b45e4a6 100644 --- a/Tests/CTestTestSkipReturnCode/test.cmake.in +++ b/Tests/CTestTestSkipReturnCode/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestStopTime/CMakeLists.txt b/Tests/CTestTestStopTime/CMakeLists.txt index 08116e2..4f6e795 100644 --- a/Tests/CTestTestStopTime/CMakeLists.txt +++ b/Tests/CTestTestStopTime/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestStopTime) include(CTest) diff --git a/Tests/CTestTestStopTime/GetDate.cmake b/Tests/CTestTestStopTime/GetDate.cmake index 64a4fb9..f8e40fc 100644 --- a/Tests/CTestTestStopTime/GetDate.cmake +++ b/Tests/CTestTestStopTime/GetDate.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.11) +cmake_minimum_required(VERSION 3.5) macro(GET_DATE) # diff --git a/Tests/CTestTestStopTime/test.cmake.in b/Tests/CTestTestStopTime/test.cmake.in index 3797d40..2d69f1d 100644 --- a/Tests/CTestTestStopTime/test.cmake.in +++ b/Tests/CTestTestStopTime/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestSubdir/CMakeLists.txt b/Tests/CTestTestSubdir/CMakeLists.txt index 87c4604..e6f3209 100644 --- a/Tests/CTestTestSubdir/CMakeLists.txt +++ b/Tests/CTestTestSubdir/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestSubdir) include(CTest) diff --git a/Tests/CTestTestSubdir/test.cmake.in b/Tests/CTestTestSubdir/test.cmake.in index 3b1fb5f..8b8d85e 100644 --- a/Tests/CTestTestSubdir/test.cmake.in +++ b/Tests/CTestTestSubdir/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestTimeout/test.cmake.in b/Tests/CTestTestTimeout/test.cmake.in index ce9c497..9d9e430 100644 --- a/Tests/CTestTestTimeout/test.cmake.in +++ b/Tests/CTestTestTimeout/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestUpload/CMakeLists.txt b/Tests/CTestTestUpload/CMakeLists.txt index 5e02b2f..5bf428d 100644 --- a/Tests/CTestTestUpload/CMakeLists.txt +++ b/Tests/CTestTestUpload/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestUpload) add_executable (Sleep sleep.c) diff --git a/Tests/CTestTestUpload/test.cmake.in b/Tests/CTestTestUpload/test.cmake.in index 74fd1ec..db428e9 100644 --- a/Tests/CTestTestUpload/test.cmake.in +++ b/Tests/CTestTestUpload/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestVerboseOutput/CMakeLists.txt b/Tests/CTestTestVerboseOutput/CMakeLists.txt index 3792385..d44e9fc 100644 --- a/Tests/CTestTestVerboseOutput/CMakeLists.txt +++ b/Tests/CTestTestVerboseOutput/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CTestTestVerboseOutput) include(CTest) diff --git a/Tests/CTestTestVerboseOutput/test.cmake.in b/Tests/CTestTestVerboseOutput/test.cmake.in index 9c9a4dc..b47383a 100644 --- a/Tests/CTestTestVerboseOutput/test.cmake.in +++ b/Tests/CTestTestVerboseOutput/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CTestTestZeroTimeout/CMakeLists.txt b/Tests/CTestTestZeroTimeout/CMakeLists.txt index 2d404c8..51ef807 100644 --- a/Tests/CTestTestZeroTimeout/CMakeLists.txt +++ b/Tests/CTestTestZeroTimeout/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project (CTestTestZeroTimeout) include (CTest) diff --git a/Tests/CTestTestZeroTimeout/test.cmake.in b/Tests/CTestTestZeroTimeout/test.cmake.in index 50dbba0..e0dbbc6 100644 --- a/Tests/CTestTestZeroTimeout/test.cmake.in +++ b/Tests/CTestTestZeroTimeout/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") diff --git a/Tests/CheckCompilerRelatedVariables/CMakeLists.txt b/Tests/CheckCompilerRelatedVariables/CMakeLists.txt index 69fa4b6..9259309 100644 --- a/Tests/CheckCompilerRelatedVariables/CMakeLists.txt +++ b/Tests/CheckCompilerRelatedVariables/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CheckCompilerRelatedVariables) diff --git a/Tests/CheckFortran.cmake b/Tests/CheckFortran.cmake index 1e943a1..850406b 100644 --- a/Tests/CheckFortran.cmake +++ b/Tests/CheckFortran.cmake @@ -7,7 +7,7 @@ if(NOT DEFINED CMAKE_Fortran_COMPILER) message(STATUS ${_desc}) file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CheckFortran) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CheckFortran/CMakeLists.txt" - "cmake_minimum_required(VERSION 2.8.12) + "cmake_minimum_required(VERSION 3.5) project(CheckFortran Fortran) file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\" \"set(CMAKE_Fortran_COMPILER \\\"\${CMAKE_Fortran_COMPILER}\\\")\\n\" diff --git a/Tests/CompileDefinitions/CMakeLists.txt b/Tests/CompileDefinitions/CMakeLists.txt index 8347d5a..cd0a0b0 100644 --- a/Tests/CompileDefinitions/CMakeLists.txt +++ b/Tests/CompileDefinitions/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(CompileDefinitions) # Use compile flags to tell executables which config is built diff --git a/Tests/Contracts/Trilinos/CMakeLists.txt b/Tests/Contracts/Trilinos/CMakeLists.txt index 6cc2d09..e23a643 100644 --- a/Tests/Contracts/Trilinos/CMakeLists.txt +++ b/Tests/Contracts/Trilinos/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(Trilinos) include(ExternalProject) diff --git a/Tests/Contracts/VTK/CMakeLists.txt b/Tests/Contracts/VTK/CMakeLists.txt index 0d36323..aee4dc6 100644 --- a/Tests/Contracts/VTK/CMakeLists.txt +++ b/Tests/Contracts/VTK/CMakeLists.txt @@ -1,6 +1,6 @@ # The VTK external project for CMake # --------------------------------------------------------------------------- -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(VTK) include(ExternalProject) diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt index fa06a94..25df300 100644 --- a/Tests/CustomCommand/CMakeLists.txt +++ b/Tests/CustomCommand/CMakeLists.txt @@ -1,7 +1,7 @@ # # Wrapping # -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project (CustomCommand) add_subdirectory(GeneratedHeader) diff --git a/Tests/CustomCommandByproducts/External/CMakeLists.txt b/Tests/CustomCommandByproducts/External/CMakeLists.txt index feaa12e..81e072b 100644 --- a/Tests/CustomCommandByproducts/External/CMakeLists.txt +++ b/Tests/CustomCommandByproducts/External/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(External C) add_library(ExternalLibrary STATIC ExternalLibrary.c) diff --git a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt index 7697a9b..531690a 100644 --- a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt +++ b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(TestWorkingDir) add_custom_command( diff --git a/Tests/CxxDialect/CMakeLists.txt b/Tests/CxxDialect/CMakeLists.txt index 8c90339..c88641b 100644 --- a/Tests/CxxDialect/CMakeLists.txt +++ b/Tests/CxxDialect/CMakeLists.txt @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) -cmake_policy(SET CMP0025 NEW) +cmake_minimum_required(VERSION 3.5) project(CxxDialect) add_executable(use_typeof use_typeof.cxx) diff --git a/Tests/CxxOnly/CMakeLists.txt b/Tests/CxxOnly/CMakeLists.txt index 09689cb..6cd3a8e 100644 --- a/Tests/CxxOnly/CMakeLists.txt +++ b/Tests/CxxOnly/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple CXX only test case -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project (CxxOnly CXX) set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix") diff --git a/Tests/Dependency/CMakeLists.txt b/Tests/Dependency/CMakeLists.txt index 58d3fb7..cae108e 100644 --- a/Tests/Dependency/CMakeLists.txt +++ b/Tests/Dependency/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project( Dependency ) # to test directories with only one character One was changed to 1 diff --git a/Tests/EmptyDepends/CMakeLists.txt b/Tests/EmptyDepends/CMakeLists.txt index 272eff7..5ba0b49 100644 --- a/Tests/EmptyDepends/CMakeLists.txt +++ b/Tests/EmptyDepends/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(EmptyDepends) include(CTest) diff --git a/Tests/EnforceConfig.cmake.in b/Tests/EnforceConfig.cmake.in index 7722d7d..61be40b 100644 --- a/Tests/EnforceConfig.cmake.in +++ b/Tests/EnforceConfig.cmake.in @@ -36,4 +36,4 @@ unset(ENV{CMAKE_GENERATOR_TOOLSET}) unset(ENV{CMAKE_EXPORT_COMPILE_COMMANDS}) @TEST_HOME_ENV_CODE@ -@TEST_WARN_VS11_CODE@ +@TEST_WARN_VS_CODE@ diff --git a/Tests/ExportImport/Import/try_compile/CMakeLists.txt b/Tests/ExportImport/Import/try_compile/CMakeLists.txt index 813cf06..bb390f9 100644 --- a/Tests/ExportImport/Import/try_compile/CMakeLists.txt +++ b/Tests/ExportImport/Import/try_compile/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) find_package(testLibRequired 2.5 REQUIRED) diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt index 81d31e7..d1ab7ac 100644 --- a/Tests/ExternalProject/CMakeLists.txt +++ b/Tests/ExternalProject/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ExternalProjectTest NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) diff --git a/Tests/ExternalProject/Example/CMakeLists.txt b/Tests/ExternalProject/Example/CMakeLists.txt index c3f2614..b6785a6 100644 --- a/Tests/ExternalProject/Example/CMakeLists.txt +++ b/Tests/ExternalProject/Example/CMakeLists.txt @@ -1,5 +1,5 @@ # This is the canonical simplest ExternalProject example CMakeLists.txt file: -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ExternalProjectExample NONE) include(ExternalProject) diff --git a/Tests/ExternalProjectLocal/CMakeLists.txt b/Tests/ExternalProjectLocal/CMakeLists.txt index 57e8105..2dfc425 100644 --- a/Tests/ExternalProjectLocal/CMakeLists.txt +++ b/Tests/ExternalProjectLocal/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ExternalProjectLocalTest NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) diff --git a/Tests/ExternalProjectUpdate/CMakeLists.txt b/Tests/ExternalProjectUpdate/CMakeLists.txt index 6f8a7b1..1b84ff3 100644 --- a/Tests/ExternalProjectUpdate/CMakeLists.txt +++ b/Tests/ExternalProjectUpdate/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ExternalProjectUpdateTest NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) diff --git a/Tests/FindGTK2/atk/CMakeLists.txt b/Tests/FindGTK2/atk/CMakeLists.txt index 0392d88..b80c6fc 100644 --- a/Tests/FindGTK2/atk/CMakeLists.txt +++ b/Tests/FindGTK2/atk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(atk C) diff --git a/Tests/FindGTK2/atkmm/CMakeLists.txt b/Tests/FindGTK2/atkmm/CMakeLists.txt index ec838de..cea87cd 100644 --- a/Tests/FindGTK2/atkmm/CMakeLists.txt +++ b/Tests/FindGTK2/atkmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(atkmm CXX) diff --git a/Tests/FindGTK2/cairo/CMakeLists.txt b/Tests/FindGTK2/cairo/CMakeLists.txt index 3652ad6..42d371a 100644 --- a/Tests/FindGTK2/cairo/CMakeLists.txt +++ b/Tests/FindGTK2/cairo/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(cairo C) diff --git a/Tests/FindGTK2/cairomm/CMakeLists.txt b/Tests/FindGTK2/cairomm/CMakeLists.txt index cde0f42..5d957ee 100644 --- a/Tests/FindGTK2/cairomm/CMakeLists.txt +++ b/Tests/FindGTK2/cairomm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(cairomm CXX) diff --git a/Tests/FindGTK2/gdk/CMakeLists.txt b/Tests/FindGTK2/gdk/CMakeLists.txt index 35ef337..2b8f533 100644 --- a/Tests/FindGTK2/gdk/CMakeLists.txt +++ b/Tests/FindGTK2/gdk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gdk C) diff --git a/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt b/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt index ea1b05d..3524f06 100644 --- a/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt +++ b/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gdk_pixbuf C) diff --git a/Tests/FindGTK2/gdkmm/CMakeLists.txt b/Tests/FindGTK2/gdkmm/CMakeLists.txt index 72fc6f4..be1cceb 100644 --- a/Tests/FindGTK2/gdkmm/CMakeLists.txt +++ b/Tests/FindGTK2/gdkmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gdkmm CXX) diff --git a/Tests/FindGTK2/gio/CMakeLists.txt b/Tests/FindGTK2/gio/CMakeLists.txt index 4835afa..b420f48 100644 --- a/Tests/FindGTK2/gio/CMakeLists.txt +++ b/Tests/FindGTK2/gio/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gio C) diff --git a/Tests/FindGTK2/giomm/CMakeLists.txt b/Tests/FindGTK2/giomm/CMakeLists.txt index b639979..bae34ff 100644 --- a/Tests/FindGTK2/giomm/CMakeLists.txt +++ b/Tests/FindGTK2/giomm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(giomm CXX) diff --git a/Tests/FindGTK2/glib/CMakeLists.txt b/Tests/FindGTK2/glib/CMakeLists.txt index 536fc67..8efde3d 100644 --- a/Tests/FindGTK2/glib/CMakeLists.txt +++ b/Tests/FindGTK2/glib/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(glib C) diff --git a/Tests/FindGTK2/glibmm/CMakeLists.txt b/Tests/FindGTK2/glibmm/CMakeLists.txt index 25d5518..f0785dc 100644 --- a/Tests/FindGTK2/glibmm/CMakeLists.txt +++ b/Tests/FindGTK2/glibmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(glibmm CXX) diff --git a/Tests/FindGTK2/gmodule/CMakeLists.txt b/Tests/FindGTK2/gmodule/CMakeLists.txt index 2bfb81e..9c686a6 100644 --- a/Tests/FindGTK2/gmodule/CMakeLists.txt +++ b/Tests/FindGTK2/gmodule/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gmodule C) diff --git a/Tests/FindGTK2/gobject/CMakeLists.txt b/Tests/FindGTK2/gobject/CMakeLists.txt index 11520f8..83d9546 100644 --- a/Tests/FindGTK2/gobject/CMakeLists.txt +++ b/Tests/FindGTK2/gobject/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gobject C) diff --git a/Tests/FindGTK2/gthread/CMakeLists.txt b/Tests/FindGTK2/gthread/CMakeLists.txt index 5ecfd9b..d97585c 100644 --- a/Tests/FindGTK2/gthread/CMakeLists.txt +++ b/Tests/FindGTK2/gthread/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gthread C) diff --git a/Tests/FindGTK2/gtk/CMakeLists.txt b/Tests/FindGTK2/gtk/CMakeLists.txt index 2c67619..2b5a56d 100644 --- a/Tests/FindGTK2/gtk/CMakeLists.txt +++ b/Tests/FindGTK2/gtk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gtk C) diff --git a/Tests/FindGTK2/gtkmm/CMakeLists.txt b/Tests/FindGTK2/gtkmm/CMakeLists.txt index 3375a55..179f5db 100644 --- a/Tests/FindGTK2/gtkmm/CMakeLists.txt +++ b/Tests/FindGTK2/gtkmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(gtkmm CXX) diff --git a/Tests/FindGTK2/pango/CMakeLists.txt b/Tests/FindGTK2/pango/CMakeLists.txt index bd6b13a..e9d6b9d 100644 --- a/Tests/FindGTK2/pango/CMakeLists.txt +++ b/Tests/FindGTK2/pango/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(pango C) diff --git a/Tests/FindGTK2/pangocairo/CMakeLists.txt b/Tests/FindGTK2/pangocairo/CMakeLists.txt index 157b9c2..bbb1f27 100644 --- a/Tests/FindGTK2/pangocairo/CMakeLists.txt +++ b/Tests/FindGTK2/pangocairo/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(pangocairo C) diff --git a/Tests/FindGTK2/pangoft2/CMakeLists.txt b/Tests/FindGTK2/pangoft2/CMakeLists.txt index 76966e7..801629c 100644 --- a/Tests/FindGTK2/pangoft2/CMakeLists.txt +++ b/Tests/FindGTK2/pangoft2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(pangoft2 C) diff --git a/Tests/FindGTK2/pangomm/CMakeLists.txt b/Tests/FindGTK2/pangomm/CMakeLists.txt index 0bb49e2..853a1dd 100644 --- a/Tests/FindGTK2/pangomm/CMakeLists.txt +++ b/Tests/FindGTK2/pangomm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(pangomm CXX) diff --git a/Tests/FindGTK2/pangoxft/CMakeLists.txt b/Tests/FindGTK2/pangoxft/CMakeLists.txt index 7051d35..f267d6c 100644 --- a/Tests/FindGTK2/pangoxft/CMakeLists.txt +++ b/Tests/FindGTK2/pangoxft/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(pangoxft C) diff --git a/Tests/FindGTK2/sigc++/CMakeLists.txt b/Tests/FindGTK2/sigc++/CMakeLists.txt index 9c1fff7..f09ea66 100644 --- a/Tests/FindGTK2/sigc++/CMakeLists.txt +++ b/Tests/FindGTK2/sigc++/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(sigc++ CXX) diff --git a/Tests/FindMatlab/basic_checks/CMakeLists.txt b/Tests/FindMatlab/basic_checks/CMakeLists.txt index c0c752a..e9b696c 100644 --- a/Tests/FindMatlab/basic_checks/CMakeLists.txt +++ b/Tests/FindMatlab/basic_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(basic_checks) diff --git a/Tests/FindMatlab/components_checks/CMakeLists.txt b/Tests/FindMatlab/components_checks/CMakeLists.txt index f5d4880..efb99ae 100644 --- a/Tests/FindMatlab/components_checks/CMakeLists.txt +++ b/Tests/FindMatlab/components_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(component_checks) diff --git a/Tests/FindMatlab/failure_reports/CMakeLists.txt b/Tests/FindMatlab/failure_reports/CMakeLists.txt index 4b092cd..45e48d7 100644 --- a/Tests/FindMatlab/failure_reports/CMakeLists.txt +++ b/Tests/FindMatlab/failure_reports/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(failure_reports) diff --git a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt index bceeba1..58db0ec 100644 --- a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt +++ b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(no_implicit_links_checks) diff --git a/Tests/FindMatlab/r2018a_check/CMakeLists.txt b/Tests/FindMatlab/r2018a_check/CMakeLists.txt index c732be1..8b21888 100644 --- a/Tests/FindMatlab/r2018a_check/CMakeLists.txt +++ b/Tests/FindMatlab/r2018a_check/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(r2018a_checks) diff --git a/Tests/FindMatlab/targets_checks/CMakeLists.txt b/Tests/FindMatlab/targets_checks/CMakeLists.txt index 4af7cc3..c709bbd 100644 --- a/Tests/FindMatlab/targets_checks/CMakeLists.txt +++ b/Tests/FindMatlab/targets_checks/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(targets_checks) diff --git a/Tests/FindMatlab/versions_checks/CMakeLists.txt b/Tests/FindMatlab/versions_checks/CMakeLists.txt index d015730..f203f4b 100644 --- a/Tests/FindMatlab/versions_checks/CMakeLists.txt +++ b/Tests/FindMatlab/versions_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) enable_testing() project(versions_checks) diff --git a/Tests/FindPatch/CMakeLists.txt b/Tests/FindPatch/CMakeLists.txt index 541f5bd..65b778b 100644 --- a/Tests/FindPatch/CMakeLists.txt +++ b/Tests/FindPatch/CMakeLists.txt @@ -4,5 +4,6 @@ add_test(NAME FindPatch.Test COMMAND "${CMake_SOURCE_DIR}/Tests/FindPatch/Test" "${CMake_BINARY_DIR}/Tests/FindPatch/Test" ${build_generator_args} + --build-project TestFindPatch --build-options ${build_options} ) diff --git a/Tests/FindX11/Test/CMakeLists.txt b/Tests/FindX11/Test/CMakeLists.txt index 18a73a3..e39ffb1 100644 --- a/Tests/FindX11/Test/CMakeLists.txt +++ b/Tests/FindX11/Test/CMakeLists.txt @@ -32,10 +32,13 @@ test_x11_component(x11_components Xau) test_x11_component(x11_components Xaw) test_x11_component(x11_components xcb) test_x11_component(x11_components X11_xcb) +test_x11_component(x11_components xcb_cursor) test_x11_component(x11_components xcb_icccm) test_x11_component(x11_components xcb_randr) +test_x11_component(x11_components xcb_shape) test_x11_component(x11_components xcb_util) test_x11_component(x11_components xcb_xfixes) +test_x11_component(x11_components xcb_xrm) test_x11_component(x11_components xcb_xtest) test_x11_component(x11_components xcb_keysyms) test_x11_component(x11_components xcb_xkb) @@ -76,10 +79,13 @@ foreach(lib Xaw xcb X11_xcb + xcb_cursor xcb_icccm xcb_randr + xcb_shape xcb_util xcb_xfixes + xcb_xrm Xcomposite Xdamage Xdmcp diff --git a/Tests/FindX11/Test/main.c b/Tests/FindX11/Test/main.c index 653a2be..5240de0 100644 --- a/Tests/FindX11/Test/main.c +++ b/Tests/FindX11/Test/main.c @@ -336,6 +336,22 @@ static void test_xcb(void) xcb_disconnect(connection); } +# ifdef HAVE_xcb_cursor +# include <xcb/xcb_cursor.h> + +static void test_xcb_cursor(void) +{ + int screen_nbr; + xcb_connection_t* connection = xcb_connect(NULL, &screen_nbr); + xcb_screen_t* screen = xcb_aux_get_screen(conn, screen_nbr); + xcb_cursor_context_t* ctx; + xcb_cursor_context_new(connection, screen, &ctx); + xcb_cursor_context_free(ctx); + xcb_disconnect(connection); +} + +# endif + # ifdef HAVE_xcb_randr # include <xcb/randr.h> @@ -350,6 +366,20 @@ static void test_xcb_randr(void) # endif +# ifdef HAVE_xcb_shape +# include <xcb/shape.h> + +static void test_xcb_shape(void) +{ + int screen_nbr; + xcb_connection_t* connection = xcb_connect(NULL, &screen_nbr); + xcb_shape_query_version_cookie_t cookie = + xcb_shape_query_version(connection); + xcb_disconnect(connection); +} + +# endif + # ifdef HAVE_xcb_util # include <xcb/xcb_aux.h> @@ -376,6 +406,20 @@ static void test_xcb_xfixes(void) # endif +# ifdef HAVE_xcb_xrm +# include <xcb/xcb_xrm.h> + +static void test_xcb_xrm(void) +{ + int screen_nbr; + xcb_connection_t* connection = xcb_connect(NULL, &screen_nbr); + xcb_xrm_database_t* db = xcb_xrm_database_from_default(connection); + xcb_xrm_database_free(db); + xcb_disconnect(connection); +} + +# endif + # ifdef HAVE_xcb_xtest # include <xcb/xtest.h> @@ -496,15 +540,24 @@ int main(int argc, char* argv[]) #ifdef HAVE_xcb test_xcb, #endif -#ifdef HAVE_xcb_util +#ifdef HAVE_xcb_cursor + test_xcb_cursor, +#endif +#ifdef HAVE_xcb_randr test_xcb_randr, #endif +#ifdef HAVE_xcb_shape + test_xcb_shape, +#endif #ifdef HAVE_xcb_util test_xcb_util, #endif #ifdef HAVE_xcb_xfixes test_xcb_xfixes, #endif +#ifdef HAVE_xcb_xrm + test_xcb_xrm, +#endif NULL, }; diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt index 0fede25..30ab16b 100644 --- a/Tests/Fortran/CMakeLists.txt +++ b/Tests/Fortran/CMakeLists.txt @@ -49,7 +49,7 @@ function(test_fortran_c_interface_module) FortranCInterface_VERIFY() FortranCInterface_VERIFY(CXX) if(CMAKE_Fortran_COMPILER_SUPPORTS_F90) - if(NOT CMAKE_Fortran_COMPILER_ID MATCHES "SunPro|PathScale|Absoft|Fujitsu") + if(NOT CMAKE_Fortran_COMPILER_ID MATCHES "SunPro|PathScale|Absoft|Fujitsu|LCC") set(module_expected 1) endif() if(FortranCInterface_MODULE_FOUND OR module_expected) diff --git a/Tests/FortranC/CMakeLists.txt b/Tests/FortranC/CMakeLists.txt index 1403aa0..f5e056b 100644 --- a/Tests/FortranC/CMakeLists.txt +++ b/Tests/FortranC/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(FortranC C Fortran) # Skip this test for compilers not known to be compatible. diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt index fc71a18..ed2a440 100644 --- a/Tests/FortranOnly/CMakeLists.txt +++ b/Tests/FortranOnly/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(FortranOnly Fortran) message("CTEST_FULL_OUTPUT ") @@ -152,13 +152,16 @@ if(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON AND set_property(SOURCE preprocess3.f PROPERTY Fortran_PREPROCESS ON) endif() -# Test that neither the compiler nor CMake performs unnecessary preprocessing. -add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f) -target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense) -set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF) -add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f) -target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense) -set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF) +# LCC < 1.24 has no way to disable Fortran preprocessor +if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LCC" OR CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.00") + # Test that neither the compiler nor CMake performs unnecessary preprocessing. + add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f) + target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense) + set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF) + add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f) + target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense) + set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF) +endif() # Test that we can explicitly not preprocess a target or source. # This will not work on certain compilers due to either missing a diff --git a/Tests/Framework/CMakeLists.txt b/Tests/Framework/CMakeLists.txt index 287be94..629deeb 100644 --- a/Tests/Framework/CMakeLists.txt +++ b/Tests/Framework/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(Framework) add_library(foo SHARED diff --git a/Tests/FunctionTest/CMakeLists.txt b/Tests/FunctionTest/CMakeLists.txt index 0660d0f..a5a8b11 100644 --- a/Tests/FunctionTest/CMakeLists.txt +++ b/Tests/FunctionTest/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project (FunctionTest) function(FAILED testname) diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 3fb53d1..ef115e6 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -200,6 +200,21 @@ set_property(TARGET importedFallback PROPERTY IMPORTED_LOCATION fallback_loc) set_property(TARGET importedFallback PROPERTY MAP_IMPORTED_CONFIG_DEBUG "" DEBUG) set_property(TARGET importedFallback PROPERTY MAP_IMPORTED_CONFIG_RELEASE "") +add_library(importedFallback2 SHARED IMPORTED) +set_property(TARGET importedFallback2 PROPERTY ENABLE_EXPORTS TRUE) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION_NOCONFIG noconfig_loc) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_NOCONFIG noconfig_imp) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION_DEBUG debug_loc) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_DEBUG debug_imp) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION_RELEASE release_loc) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_RELEASE release_imp) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION fallback_loc) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB fallback_imp) +set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_SPECIAL special_imp) +set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_NOCONFIG SPECIAL "") +set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_DEBUG SPECIAL "") +set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_RELEASE SPECIAL "") + add_library(importedFallback_genex STATIC IMPORTED) set_property(TARGET importedFallback_genex PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_property(TARGET importedFallback_genex PROPERTY IMPORTED_LOCATION_RELEASE release_loc) @@ -217,6 +232,7 @@ add_custom_target(check-part3 ALL -Dconfig=$<CONFIGURATION> -Dtest_imported_includes=$<TARGET_PROPERTY:imported4,INCLUDE_DIRECTORIES> -Dtest_imported_fallback=$<STREQUAL:$<TARGET_FILE_NAME:importedFallback>,fallback_loc> + -Dtest_imported_fallback2=$<IF:$<OR:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${CMAKE_TAPI}>>>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,special_imp>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,fallback_loc>> -Dtest_imported_fallback_genex=$<STREQUAL:$<TARGET_PROPERTY:importedFallback_genex,INTERFACE_COMPILE_DEFINITIONS>,FOOBAR=1> -Dtest_alias_file_exe=$<STREQUAL:$<TARGET_FILE:Alias::SomeExe>,$<TARGET_FILE:someexe>> -Dtest_alias_file_lib=$<STREQUAL:$<TARGET_FILE:Alias::SomeLib>,$<TARGET_FILE:empty1>> diff --git a/Tests/GeneratorExpression/check-part3.cmake b/Tests/GeneratorExpression/check-part3.cmake index e1b1f93..7bb0d85 100644 --- a/Tests/GeneratorExpression/check-part3.cmake +++ b/Tests/GeneratorExpression/check-part3.cmake @@ -19,6 +19,7 @@ else() endif() check(test_imported_fallback "1") +check(test_imported_fallback2 "1") check(test_imported_fallback_genex "1") check(test_alias_file_exe "1") diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt index e4973b0..d4720be 100644 --- a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(SystemIncludeDirectories) diff --git a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt index 3b994a2..6812267 100644 --- a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(TargetIncludeDirectories) diff --git a/Tests/InterfaceLibrary/CMakeLists.txt b/Tests/InterfaceLibrary/CMakeLists.txt index a302c7c..d57eccc 100644 --- a/Tests/InterfaceLibrary/CMakeLists.txt +++ b/Tests/InterfaceLibrary/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(InterfaceLibrary) diff --git a/Tests/JCTest/CMakeLists.txt b/Tests/JCTest/CMakeLists.txt index b120640..adbdf9a 100644 --- a/Tests/JCTest/CMakeLists.txt +++ b/Tests/JCTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(TestTime) enable_testing() add_executable(TestTime TestTime.cxx) diff --git a/Tests/Java/CMakeLists.txt b/Tests/Java/CMakeLists.txt index 1d8d7ac..c1c6817 100644 --- a/Tests/Java/CMakeLists.txt +++ b/Tests/Java/CMakeLists.txt @@ -1,6 +1,6 @@ project(hello Java) -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) set(CMAKE_VERBOSE_MAKEFILE 1) include(CTest) diff --git a/Tests/JavaJavah/CMakeLists.txt b/Tests/JavaJavah/CMakeLists.txt index b56cc21..06fc06a 100644 --- a/Tests/JavaJavah/CMakeLists.txt +++ b/Tests/JavaJavah/CMakeLists.txt @@ -1,6 +1,6 @@ project(helloJavah Java CXX) -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) set(CMAKE_VERBOSE_MAKEFILE 1) include(CTest) diff --git a/Tests/JavaNativeHeaders/CMakeLists.txt b/Tests/JavaNativeHeaders/CMakeLists.txt index 2471e01..8a2e460 100644 --- a/Tests/JavaNativeHeaders/CMakeLists.txt +++ b/Tests/JavaNativeHeaders/CMakeLists.txt @@ -1,6 +1,6 @@ project(helloJavaNativeHeaders Java CXX) -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) set(CMAKE_VERBOSE_MAKEFILE 1) include (CTest) diff --git a/Tests/LinkDirectory/CMakeLists.txt b/Tests/LinkDirectory/CMakeLists.txt index d9a8ac8..2c2e488 100644 --- a/Tests/LinkDirectory/CMakeLists.txt +++ b/Tests/LinkDirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(LinkDirectory C) # Put the subproject source tree in our build tree so it can refer to diff --git a/Tests/LinkDirectory/External/CMakeLists.txt b/Tests/LinkDirectory/External/CMakeLists.txt index e222929..431fd89 100644 --- a/Tests/LinkDirectory/External/CMakeLists.txt +++ b/Tests/LinkDirectory/External/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(LinkDirectoryExternal C) diff --git a/Tests/LinkFlags/CMakeLists.txt b/Tests/LinkFlags/CMakeLists.txt index 31ff9b5..c25c4b2 100644 --- a/Tests/LinkFlags/CMakeLists.txt +++ b/Tests/LinkFlags/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(LinkFlags C) string(TOUPPER "${TEST_CONFIG}" TEST_CONFIG_UPPER) @@ -32,6 +32,11 @@ add_executable(LinkFlags_exe_config LinkFlagsExe.c) set_property(TARGET LinkFlags_exe_config PROPERTY LINK_FLAGS_${TEST_CONFIG_UPPER} ${pre}BADFLAG_${TEST_CONFIG}${obj}) add_executable(LinkFlags LinkFlags.c) +if("x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC") + set_property(TARGET LinkFlags PROPERTY + LINK_FLAGS "/NODEFAULTLIB:\"libcdg.lib\" /NODEFAULTLIB:\"libcmtg.lib\" /NODEFAULTLIB:\"foomsvcrt.lib\" /NODEFAULTLIB:\"libbar.lib\" /NODEFAULTLIB:\"libfooba.lib\"" + ) +endif() add_subdirectory(LinkerFlags) add_subdirectory(LinkerFlagsConfig) diff --git a/Tests/LoadCommand/CMakeCommands/CMakeLists.txt b/Tests/LoadCommand/CMakeCommands/CMakeLists.txt index cafa99b..3313c57 100644 --- a/Tests/LoadCommand/CMakeCommands/CMakeLists.txt +++ b/Tests/LoadCommand/CMakeCommands/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CMAKE_LOADED_COMMANDS) if (MUDSLIDE_TYPE MATCHES MUCHO) diff --git a/Tests/LoadCommand/CMakeLists.txt b/Tests/LoadCommand/CMakeLists.txt index e1c4998..c0dc247 100644 --- a/Tests/LoadCommand/CMakeLists.txt +++ b/Tests/LoadCommand/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) +cmake_policy(SET CMP0031 OLD) # testing the old behavior project(LoadCommand) # set a definition diff --git a/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt b/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt index dc029a4..74a1f55 100644 --- a/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt +++ b/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(CMAKE_LOADED_COMMANDS) if (MUDSLIDE_TYPE MATCHES MUCHO) diff --git a/Tests/LoadCommandOneConfig/CMakeLists.txt b/Tests/LoadCommandOneConfig/CMakeLists.txt index fef4bb7..35dc0fe 100644 --- a/Tests/LoadCommandOneConfig/CMakeLists.txt +++ b/Tests/LoadCommandOneConfig/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) +cmake_policy(SET CMP0031 OLD) # testing the old behavior project(LoadCommand) # set a definition diff --git a/Tests/MFC/CMakeLists.txt b/Tests/MFC/CMakeLists.txt index d17b955..3f78a81 100644 --- a/Tests/MFC/CMakeLists.txt +++ b/Tests/MFC/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(mfc_driver) include(CTest) diff --git a/Tests/MFC/CMakeLists.txt.in b/Tests/MFC/CMakeLists.txt.in index a600c63..bae3d2f 100644 --- a/Tests/MFC/CMakeLists.txt.in +++ b/Tests/MFC/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(mfc1) macro(replace_flags var these those) diff --git a/Tests/MFC/try_compile/CMakeLists.txt b/Tests/MFC/try_compile/CMakeLists.txt index 768d2a6..d22b8b6 100644 --- a/Tests/MFC/try_compile/CMakeLists.txt +++ b/Tests/MFC/try_compile/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(try_compile_mfc) set(files diff --git a/Tests/MSManifest/Subdir/CMakeLists.txt b/Tests/MSManifest/Subdir/CMakeLists.txt index 3b4fccc..68c66fe 100644 --- a/Tests/MSManifest/Subdir/CMakeLists.txt +++ b/Tests/MSManifest/Subdir/CMakeLists.txt @@ -5,6 +5,11 @@ if(MSVC AND NOT MSVC_VERSION LESS 1400) add_test(NAME MSManifest.Single COMMAND ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSManifest> -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) + add_executable(MSManifestNonIncremental main.c ${CMAKE_CURRENT_BINARY_DIR}/test.manifest) + set_property(TARGET MSManifestNonIncremental PROPERTY LINK_FLAGS "/INCREMENTAL:NO") + add_test(NAME MSManifest.Single.NonIncremental COMMAND + ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSManifestNonIncremental> + -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) add_executable(MSManifestNone main.c) set_property(TARGET MSManifestNone PROPERTY LINK_FLAGS "/MANIFEST:NO") elseif(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang") diff --git a/Tests/MSManifest/Subdir2/CMakeLists.txt b/Tests/MSManifest/Subdir2/CMakeLists.txt index 0d960ad..bbc70dc 100644 --- a/Tests/MSManifest/Subdir2/CMakeLists.txt +++ b/Tests/MSManifest/Subdir2/CMakeLists.txt @@ -10,4 +10,14 @@ if((MSVC AND NOT MSVC_VERSION LESS 1400) OR (WIN32 AND CMAKE_C_COMPILER_ID MATCH add_test(NAME MSManifest.Multiple COMMAND ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSMultipleManifest> -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) + if(MSVC AND NOT MSVC_VERSION LESS 1400) + add_executable(MSMultipleManifestNonIncremental main.c + ${CMAKE_CURRENT_BINARY_DIR}/test_manifest1.manifest + ${CMAKE_CURRENT_BINARY_DIR}/test_manifest2.manifest + ${CMAKE_CURRENT_BINARY_DIR}/test_manifest3.manifest) + set_property(TARGET MSMultipleManifestNonIncremental PROPERTY LINK_FLAGS "/INCREMENTAL:NO") + add_test(NAME MSManifest.Multiple.NonIncremental COMMAND + ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSMultipleManifestNonIncremental> + -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake) + endif() endif() diff --git a/Tests/MacRuntimePath/A/CMakeLists.txt b/Tests/MacRuntimePath/A/CMakeLists.txt index c9d3f2c..7af746c 100644 --- a/Tests/MacRuntimePath/A/CMakeLists.txt +++ b/Tests/MacRuntimePath/A/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(MacRuntimePath_A) # a shared library diff --git a/Tests/MacRuntimePath/B/CMakeLists.txt b/Tests/MacRuntimePath/B/CMakeLists.txt index 85598c4..e88433c 100644 --- a/Tests/MacRuntimePath/B/CMakeLists.txt +++ b/Tests/MacRuntimePath/B/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(MacRuntimePath_B) include(${MacRuntimePath_B_BINARY_DIR}/../Root/lib/exp.cmake) diff --git a/Tests/MakeClean/CMakeLists.txt b/Tests/MakeClean/CMakeLists.txt index 809d65b..b7b9602 100644 --- a/Tests/MakeClean/CMakeLists.txt +++ b/Tests/MakeClean/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(MakeClean) # Build the to-clean project. diff --git a/Tests/MissingSourceFile/CMakeLists.txt b/Tests/MissingSourceFile/CMakeLists.txt index b4f0033..f4fd8b0 100644 --- a/Tests/MissingSourceFile/CMakeLists.txt +++ b/Tests/MissingSourceFile/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(MissingSourceFile C) add_executable(MissingSourceFile DoesNotExist/MissingSourceFile.c) diff --git a/Tests/ModuleDefinition/CMakeLists.txt b/Tests/ModuleDefinition/CMakeLists.txt index 483bd8b..49577a7 100644 --- a/Tests/ModuleDefinition/CMakeLists.txt +++ b/Tests/ModuleDefinition/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ModuleDefinition C) # Test .def file source recognition for DLLs. diff --git a/Tests/NewlineArgs/CMakeLists.txt b/Tests/NewlineArgs/CMakeLists.txt index 3e4b436..c822113 100644 --- a/Tests/NewlineArgs/CMakeLists.txt +++ b/Tests/NewlineArgs/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple CXX only test case -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project (NewlineArgs CXX) add_definitions("-DTEST_FLAG_1 diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt index 06167a8..05a35bb 100644 --- a/Tests/ObjectLibrary/CMakeLists.txt +++ b/Tests/ObjectLibrary/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ObjectLibrary C) add_subdirectory(A) diff --git a/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt b/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt index fb0ebc0..f19d5a4 100644 --- a/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt +++ b/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ExportLanguages CXX) add_library(ExportLanguagesA OBJECT a.cxx) add_library(ExportLanguagesB STATIC a.c $<TARGET_OBJECTS:ExportLanguagesA>) diff --git a/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt b/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt index 8544798..093aca6 100644 --- a/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt +++ b/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(ExportLanguagesTest) diff --git a/Tests/OutDir/CMakeLists.txt b/Tests/OutDir/CMakeLists.txt index 8afe036..e7bc3ab 100644 --- a/Tests/OutDir/CMakeLists.txt +++ b/Tests/OutDir/CMakeLists.txt @@ -7,7 +7,7 @@ if(_isMultiConfig) string(TOUPPER "${config}" CONFIG) list(APPEND configs "${CONFIG}") endforeach() - set(CMAKE_BUILD_TYPE) + unset(CMAKE_BUILD_TYPE CACHE) elseif(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() diff --git a/Tests/PDBDirectoryAndName/CMakeLists.txt b/Tests/PDBDirectoryAndName/CMakeLists.txt index 5aa2459..81207bc 100644 --- a/Tests/PDBDirectoryAndName/CMakeLists.txt +++ b/Tests/PDBDirectoryAndName/CMakeLists.txt @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) -cmake_policy(SET CMP0054 NEW) +cmake_minimum_required(VERSION 3.5) project(PDBDirectoryAndName C) # Make sure the proper compiler is in use. diff --git a/Tests/Plugin/CMakeLists.txt b/Tests/Plugin/CMakeLists.txt index c2f43cd..a62e53f 100644 --- a/Tests/Plugin/CMakeLists.txt +++ b/Tests/Plugin/CMakeLists.txt @@ -1,5 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) -cmake_policy(SET CMP0054 NEW) +cmake_minimum_required (VERSION 3.5) project(Plugin) # Test per-target output directory properties. diff --git a/Tests/Plugin/PluginTest/CMakeLists.txt b/Tests/Plugin/PluginTest/CMakeLists.txt index f00122d..4100683 100644 --- a/Tests/Plugin/PluginTest/CMakeLists.txt +++ b/Tests/Plugin/PluginTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(PluginTest) diff --git a/Tests/PositionIndependentTargets/CMakeLists.txt b/Tests/PositionIndependentTargets/CMakeLists.txt index ff779d3..4f7e285 100644 --- a/Tests/PositionIndependentTargets/CMakeLists.txt +++ b/Tests/PositionIndependentTargets/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(PositionIndependentTargets) diff --git a/Tests/Preprocess/CMakeLists.txt b/Tests/Preprocess/CMakeLists.txt index 84ca5e8..63a7b5e 100644 --- a/Tests/Preprocess/CMakeLists.txt +++ b/Tests/Preprocess/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) +cmake_policy(SET CMP0043 OLD) # testing the old behavior + project(Preprocess) # This test is meant both as a test and as a reference for supported diff --git a/Tests/Qt4And5Automoc/CMakeLists.txt b/Tests/Qt4And5Automoc/CMakeLists.txt index ad74961..12dc99b 100644 --- a/Tests/Qt4And5Automoc/CMakeLists.txt +++ b/Tests/Qt4And5Automoc/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(Qt4And5Automoc) diff --git a/Tests/Qt4Targets/CMakeLists.txt b/Tests/Qt4Targets/CMakeLists.txt index 3ddc345..83cd44f 100644 --- a/Tests/Qt4Targets/CMakeLists.txt +++ b/Tests/Qt4Targets/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(Qt4Targets) diff --git a/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt b/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt index 65e2b64..d0e9617 100644 --- a/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt +++ b/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(IncrementalMoc) find_package(Qt4 REQUIRED) diff --git a/Tests/QtAutomocNoQt/CMakeLists.txt b/Tests/QtAutomocNoQt/CMakeLists.txt index 655f12b..4e2ceeb 100644 --- a/Tests/QtAutomocNoQt/CMakeLists.txt +++ b/Tests/QtAutomocNoQt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(QtAutomocNoQt) diff --git a/Tests/ReturnTest/CMakeLists.txt b/Tests/ReturnTest/CMakeLists.txt index 78e3fc1..03a0f7a 100644 --- a/Tests/ReturnTest/CMakeLists.txt +++ b/Tests/ReturnTest/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project (ReturnTest) function (FAILED testname) diff --git a/Tests/RunCMake/AppleTextStubs/CMakeLists.txt b/Tests/RunCMake/AppleTextStubs/CMakeLists.txt new file mode 100644 index 0000000..93ee9df --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.5) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/AppleTextStubs/Framework-export.cmake b/Tests/RunCMake/AppleTextStubs/Framework-export.cmake new file mode 100644 index 0000000..f75c6d1 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Framework-export.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +add_library(foo SHARED foo.c) +set_property(TARGET foo PROPERTY FRAMEWORK TRUE) +set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE) +set_property(TARGET foo PROPERTY LIBRARY_OUTPUT_DIRECTORY $<CONFIG>) + +install(TARGETS foo EXPORT foo FRAMEWORK DESTINATION DESTINATION "${CMAKE_BINARY_DIR}/$<CONFIG>") +install(EXPORT foo DESTINATION lib/foo NAMESPACE foo-install::) +install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/foo) + +export(TARGETS foo NAMESPACE foo-build:: FILE Release/foo.cmake) diff --git a/Tests/RunCMake/AppleTextStubs/Framework-import.cmake b/Tests/RunCMake/AppleTextStubs/Framework-import.cmake new file mode 100644 index 0000000..e0001d0 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Framework-import.cmake @@ -0,0 +1,62 @@ +enable_language(C) + +find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH) + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo-install::foo) + +get_property(is_framework TARGET foo-install::foo PROPERTY FRAMEWORK) +if (NOT is_framework) + message(SEND_ERROR "foo-build::foo: FRAMEWORK not set.") +endif() +get_property(enable_exports TARGET foo-install::foo PROPERTY ENABLE_EXPORTS) +if (CAMKE_TAPI AND NOT enable_exports) + message(SEND_ERROR "foo-install::foo: ENABLE_EXPORTS not set.") +endif() + +get_property(implib TARGET foo-install::foo PROPERTY IMPORTED_IMPLIB_RELEASE) +if (CAMKE_TAPI AND NOT implib) + message(SEND_ERROR "foo-install::foo: IMPORTED_IMPLIB_RELEASE not set.") +endif() +if (CAMKE_TAPI AND NOT implib MATCHES "foo.framework/Versions/A/foo.tbd$") + message(SEND_ERROR "foo-install::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.") +endif() + +get_property(location TARGET foo-install::foo PROPERTY IMPORTED_LOCATION_RELEASE) +if (NOT location) + message(SEND_ERROR "foo-install::foo: IMPORTED_LOCATION_RELEASE not set.") +endif() +if (NOT location MATCHES "foo.framework/Versions/A/foo$") + message(SEND_ERROR "foo-install::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.") +endif() + + +include(${foo_BUILD}/foo.cmake) + +add_executable(main2 main.c) +target_link_libraries(main2 PRIVATE foo-build::foo) + +get_property(is_framework TARGET foo-build::foo PROPERTY FRAMEWORK) +if (NOT is_framework) + message(SEND_ERROR "foo-build::foo: FRAMEWORK not set.") +endif() +get_property(enable_exports TARGET foo-build::foo PROPERTY ENABLE_EXPORTS) +if (CAMKE_TAPI AND NOT enable_exports) + message(SEND_ERROR "foo-build::foo: ENABLE_EXPORTS not set.") +endif() + +get_property(implib TARGET foo-build::foo PROPERTY IMPORTED_IMPLIB_RELEASE) +if (CAMKE_TAPI AND NOT implib) + message(SEND_ERROR "foo-build::foo: IMPORTED_IMPLIB_RELEASE not set.") +endif() +if (CAMKE_TAPI AND NOT implib STREQUAL "${foo_BUILD}/foo.framework/Versions/A/foo.tbd") + message(SEND_ERROR "foo-build::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.") +endif() + +get_property(location TARGET foo-build::foo PROPERTY IMPORTED_LOCATION_RELEASE) +if (NOT location) + message(SEND_ERROR "foo-build::foo: IMPORTED_LOCATION_RELEASE not set.") +endif() +if (NOT location STREQUAL "${foo_BUILD}/foo.framework/Versions/A/foo") + message(SEND_ERROR "foo-build::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.") +endif() diff --git a/Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake b/Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake new file mode 100644 index 0000000..e8a5557 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/Framework-Release-generated.cmake") diff --git a/Tests/RunCMake/AppleTextStubs/Framework.cmake b/Tests/RunCMake/AppleTextStubs/Framework.cmake new file mode 100644 index 0000000..f99eb5e --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Framework.cmake @@ -0,0 +1,59 @@ +enable_language(C) + +add_library(foo SHARED foo.c) +set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE) +set_property(TARGET foo PROPERTY FRAMEWORK TRUE) + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo) + + +install(TARGETS foo FRAMEWORK DESTINATION "${CMAKE_BINARY_DIR}/INSTALL") + +# LIBRARY and ARCHIVE should be ignored +install(TARGETS foo FRAMEWORK DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2" + LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/lib" + ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev") + + +set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\") + set (APPLE_TEXT_STUBS_SUPPORTED TRUE) +endif()\n\n") + +string (APPEND GENERATE_CONTENT [[ +macro (CHECK_FILE test_msg path) + if (NOT EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n") + endif() +endmacro() + +macro (CHECK_SYMLINK test_msg path) + if(NOT IS_SYMLINK "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a symbolic link\n") + elseif (NOT EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a valid symlink\n") + endif() +endmacro() + +check_file("DYLIB file" "$<TARGET_FILE:foo>") +check_symlink("Public DYLIB file" "$<TARGET_LINKER_LIBRARY_FILE:foo>") +check_file("executable file" "$<TARGET_FILE:main>") + +check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/Versions/A/$<TARGET_FILE_NAME:foo>") +check_symlink("Installed Public DULIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/$<TARGET_FILE_NAME:foo>") +check_file("Installed DULIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/Versions/A/$<TARGET_FILE_NAME:foo>") +check_symlink("Installed Public DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/$<TARGET_FILE_NAME:foo>") + +if (APPLE_TEXT_STUBS_SUPPORTED) + check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>") + check_symlink("Public TBD file" "$<TARGET_LINKER_IMPORT_FILE:foo>") + + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/Versions/A/$<TARGET_IMPORT_FILE_NAME:foo>") + check_symlink("Installed Public TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/$<TARGET_IMPORT_FILE_NAME:foo>") + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/Versions/A/$<TARGET_IMPORT_FILE_NAME:foo>") + check_symlink("Installed Public TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/$<TARGET_IMPORT_FILE_NAME:foo>") +endif() +]]) + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Framework-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/AppleTextStubs/Library-export.cmake b/Tests/RunCMake/AppleTextStubs/Library-export.cmake new file mode 100644 index 0000000..d2e09ea --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Library-export.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +add_library(foo SHARED foo.c) +set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE) +set_property(TARGET foo PROPERTY LIBRARY_OUTPUT_DIRECTORY $<CONFIG>) +set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_DIRECTORY $<CONFIG>) + +install(TARGETS foo EXPORT foo DESTINATION "${CMAKE_BINARY_DIR}/$<CONFIG>") +install(EXPORT foo DESTINATION lib/foo NAMESPACE foo-install::) +install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/foo) + +export(TARGETS foo NAMESPACE foo-build:: FILE Release/foo.cmake) diff --git a/Tests/RunCMake/AppleTextStubs/Library-import.cmake b/Tests/RunCMake/AppleTextStubs/Library-import.cmake new file mode 100644 index 0000000..9406aac --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Library-import.cmake @@ -0,0 +1,54 @@ +enable_language(C) + +find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH) + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo-install::foo) + +get_property(enable_exports TARGET foo-install::foo PROPERTY ENABLE_EXPORTS) +if (CMAKE_TAPI AND NOT enable_exports) + message(SEND_ERROR "foo-install::foo: ENABLE_EXPORTS not set.") +endif() + +get_property(implib TARGET foo-install::foo PROPERTY IMPORTED_IMPLIB_RELEASE) +if (CMAKE_TAPI AND NOT implib) + message(SEND_ERROR "foo-install::foo: IMPORTED_IMPLIB_RELEASE not set.") +endif() +if (CMAKE_TAPI AND NOT implib MATCHES "Release/libfoo.tbd$") + message(SEND_ERROR "foo-install::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.") +endif() + +get_property(location TARGET foo-install::foo PROPERTY IMPORTED_LOCATION_RELEASE) +if (NOT location) + message(SEND_ERROR "foo-install::foo: IMPORTED_LOCATION_RELEASE not set.") +endif() +if (NOT location MATCHES "Release/libfoo.dylib$") + message(SEND_ERROR "foo-install::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.") +endif() + + +include(${foo_BUILD}/foo.cmake) + +add_executable(main2 main.c) +target_link_libraries(main2 PRIVATE foo-build::foo) + +get_property(enable_exports TARGET foo-build::foo PROPERTY ENABLE_EXPORTS) +if (CMAKE_TAPI AND NOT enable_exports) + message(SEND_ERROR "foo-build::foo: ENABLE_EXPORTS not set.") +endif() + +get_property(implib TARGET foo-build::foo PROPERTY IMPORTED_IMPLIB_RELEASE) +if (CMAKE_TAPI AND NOT implib) + message(SEND_ERROR "foo-build::foo: IMPORTED_IMPLIB_RELEASE not set.") +endif() +if (CMAKE_TAPI AND NOT implib STREQUAL "${foo_BUILD}/libfoo.tbd") + message(SEND_ERROR "foo-build::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.") +endif() + +get_property(location TARGET foo-build::foo PROPERTY IMPORTED_LOCATION_RELEASE) +if (NOT location) + message(SEND_ERROR "foo-build::foo: IMPORTED_LOCATION_RELEASE not set.") +endif() +if (NOT location STREQUAL "${foo_BUILD}/libfoo.dylib") + message(SEND_ERROR "foo-build::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.") +endif() diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake new file mode 100644 index 0000000..40ec0a6 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/LibraryWithOutputs-Release-generated.cmake") diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake new file mode 100644 index 0000000..f61c8f2 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake @@ -0,0 +1,52 @@ +enable_language(C) + +add_library(foo SHARED foo.c) +set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE) +set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/TBD/$<CONFIG>") +set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_NAME "tbd") + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo) + + +set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\") + set (APPLE_TEXT_STUBS_SUPPORTED TRUE) +endif()\n\n") + +string (APPEND GENERATE_CONTENT [[ +macro (CHECK_FILE test_msg path) + if (NOT EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n") + endif() +endmacro() + +check_file("DYLIB file" "$<TARGET_FILE:foo>") +check_file("executable file" "$<TARGET_FILE:main>") + +if (APPLE_TEXT_STUBS_SUPPORTED) + check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>") +]]) + +if (CMAKE_GENERATOR STREQUAL "Xcode") + # ARCHIVE outputs are ignored by this generator + string (APPEND GENERATE_CONTENT + "\n if (NOT \"$<TARGET_IMPORT_FILE_DIR:foo>\" STREQUAL \"${CMAKE_BINARY_DIR}/$<CONFIG>\") + string (APPEND RunCMake_TEST_FAILED \"Wrong directory for TBD file: \\\"$<TARGET_IMPORT_FILE_DIR:foo>\\\"\n\") + endif() + if (NOT \"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\" STREQUAL \"foo\") + string (APPEND RunCMake_TEST_FAILED \"Wrong base name for TBD file: \\\"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\\\"\n\") + endif()\n") +else() + string (APPEND GENERATE_CONTENT + "\n if (NOT \"$<TARGET_IMPORT_FILE_DIR:foo>\" STREQUAL \"${CMAKE_BINARY_DIR}/TBD/$<CONFIG>\") + string (APPEND RunCMake_TEST_FAILED \"Wrong directory for TBD file: \\\"$<TARGET_IMPORT_FILE_DIR:foo>\\\"\n\") + endif() + if (NOT \"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\" STREQUAL \"tbd\") + string (APPEND RunCMake_TEST_FAILED \"Wrong base name for TBD file: \\\"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\\\"\n\") + endif()\n") +endif() +string (APPEND GENERATE_CONTENT "endif()\n") + + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/LibraryWithOutputs-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake new file mode 100644 index 0000000..af73286 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/LibraryWithVersions-Release-generated.cmake") diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake new file mode 100644 index 0000000..28c175d --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake @@ -0,0 +1,96 @@ +enable_language(C) + +add_library(foo SHARED foo.c) +set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE) +set_property (TARGET foo PROPERTY VERSION 2.5.0) +set_property (TARGET foo PROPERTY SOVERSION 2.0.0) + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo) + + +install(TARGETS foo DESTINATION "${CMAKE_BINARY_DIR}/INSTALL" COMPONENT default) + +install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev1" NAMELINK_SKIP COMPONENT default) +install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev2" NAMELINK_ONLY COMPONENT default) + +install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL3" + COMPONENT lib3 NAMELINK_COMPONENT dev3) +install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL4" + COMPONENT lib4 NAMELINK_COMPONENT dev4) + + +set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\") + set (APPLE_TEXT_STUBS_SUPPORTED TRUE) +endif()\n\n") + +string (APPEND GENERATE_CONTENT [[ +cmake_policy (SET CMP0011 NEW) +cmake_policy (SET CMP0057 NEW) + +macro (CHECK_FILE test_msg path) + if (NOT EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n") + endif() +endmacro() + +macro (CHECK_SYMLINK test_msg path) + if (NOT IS_SYMLINK "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a symbolic link\n") + elseif (NOT EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not a valid symlink\n") + endif() +endmacro() + +macro (CHECK_NOFILE test_msg path) + if (EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" was found\n") + endif() +endmacro() + +macro (CHECK_INSTALLED test_msg dir file) + file(GLOB installed_files LIST_DIRECTORIES FALSE RELATIVE "${dir}" "${dir}/*") + if (NOT "${file}" IN_LIST installed_files) + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${dir}/${file}\" not found\n") + endif() +endmacro() + + +check_file("DYLIB file" "$<TARGET_FILE:foo>") +check_symlink("Linkable DYLIB file" "$<TARGET_LINKER_LIBRARY_FILE:foo>") +check_symlink("SONAME DYLIB file" "$<TARGET_SONAME_FILE:foo>") +check_file("executable file" "$<TARGET_FILE:main>") + +check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_FILE_NAME:foo>") +check_symlink("Installed Linkable DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_LINKER_LIBRARY_FILE_NAME:foo>") +check_symlink("Installed SONAME DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_SONAME_FILE_NAME:foo>") + +if (APPLE_TEXT_STUBS_SUPPORTED) + check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>") + check_symlink("Linkable TBD file" "$<TARGET_LINKER_IMPORT_FILE:foo>") + check_symlink("SONAME TBD file" "$<TARGET_SONAME_IMPORT_FILE:foo>") + + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_IMPORT_FILE_NAME:foo>") + check_symlink("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>") + check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>") + + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_IMPORT_FILE_NAME:foo>") + check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>") + check_nofile("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>") + + check_installed("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2" "$<TARGET_LINKER_IMPORT_FILE_NAME:foo>") + check_nofile("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2/$<TARGET_IMPORT_FILE_NAME:foo>") + check_nofile("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>") + + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_IMPORT_FILE_NAME:foo>") + check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>") + check_nofile("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>") + + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_IMPORT_FILE_NAME:foo>") + check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>") + check_symlink("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>") +endif() +]]) + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/LibraryWithVersions-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake b/Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake new file mode 100644 index 0000000..9ccd685 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake @@ -0,0 +1,58 @@ +include(RunCMake) + +function(build_project test) + if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + run_cmake(${test}) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_NO_CLEAN 1) + + run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release) + if ("${ARGC}" GREATER "1") + # custom install step + cmake_language(CALL ${ARGV1}) + else() + run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Release) + endif() +endfunction() + +build_project(Simple) +build_project(Framework) +build_project(LibraryWithOutputs) + + +function(LibraryWithVersions-install) + run_cmake_command(LibraryWithVersions-install-component-lib3 ${CMAKE_COMMAND} --install . --config Release --component lib3) + run_cmake_command(LibraryWithVersions-install-component-lib4 ${CMAKE_COMMAND} --install . --config Release --component lib4) + run_cmake_command(LibraryWithVersions-install-components-dev4 ${CMAKE_COMMAND} --install . --config Release --component dev4) + run_cmake_command(LibraryWithVersions-install ${CMAKE_COMMAND} --install . --config Release --component default) +endfunction() + +build_project(LibraryWithVersions LibraryWithVersions-install) + + +function(build_ExportImport_project test) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-export-build) + set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root) + if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + run_cmake(${test}-export) + unset(RunCMake_TEST_OPTIONS) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${test}-export-build ${CMAKE_COMMAND} --build . --config Release) + run_cmake_command(${test}-export-install ${CMAKE_COMMAND} --install . --prefix ${CMAKE_INSTALL_PREFIX} --config Release) + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-import-build) + set (foo_BUILD "${RunCMake_BINARY_DIR}/${test}-export-build") + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + string (APPEND foo_BUILD "/Release") + endif() + run_cmake_with_options(${test}-import -Dfoo_DIR=${CMAKE_INSTALL_PREFIX}/lib/foo + -Dfoo_BUILD=${RunCMake_BINARY_DIR}/${test}-export-build/Release) + run_cmake_command(${test}-import-build ${CMAKE_COMMAND} --build . --config Release) +endfunction() + +build_ExportImport_project(Library) +build_ExportImport_project(Framework) diff --git a/Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake b/Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake new file mode 100644 index 0000000..94054fa --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/Simple-Release-generated.cmake") diff --git a/Tests/RunCMake/AppleTextStubs/Simple.cmake b/Tests/RunCMake/AppleTextStubs/Simple.cmake new file mode 100644 index 0000000..9f6318c --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/Simple.cmake @@ -0,0 +1,41 @@ +enable_language(C) + +add_library(foo SHARED foo.c) +set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE) + +add_executable(main main.c) +target_link_libraries(main PRIVATE foo) + + +install(TARGETS foo DESTINATION "${CMAKE_BINARY_DIR}/INSTALL") + +install(TARGETS foo LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/lib" + ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev") + + +set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\") + set (APPLE_TEXT_STUBS_SUPPORTED TRUE) +endif()\n\n") + +string (APPEND GENERATE_CONTENT [[ +macro (CHECK_FILE test_msg path) + if (NOT EXISTS "${path}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n") + endif() +endmacro() + +check_file("DYLIB file" "$<TARGET_FILE:foo>") +check_file("executable file" "$<TARGET_FILE:main>") + +check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/lib/$<TARGET_FILE_NAME:foo>") + +if (APPLE_TEXT_STUBS_SUPPORTED) + check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>") + + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_IMPORT_FILE_NAME:foo>") + check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev/$<TARGET_IMPORT_FILE_NAME:foo>") +endif() +]]) + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Simple-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/AppleTextStubs/foo-config.cmake.in b/Tests/RunCMake/AppleTextStubs/foo-config.cmake.in new file mode 100644 index 0000000..b038138 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/foo-config.cmake.in @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/foo.cmake) diff --git a/Tests/RunCMake/AppleTextStubs/foo.c b/Tests/RunCMake/AppleTextStubs/foo.c new file mode 100644 index 0000000..7f39d71 --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return 0; +} diff --git a/Tests/RunCMake/AppleTextStubs/main.c b/Tests/RunCMake/AppleTextStubs/main.c new file mode 100644 index 0000000..dc5ce3d --- /dev/null +++ b/Tests/RunCMake/AppleTextStubs/main.c @@ -0,0 +1,7 @@ + +extern int foo(void); + +int main() +{ + return foo(); +} diff --git a/Tests/RunCMake/AutoExportDll/CMakeLists.txt b/Tests/RunCMake/AutoExportDll/CMakeLists.txt index 18dfd26..93ee9df 100644 --- a/Tests/RunCMake/AutoExportDll/CMakeLists.txt +++ b/Tests/RunCMake/AutoExportDll/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/AutoExportDll/hello.cxx b/Tests/RunCMake/AutoExportDll/hello.cxx index 74e7a4e..35ccbb7 100644 --- a/Tests/RunCMake/AutoExportDll/hello.cxx +++ b/Tests/RunCMake/AutoExportDll/hello.cxx @@ -12,3 +12,12 @@ void hello() } void Hello::operator delete[](void*){}; void Hello::operator delete(void*){}; + +#ifdef HELLO_VFTABLE +HelloVFTable::HelloVFTable() +{ +} +HelloVFTable::~HelloVFTable() +{ +} +#endif diff --git a/Tests/RunCMake/AutoExportDll/hello.h b/Tests/RunCMake/AutoExportDll/hello.h index 7192f65..410ffab 100644 --- a/Tests/RunCMake/AutoExportDll/hello.h +++ b/Tests/RunCMake/AutoExportDll/hello.h @@ -16,3 +16,20 @@ public: static void operator delete[](void*); static void operator delete(void*); }; + +// In the MSVC ABI, a delegating constructor references the vftable. +#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) +# define HELLO_VFTABLE +#endif +#ifdef HELLO_VFTABLE +class HelloVFTable +{ +public: + HelloVFTable(); + HelloVFTable(int) + : HelloVFTable() + { + } + virtual ~HelloVFTable(); +}; +#endif diff --git a/Tests/RunCMake/AutoExportDll/say.cxx b/Tests/RunCMake/AutoExportDll/say.cxx index 8fc768a..a9459a9 100644 --- a/Tests/RunCMake/AutoExportDll/say.cxx +++ b/Tests/RunCMake/AutoExportDll/say.cxx @@ -53,5 +53,8 @@ int main() #ifdef HAS_JUSTNOP justnop(); #endif +#ifdef HELLO_VFTABLE + HelloVFTable helloVFTable(1); +#endif return 0; } diff --git a/Tests/RunCMake/BuildDepends/CMakeLists.txt b/Tests/RunCMake/BuildDepends/CMakeLists.txt index 99f238b..8eb5748 100644 --- a/Tests/RunCMake/BuildDepends/CMakeLists.txt +++ b/Tests/RunCMake/BuildDepends/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/BundleUtilities/CMakeLists.txt b/Tests/RunCMake/BundleUtilities/CMakeLists.txt index 6dd8cdf..44025d3 100644 --- a/Tests/RunCMake/BundleUtilities/CMakeLists.txt +++ b/Tests/RunCMake/BundleUtilities/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.12) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake b/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake index df28102..a7b05d2 100644 --- a/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake +++ b/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.4) include(RunCMake) # TODO Migrate Tests/BundleUtilities here diff --git a/Tests/RunCMake/Byproducts/CleanByproducts.cmake b/Tests/RunCMake/Byproducts/CleanByproducts.cmake index 85d9582..961deb9 100644 --- a/Tests/RunCMake/Byproducts/CleanByproducts.cmake +++ b/Tests/RunCMake/Byproducts/CleanByproducts.cmake @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.10) -project(CleanByproducts) +enable_language(C) +enable_language(CXX) # Configurable parameters set(TEST_CLEAN_NO_CUSTOM FALSE CACHE BOOL "Value for the CLEAN_NO_CUSTOM PROPERTY") diff --git a/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake b/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake index f42d8e4..3d861fb 100644 --- a/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake +++ b/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 2.8.4) - cmake_policy(SET CMP0004 NEW) add_library(foo SHARED empty.cpp) diff --git a/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake b/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake index 3fa58b6..32c1474 100644 --- a/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake +++ b/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 2.8.4) - cmake_policy(SET CMP0004 OLD) add_library(foo SHARED empty.cpp) diff --git a/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake b/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake index 2970476..b7cd7ff 100644 --- a/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake +++ b/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 2.8.4) - cmake_policy(SET CMP0004 NEW) add_library(foo SHARED empty.cpp) diff --git a/Tests/RunCMake/CMP0004/CMakeLists.txt b/Tests/RunCMake/CMP0004/CMakeLists.txt index 12cd3c7..93ee9df 100644 --- a/Tests/RunCMake/CMP0004/CMakeLists.txt +++ b/Tests/RunCMake/CMP0004/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.4) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt b/Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt b/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt index a446211..dc03414 100644 --- a/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt @@ -1,13 +1,6 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -+ -CMake Deprecation Warning at CMP0019-OLD.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMP0019-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0019 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt b/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt index f7b9c0e..6eee437 100644 --- a/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt @@ -1,11 +1,4 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -+ -CMake Warning \(dev\) in CMakeLists.txt: +^CMake Warning \(dev\) in CMakeLists\.txt: Policy CMP0019 is not set: Do not re-expand variables in include and link information. Run "cmake --help-policy CMP0019" for policy details. Use the cmake_policy command to set the policy and suppress this warning. diff --git a/Tests/RunCMake/CMP0019/RunCMakeTest.cmake b/Tests/RunCMake/CMP0019/RunCMakeTest.cmake index 119fc2b..fcd080f 100644 --- a/Tests/RunCMake/CMP0019/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0019/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0019-WARN) run_cmake(CMP0019-OLD) diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt index 87404d3..c84a289 100644 --- a/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt +++ b/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt @@ -1,11 +1,4 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -+ -CMake Warning \(dev\) in CMakeLists.txt: +^CMake Warning \(dev\) in CMakeLists\.txt: Policy CMP0022 is not set: INTERFACE_LINK_LIBRARIES defines the link interface. Run "cmake --help-policy CMP0022" for policy details. Use the cmake_policy command to set the policy and suppress this warning. diff --git a/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt index 5d75720..39a9511 100644 --- a/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt @@ -1,11 +1,4 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -+ -CMake Warning \(dev\) in CMakeLists.txt: +^CMake Warning \(dev\) in CMakeLists\.txt: Policy CMP0022 is not set: INTERFACE_LINK_LIBRARIES defines the link interface. Run "cmake --help-policy CMP0022" for policy details. Use the cmake_policy command to set the policy and suppress this warning. diff --git a/Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt deleted file mode 100644 index 66a58fb..0000000 --- a/Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions.$ diff --git a/Tests/RunCMake/CMP0022/RunCMakeTest.cmake b/Tests/RunCMake/CMP0022/RunCMakeTest.cmake index 4c10996..ea956fc 100644 --- a/Tests/RunCMake/CMP0022/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0022/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0022-WARN) run_cmake(CMP0022-WARN-tll) diff --git a/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt b/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt new file mode 100644 index 0000000..259eabd --- /dev/null +++ b/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0026-IMPORTED.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0111 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/CMP0026/RunCMakeTest.cmake b/Tests/RunCMake/CMP0026/RunCMakeTest.cmake index 047da28..6476176 100644 --- a/Tests/RunCMake/CMP0026/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0026/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0026-WARN) run_cmake(CMP0026-OLD) diff --git a/Tests/RunCMake/CMP0037/RunCMakeTest.cmake b/Tests/RunCMake/CMP0037/RunCMakeTest.cmake index 5952279..558fba3 100644 --- a/Tests/RunCMake/CMP0037/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0037/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) if(RunCMake_GENERATOR MATCHES "^Ninja") # Detect ninja version so we know what tests can be supported. diff --git a/Tests/RunCMake/CMP0038/RunCMakeTest.cmake b/Tests/RunCMake/CMP0038/RunCMakeTest.cmake index fc3500a..3e7b5f3 100644 --- a/Tests/RunCMake/CMP0038/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0038/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0038-WARN) run_cmake(CMP0038-NEW) diff --git a/Tests/RunCMake/CMP0039/RunCMakeTest.cmake b/Tests/RunCMake/CMP0039/RunCMakeTest.cmake index 58e8ea9..ce7541a 100644 --- a/Tests/RunCMake/CMP0039/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0039/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0039-WARN) run_cmake(CMP0039-NEW) diff --git a/Tests/RunCMake/CMP0040/RunCMakeTest.cmake b/Tests/RunCMake/CMP0040/RunCMakeTest.cmake index 13160e3..e5e6c37 100644 --- a/Tests/RunCMake/CMP0040/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0040/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0040-OLD-missing-target) run_cmake(CMP0040-NEW-missing-target) diff --git a/Tests/RunCMake/CMP0041/RunCMakeTest.cmake b/Tests/RunCMake/CMP0041/RunCMakeTest.cmake index f47bb2e..93378c2 100644 --- a/Tests/RunCMake/CMP0041/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0041/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) # Protect tests from running inside the default install prefix. set(RunCMake_TEST_OPTIONS "-DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/NotDefaultPrefix") diff --git a/Tests/RunCMake/CMP0042/RunCMakeTest.cmake b/Tests/RunCMake/CMP0042/RunCMakeTest.cmake index 3b226d7..6b23145 100644 --- a/Tests/RunCMake/CMP0042/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0042/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0042-OLD) run_cmake(CMP0042-NEW) diff --git a/Tests/RunCMake/CMP0043/RunCMakeTest.cmake b/Tests/RunCMake/CMP0043/RunCMakeTest.cmake index 7f9572e..b940528 100644 --- a/Tests/RunCMake/CMP0043/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0043/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) diff --git a/Tests/RunCMake/CMP0045/RunCMakeTest.cmake b/Tests/RunCMake/CMP0045/RunCMakeTest.cmake index 7c0e8a2..009d455 100644 --- a/Tests/RunCMake/CMP0045/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0045/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0045-OLD) run_cmake(CMP0045-NEW) diff --git a/Tests/RunCMake/CMP0046/RunCMakeTest.cmake b/Tests/RunCMake/CMP0046/RunCMakeTest.cmake index 0a39c76..86b749a 100644 --- a/Tests/RunCMake/CMP0046/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0046/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0046-OLD-missing-dependency) run_cmake(CMP0046-NEW-missing-dependency) diff --git a/Tests/RunCMake/CMP0049/RunCMakeTest.cmake b/Tests/RunCMake/CMP0049/RunCMakeTest.cmake index a8aa9d9..e71f31e 100644 --- a/Tests/RunCMake/CMP0049/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0049/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0049-OLD) run_cmake(CMP0049-NEW) diff --git a/Tests/RunCMake/CMP0050/RunCMakeTest.cmake b/Tests/RunCMake/CMP0050/RunCMakeTest.cmake index b7de284..526a9aa 100644 --- a/Tests/RunCMake/CMP0050/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0050/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0050-OLD) run_cmake(CMP0050-NEW) diff --git a/Tests/RunCMake/CMP0051/RunCMakeTest.cmake b/Tests/RunCMake/CMP0051/RunCMakeTest.cmake index 621192d..955d898 100644 --- a/Tests/RunCMake/CMP0051/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0051/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0051-OLD) run_cmake(CMP0051-NEW) diff --git a/Tests/RunCMake/CMP0053/RunCMakeTest.cmake b/Tests/RunCMake/CMP0053/RunCMakeTest.cmake index 6521ac0..de58c25 100644 --- a/Tests/RunCMake/CMP0053/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0053/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0053-OLD) run_cmake(CMP0053-NEW) diff --git a/Tests/RunCMake/CMP0054/RunCMakeTest.cmake b/Tests/RunCMake/CMP0054/RunCMakeTest.cmake index 2f2fb76..fc031de 100644 --- a/Tests/RunCMake/CMP0054/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0054/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0054-OLD) run_cmake(CMP0054-NEW) diff --git a/Tests/RunCMake/CMP0055/RunCMakeTest.cmake b/Tests/RunCMake/CMP0055/RunCMakeTest.cmake index efcfcab..33a5b4b 100644 --- a/Tests/RunCMake/CMP0055/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0055/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0055-OLD-Out-of-Scope) run_cmake(CMP0055-NEW-Out-of-Scope) diff --git a/Tests/RunCMake/CMP0057/RunCMakeTest.cmake b/Tests/RunCMake/CMP0057/RunCMakeTest.cmake index 719e054..76eaca6 100644 --- a/Tests/RunCMake/CMP0057/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0057/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0057-OLD) run_cmake(CMP0057-WARN) diff --git a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt index e2c280e..7230a07 100644 --- a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt +++ b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt @@ -12,5 +12,5 @@ will ask the linker to search for these by library name. Call Stack \(most recent call first\): CMP0060-WARN-ON.cmake:[0-9]+ \(include\) - CMakeLists.txt:4 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it.$ diff --git a/Tests/RunCMake/CMP0060/CMakeLists.txt b/Tests/RunCMake/CMP0060/CMakeLists.txt index 291d34d..db6b701 100644 --- a/Tests/RunCMake/CMP0060/CMakeLists.txt +++ b/Tests/RunCMake/CMP0060/CMakeLists.txt @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.9) -cmake_policy(VERSION 3.2) +cmake_minimum_required(VERSION 3.2) project(${RunCMake_TEST} C) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0060/RunCMakeTest.cmake b/Tests/RunCMake/CMP0060/RunCMakeTest.cmake index 445156f..b7eae5a 100644 --- a/Tests/RunCMake/CMP0060/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0060/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) function(run_cmake_CMP0060 CASE) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0060-${CASE}-build) diff --git a/Tests/RunCMake/CMP0064/RunCMakeTest.cmake b/Tests/RunCMake/CMP0064/RunCMakeTest.cmake index 26e0a91..4c68510 100644 --- a/Tests/RunCMake/CMP0064/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0064/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0064-OLD) run_cmake(CMP0064-WARN) diff --git a/Tests/RunCMake/CMP0065/RunCMakeTest.cmake b/Tests/RunCMake/CMP0065/RunCMakeTest.cmake index e86b50e..1ca4605 100644 --- a/Tests/RunCMake/CMP0065/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0065/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(OLDBad1) if(NOT CMAKE_SYSTEM_NAME STREQUAL "AIX") diff --git a/Tests/RunCMake/CMP0081/CMakeLists.txt b/Tests/RunCMake/CMP0081/CMakeLists.txt index ef2163c..44025d3 100644 --- a/Tests/RunCMake/CMP0081/CMakeLists.txt +++ b/Tests/RunCMake/CMP0081/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.12) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0102/CMakeLists.txt b/Tests/RunCMake/CMP0102/CMakeLists.txt index ef2163c..2632ffa 100644 --- a/Tests/RunCMake/CMP0102/CMakeLists.txt +++ b/Tests/RunCMake/CMP0102/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.16) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0106/CMakeLists.txt b/Tests/RunCMake/CMP0106/CMakeLists.txt index eafa642..0a96a26 100644 --- a/Tests/RunCMake/CMP0106/CMakeLists.txt +++ b/Tests/RunCMake/CMP0106/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.17) if (RunCMake_TEST STREQUAL "CMP0106-WARN-VTK") project(VTK NONE) else () diff --git a/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt b/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt new file mode 100644 index 0000000..bf7fb08 --- /dev/null +++ b/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0111-OLD.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0111 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/CMP0118/CMP0118-NEW-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt index ec777f7..acd145e 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt @@ -1,5 +1,5 @@ ^prop: `0` -CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-build/GeneratedMain\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt index f7d9f6b..929dafd 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt index a876390..6a096b8 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt index b750ae7..e721876 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt @@ -124,7 +124,7 @@ 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 CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt) @@ -132,7 +132,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt) @@ -140,7 +140,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt) @@ -148,7 +148,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt) @@ -156,7 +156,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt) diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt index 580f04f..f358297 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt @@ -124,7 +124,7 @@ 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 CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt) @@ -132,7 +132,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt) @@ -140,7 +140,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt) @@ -148,7 +148,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt) @@ -156,7 +156,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt) diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt index e268a7a..0e27c1d 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt @@ -115,7 +115,7 @@ 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 CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -123,7 +123,7 @@ 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]+ \(add_custom_target\): +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) @@ -131,7 +131,7 @@ 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]+ \(add_custom_target\): +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) @@ -139,7 +139,7 @@ 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]+ \(add_custom_target\): +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) @@ -147,7 +147,7 @@ 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]+ \(add_custom_target\): +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) @@ -155,7 +155,7 @@ 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]+ \(add_custom_target\): +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) @@ -163,7 +163,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt index 08eb682..4de6e76 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt @@ -47,7 +47,7 @@ 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-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-build/Generated_source[4-6]\.txt @@ -55,7 +55,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-build/Generated_source[4-6]\.txt @@ -63,7 +63,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt index b7c496c..b7a170b 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt @@ -47,7 +47,7 @@ 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-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-build/Generated_source[4-6]\.txt @@ -55,7 +55,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-build/Generated_source[4-6]\.txt @@ -63,7 +63,7 @@ Call Stack \(most recent call first\): CMP0118-NEW-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt index 58144c8..2af72a4 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt @@ -1,5 +1,5 @@ ^prop: `0` -CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-build/GeneratedMain\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt index 1f1bc90..6109f65 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt @@ -40,7 +40,7 @@ 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 CMP0118-Common-Test10\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test10\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt index 5c15f12..e5e97de 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt @@ -40,7 +40,7 @@ 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-Test11\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-build/Generated_source[4-6]\.txt @@ -48,7 +48,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-build/Generated_source[4-6]\.txt @@ -56,7 +56,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt index 12a913a..f5b3d1a 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt @@ -40,7 +40,7 @@ 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 CMP0118-Common-Test14\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test14\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt index 62db7ee..a30bc84 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt @@ -40,7 +40,7 @@ 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-Test15\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-build/Generated_source[4-6]\.txt @@ -48,7 +48,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-build/Generated_source[4-6]\.txt @@ -56,7 +56,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt index 7f86d38..4f4fea3 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt index 4104fc0..3c80531 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt index 7a16d0b..9600fee 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt) @@ -60,7 +60,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt) @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt) @@ -76,7 +76,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt) @@ -84,7 +84,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt) diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt index 5a5c4ec..e638660 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt) @@ -60,7 +60,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt) @@ -68,7 +68,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt) @@ -76,7 +76,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt) @@ -84,7 +84,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt) diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt index 12fa617..18e6a8c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) @@ -84,7 +84,7 @@ 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]+ \(add_custom_target\): +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) @@ -92,7 +92,7 @@ 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]+ \(add_custom_target\): +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) @@ -100,7 +100,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt index 7199f04..a60545f 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt @@ -34,7 +34,7 @@ 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 CMP0118-Common-Test6\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test6\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt index 233fd8b..fd496cb 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt @@ -34,7 +34,7 @@ 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-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt @@ -42,7 +42,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt @@ -50,7 +50,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt @@ -58,7 +58,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt @@ -66,7 +66,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt index 4aed2ed..3505242 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt @@ -34,7 +34,7 @@ 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 CMP0118-Common-Test8\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test8\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt index cea8c22..63a9341 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt @@ -34,7 +34,7 @@ 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-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-build/Generated_source[4-6]\.txt @@ -42,7 +42,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-build/Generated_source[4-6]\.txt @@ -50,7 +50,7 @@ Call Stack \(most recent call first\): CMP0118-OLD-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt index e2a2cf5..1d7cbde 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt @@ -1,5 +1,5 @@ ^prop: `0` -CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-build/GeneratedMain\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt index bce7681..f4c7d00 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt @@ -40,7 +40,7 @@ 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 CMP0118-Common-Test10\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test10\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt index 00c47e9..93d8b83 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt @@ -63,7 +63,7 @@ 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-Test11\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-build/Generated_source[4-6]\.txt @@ -71,7 +71,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-build/Generated_source[4-6]\.txt @@ -79,7 +79,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test11\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt index 5b7994c..fdde792 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt @@ -40,7 +40,7 @@ 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 CMP0118-Common-Test14\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test14\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt index c975c23..9e6a4a5 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt @@ -63,7 +63,7 @@ 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-Test15\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-build/Generated_source[4-6]\.txt @@ -71,7 +71,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-build/Generated_source[4-6]\.txt @@ -79,7 +79,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test15\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt index 142d8a0..58ba793 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt index d4ef667..cdb7cb4 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt @@ -52,7 +52,7 @@ 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 CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -60,7 +60,7 @@ 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]+ \(add_custom_target\): +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) @@ -68,7 +68,7 @@ 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]+ \(add_custom_target\): +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) @@ -76,7 +76,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt index ceeb570..4cd401c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt @@ -169,7 +169,7 @@ 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 CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt) @@ -177,7 +177,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt) @@ -185,7 +185,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt) @@ -193,7 +193,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt) @@ -201,7 +201,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt) diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt index f8484d0..6acd3df 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt @@ -169,7 +169,7 @@ 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 CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt) @@ -177,7 +177,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt) @@ -185,7 +185,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt) @@ -193,7 +193,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt) @@ -201,7 +201,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt) diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt index 0556391..2805cbf 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt @@ -154,7 +154,7 @@ 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 CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\): +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) @@ -162,7 +162,7 @@ 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]+ \(add_custom_target\): +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) @@ -170,7 +170,7 @@ 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]+ \(add_custom_target\): +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) @@ -178,7 +178,7 @@ 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]+ \(add_custom_target\): +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) @@ -186,7 +186,7 @@ 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]+ \(add_custom_target\): +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) @@ -194,7 +194,7 @@ 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]+ \(add_custom_target\): +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) @@ -202,7 +202,7 @@ 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]+ \(add_custom_target\): +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) diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt index 7d588a2..6f29170 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt @@ -34,7 +34,7 @@ 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 CMP0118-Common-Test6\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test6\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt index 8421061..cde1164 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt @@ -57,7 +57,7 @@ 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-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt @@ -65,7 +65,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt @@ -73,7 +73,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt @@ -81,7 +81,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt @@ -89,7 +89,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt index e0f17e6..91c4c4c 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt @@ -34,7 +34,7 @@ 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 CMP0118-Common-Test8\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test8\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-build/Generated_source4\.txt diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt index 80f3edf..547e820 100644 --- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt +++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt @@ -57,7 +57,7 @@ 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-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-build/Generated_source[4-6]\.txt @@ -65,7 +65,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-build/Generated_source[4-6]\.txt @@ -73,7 +73,7 @@ Call Stack \(most recent call first\): CMP0118-WARN-Test9\.cmake:[0-9]+ \(include\) CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\): +CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-build/Generated_source[4-6]\.txt diff --git a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt index d3aa546..5e9cf6c 100644 --- a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt +++ b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt @@ -1,8 +1,5 @@ -^CMake Error at GenInSubdir-Common.cmake:[0-9]+ \(add_custom_target\): +^CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\): Cannot find source file: [^ ]*/Tests/RunCMake/CMP0118/GenInSubdir-OLD-build/GenInSubdir/sub.txt -Call Stack \(most recent call first\): - GenInSubdir-OLD.cmake:[0-9]+ \(include\) - CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt b/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt index 5eb8a34..2820ba3 100644 --- a/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt +++ b/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt @@ -1,8 +1,5 @@ -^CMake Error at GenInSubdir-Common.cmake:[0-9]+ \(add_custom_target\): +^CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\): Cannot find source file: [^ ]*/Tests/RunCMake/CMP0118/GenInSubdir-WARN-build/GenInSubdir/sub.txt -Call Stack \(most recent call first\): - GenInSubdir-WARN.cmake:[0-9]+ \(include\) - CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CMP0139/CMakeLists.txt b/Tests/RunCMake/CMP0139/CMakeLists.txt index 18dfd26..5ff8d3e 100644 --- a/Tests/RunCMake/CMP0139/CMakeLists.txt +++ b/Tests/RunCMake/CMP0139/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.23) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 930122c..0ebb720 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -270,8 +270,6 @@ if(want_NoQt_test) add_RunCMake_test(AutogenNoQt TEST_DIR Autogen) endif() -add_RunCMake_test(ArtifactOutputDirs) - if(NOT DEFINED CMake_TEST_BuildDepends_GNU_AS AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "LCC") AND CMAKE_GENERATOR MATCHES "^Ninja" @@ -323,6 +321,10 @@ endif() add_RunCMake_test(ExcludeFromAll) add_RunCMake_test(ExportImport) add_RunCMake_test(ExternalData) +if(CMAKE_GENERATOR MATCHES "^(Unix Makefiles|Ninja)$" + AND NOT "${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + add_RunCMake_test(ExtraGenerators) +endif() add_RunCMake_test(FeatureSummary) add_RunCMake_test(FPHSA) if(CMAKE_USE_SYSTEM_JSONCPP) @@ -353,6 +355,7 @@ add_RunCMake_test(GenEx-DEVICE_LINK) add_RunCMake_test(GenEx-LINK_LIBRARY) add_RunCMake_test(GenEx-LINK_GROUP) add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB}) +add_RunCMake_test(GenEx-TARGET_IMPORT_FILE) add_RunCMake_test(GenEx-GENEX_EVAL) add_RunCMake_test(GenEx-TARGET_PROPERTY) add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS) @@ -392,6 +395,7 @@ if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STRE endif() add_RunCMake_test(ScriptMode) add_RunCMake_test(Swift -DCMAKE_Swift_COMPILER=${CMAKE_Swift_COMPILER} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) +add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) add_RunCMake_test(TargetObjects) add_RunCMake_test(TargetProperties) add_RunCMake_test(ToolchainFile) @@ -511,6 +515,7 @@ add_RunCMake_test(BundleUtilities) if(APPLE) add_RunCMake_test(INSTALL_NAME_DIR) add_RunCMake_test(MacOSVersions) + add_RunCMake_test(AppleTextStubs) endif() function(add_RunCMake_test_try_compile) diff --git a/Tests/RunCMake/CPack/CMakeLists.txt b/Tests/RunCMake/CPack/CMakeLists.txt index c81b34e..f210474 100644 --- a/Tests/RunCMake/CPack/CMakeLists.txt +++ b/Tests/RunCMake/CPack/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index ef4cf5e..ca02b76 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) include(RunCMake) include("${RunCMake_SOURCE_DIR}/CPackTestHelpers.cmake") diff --git a/Tests/RunCMake/CPackConfig/CMakeLists.txt b/Tests/RunCMake/CPackConfig/CMakeLists.txt index 1e071ec..2b3e1f9 100644 --- a/Tests/RunCMake/CPackConfig/CMakeLists.txt +++ b/Tests/RunCMake/CPackConfig/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST}) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt b/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt index 89ff7c4..404e162 100644 --- a/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt +++ b/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CPackSymlinks/testcpacksym.tar b/Tests/RunCMake/CPackSymlinks/testcpacksym.tar Binary files differindex c24af48..7cfcb36 100644 --- a/Tests/RunCMake/CPackSymlinks/testcpacksym.tar +++ b/Tests/RunCMake/CPackSymlinks/testcpacksym.tar diff --git a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt +++ b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt b/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt +++ b/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt new file mode 100644 index 0000000..06fce77 --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at CMP0145-Dart-NEW\.cmake:[0-9]+ \(include\): + include could not find requested file: + + Dart +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake b/Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake new file mode 100644 index 0000000..5b14ecc --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0145 NEW) +include(Dart) diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake b/Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake new file mode 100644 index 0000000..2f66c3f --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake @@ -0,0 +1,7 @@ +cmake_policy(SET CMP0145 OLD) +set(_FindDart_testing 1) +include(Dart) + +if(NOT _FindDart_included) + message(FATAL_ERROR "FindDart.cmake not included") +endif() diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt b/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt new file mode 100644 index 0000000..5a751fc --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(dev\) at CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\): + Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run + "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. + +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning \(dev\) at [^ +]*/Modules/Dart\.cmake:[0-9]+ \(message\): + Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run + "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\) + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake b/Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake new file mode 100644 index 0000000..1398dbe --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake @@ -0,0 +1,7 @@ +# Do not set CMP0145. +set(_FindDart_testing 1) +include(Dart) + +if(NOT _FindDart_included) + message(FATAL_ERROR "FindDart.cmake not included") +endif() diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt new file mode 100644 index 0000000..b045636 --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at CMP0145-FindDart-NEW\.cmake:[0-9]+ \(include\): + include could not find requested file: + + FindDart +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake new file mode 100644 index 0000000..c1227d6 --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0145 NEW) +include(FindDart) diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake b/Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake new file mode 100644 index 0000000..b9f3c76 --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake @@ -0,0 +1,7 @@ +cmake_policy(SET CMP0145 OLD) +set(_FindDart_testing 1) +include(FindDart) + +if(NOT _FindDart_included) + message(FATAL_ERROR "FindDart.cmake not included") +endif() diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt new file mode 100644 index 0000000..d076235 --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt @@ -0,0 +1,8 @@ +^CMake Warning \(dev\) at CMP0145-FindDart-WARN\.cmake:[0-9]+ \(include\): + Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run + "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. + +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake new file mode 100644 index 0000000..59febdf --- /dev/null +++ b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake @@ -0,0 +1,7 @@ +# Do not set CMP0145. +set(_FindDart_testing 1) +include(FindDart) + +if(NOT _FindDart_included) + message(FATAL_ERROR "FindDart.cmake not included") +endif() diff --git a/Tests/RunCMake/CTest/CMakeLists.txt b/Tests/RunCMake/CTest/CMakeLists.txt index f1a83e8..1319aec 100644 --- a/Tests/RunCMake/CTest/CMakeLists.txt +++ b/Tests/RunCMake/CTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) if(NOT NoProject) project(${RunCMake_TEST} NONE) endif() diff --git a/Tests/RunCMake/CTest/RunCMakeTest.cmake b/Tests/RunCMake/CTest/RunCMakeTest.cmake index b81f319..4c2c107 100644 --- a/Tests/RunCMake/CTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTest/RunCMakeTest.cmake @@ -39,3 +39,10 @@ endfunction() if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) run_SingleConfig() endif() + +run_cmake(CMP0145-Dart-OLD) +run_cmake(CMP0145-Dart-WARN) +run_cmake(CMP0145-Dart-NEW) +run_cmake(CMP0145-FindDart-OLD) +run_cmake(CMP0145-FindDart-WARN) +run_cmake(CMP0145-FindDart-NEW) diff --git a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt +++ b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CTestCommandLine/test.cmake.in b/Tests/RunCMake/CTestCommandLine/test.cmake.in index b82968a..11bede7 100644 --- a/Tests/RunCMake/CTestCommandLine/test.cmake.in +++ b/Tests/RunCMake/CTestCommandLine/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in b/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in index e9592f6..cfcf56d 100644 --- a/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in +++ b/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) project(TimeoutAfterMatch NONE) include(CTest) add_test(NAME SleepFor1Second COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_SOURCE_DIR}/SleepFor1Second.cmake) diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in b/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in index d049c9f..172d2c6 100644 --- a/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in +++ b/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/CacheNewline/CacheNewline.cmake b/Tests/RunCMake/CacheNewline/CacheNewline.cmake index 81851db..418a847 100644 --- a/Tests/RunCMake/CacheNewline/CacheNewline.cmake +++ b/Tests/RunCMake/CacheNewline/CacheNewline.cmake @@ -1,5 +1 @@ -cmake_minimum_required(VERSION 3.5) - -project(CacheNewlineTest NONE) - set(NEWLINE_VARIABLE "a\nb" CACHE STRING "Offending entry") diff --git a/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt b/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt index 4a13d29..9f18d8d 100644 --- a/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt +++ b/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt @@ -1,7 +1,5 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.9) project(${RunCMake_TEST} NONE) -cmake_policy(SET CMP0069 NEW) - include(CheckIPOSupported) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CheckModules/CMakeLists.txt b/Tests/RunCMake/CheckModules/CMakeLists.txt index 842c5cf..93ee9df 100644 --- a/Tests/RunCMake/CheckModules/CMakeLists.txt +++ b/Tests/RunCMake/CheckModules/CMakeLists.txt @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) -cmake_policy(SET CMP0054 NEW) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CommandLine/CMakeLists.txt b/Tests/RunCMake/CommandLine/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/CommandLine/CMakeLists.txt +++ b/Tests/RunCMake/CommandLine/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0004/CMP0004-WARN-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake index e69de29..e69de29 100644 --- a/Tests/RunCMake/CMP0004/CMP0004-WARN-stderr.txt +++ b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt new file mode 100644 index 0000000..c3329a0 --- /dev/null +++ b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt @@ -0,0 +1,5 @@ +^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 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake diff --git a/Tests/RunCMake/CommandLine/E_time-stdout.txt b/Tests/RunCMake/CommandLine/E_time-stdout.txt index a51446a..1a5e134 100644 --- a/Tests/RunCMake/CommandLine/E_time-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_time-stdout.txt @@ -1,3 +1,3 @@ ^hello world -Elapsed time: [^ +Elapsed time \(seconds\): [^ ]*$ diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 943be24..205949b 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) include(RunCMake) @@ -1101,6 +1101,13 @@ set(RunCMake_TEST_OPTIONS --profiling-format=google-trace --profiling-output=${P 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 11 2012") run_cmake_with_options(DeprecateVS11-WARN-ON -DCMAKE_WARN_VS11=ON) unset(ENV{CMAKE_WARN_VS11}) diff --git a/Tests/RunCMake/CommandLine/trace-expand-stderr.txt b/Tests/RunCMake/CommandLine/trace-expand-stderr.txt index 4fee9bc..b900686 100644 --- a/Tests/RunCMake/CommandLine/trace-expand-stderr.txt +++ b/Tests/RunCMake/CommandLine/trace-expand-stderr.txt @@ -1,2 +1,2 @@ -^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.0 \) +^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \) .*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(trace-expand NONE \) diff --git a/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt b/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt index 74429b6..88aad00 100644 --- a/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt +++ b/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt @@ -1,2 +1,2 @@ -^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.0 \) +^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \) .*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(trace-expand-warn-uninitialized NONE \) diff --git a/Tests/RunCMake/CommandLine/trace-stderr.txt b/Tests/RunCMake/CommandLine/trace-stderr.txt index 8e8ddfa..4bf3cff 100644 --- a/Tests/RunCMake/CommandLine/trace-stderr.txt +++ b/Tests/RunCMake/CommandLine/trace-stderr.txt @@ -1,2 +1,2 @@ -^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.0 \) +^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \) .*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(\${RunCMake_TEST} NONE \) diff --git a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake index 982cb89..4107aa4 100644 --- a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake +++ b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake @@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 3.24) -project(test C) +cmake_policy(VERSION 3.24) +enable_language(C) diff --git a/Tests/RunCMake/CommandLine/trace-try_compile.cmake b/Tests/RunCMake/CommandLine/trace-try_compile.cmake index 982cb89..4107aa4 100644 --- a/Tests/RunCMake/CommandLine/trace-try_compile.cmake +++ b/Tests/RunCMake/CommandLine/trace-try_compile.cmake @@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 3.24) -project(test C) +cmake_policy(VERSION 3.24) +enable_language(C) diff --git a/Tests/RunCMake/CommandLineTar/CMakeLists.txt b/Tests/RunCMake/CommandLineTar/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/CommandLineTar/CMakeLists.txt +++ b/Tests/RunCMake/CommandLineTar/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompatibleInterface/CMakeLists.txt b/Tests/RunCMake/CompatibleInterface/CMakeLists.txt index ebab7a3..12a7fd4 100644 --- a/Tests/RunCMake/CompatibleInterface/CMakeLists.txt +++ b/Tests/RunCMake/CompatibleInterface/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake b/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake index 64b52d9..60a4246 100644 --- a/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake +++ b/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake @@ -1,7 +1,4 @@ - -cmake_minimum_required(VERSION 3.3) - -project(CompatibleInterface) +enable_language(CXX) include(GenerateExportHeader) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/Tests/RunCMake/CompileDefinitions/CMakeLists.txt b/Tests/RunCMake/CompileDefinitions/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/CompileDefinitions/CMakeLists.txt +++ b/Tests/RunCMake/CompileDefinitions/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompileFeatures/CMakeLists.txt b/Tests/RunCMake/CompileFeatures/CMakeLists.txt index 3482e6b..12a7fd4 100644 --- a/Tests/RunCMake/CompileFeatures/CMakeLists.txt +++ b/Tests/RunCMake/CompileFeatures/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerArgs/CMakeLists.txt b/Tests/RunCMake/CompilerArgs/CMakeLists.txt index 18dfd26..93ee9df 100644 --- a/Tests/RunCMake/CompilerArgs/CMakeLists.txt +++ b/Tests/RunCMake/CompilerArgs/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerChange/CMakeLists.txt b/Tests/RunCMake/CompilerChange/CMakeLists.txt index 14c47ad..b41f3f3 100644 --- a/Tests/RunCMake/CompilerChange/CMakeLists.txt +++ b/Tests/RunCMake/CompilerChange/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) if(NOT RunCMake_TEST) set(RunCMake_TEST "$ENV{RunCMake_TEST}") # needed when cache is deleted endif() diff --git a/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt index 3313e31..544b65f 100644 --- a/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.* diff --git a/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt index 3313e31..544b65f 100644 --- a/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.* diff --git a/Tests/RunCMake/CompilerLauncher/CMakeLists.txt b/Tests/RunCMake/CompilerLauncher/CMakeLists.txt index 18dfd26..93ee9df 100644 --- a/Tests/RunCMake/CompilerLauncher/CMakeLists.txt +++ b/Tests/RunCMake/CompilerLauncher/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt index 3313e31..a6e8b0a 100644 --- a/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CUDA.* diff --git a/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt index 3313e31..a6e8b0a 100644 --- a/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CUDA.* diff --git a/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt index 3313e31..082c7b5 100644 --- a/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.* diff --git a/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt index 3313e31..082c7b5 100644 --- a/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.* diff --git a/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt index 3313e31..9f8c754 100644 --- a/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=Fortran.* diff --git a/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt index 3313e31..9f8c754 100644 --- a/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=Fortran.* diff --git a/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt index 3313e31..354e317 100644 --- a/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=HIP.* diff --git a/Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt deleted file mode 100644 index 3313e31..0000000 --- a/Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt +++ /dev/null @@ -1 +0,0 @@ -.*-E env USED_LAUNCHER=1.* diff --git a/Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt new file mode 100644 index 0000000..354e317 --- /dev/null +++ b/Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt @@ -0,0 +1 @@ +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=HIP.* diff --git a/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt index 3313e31..6b71839 100644 --- a/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=ISPC.* diff --git a/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt index 3313e31..6b71839 100644 --- a/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=ISPC.* diff --git a/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt index 3313e31..d2efd3d 100644 --- a/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.* diff --git a/Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt deleted file mode 100644 index 3313e31..0000000 --- a/Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt +++ /dev/null @@ -1 +0,0 @@ -.*-E env USED_LAUNCHER=1.* diff --git a/Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt new file mode 100644 index 0000000..d2efd3d --- /dev/null +++ b/Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt @@ -0,0 +1 @@ +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.* diff --git a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt index 3313e31..0082ab2 100644 --- a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt +++ b/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.* diff --git a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt deleted file mode 100644 index 3313e31..0000000 --- a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt +++ /dev/null @@ -1 +0,0 @@ -.*-E env USED_LAUNCHER=1.* diff --git a/Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt new file mode 100644 index 0000000..0082ab2 --- /dev/null +++ b/Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt @@ -0,0 +1 @@ +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.* diff --git a/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake b/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake index e6a2605..b051a19 100644 --- a/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake +++ b/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake @@ -18,7 +18,7 @@ endfunction() function(run_compiler_launcher_env lang) string(REGEX REPLACE "-.*" "" core_lang "${lang}") # Use the noop genexp $<PATH:...> genexp to validate genexp support. - set(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER} "$<PATH:CMAKE_PATH,${CMAKE_COMMAND}>;-E;env;USED_LAUNCHER=1") + set(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER} "$<PATH:CMAKE_PATH,${CMAKE_COMMAND}>;-E;env;USED_LAUNCHER=1;TARGET_NAME=$<TARGET_PROPERTY:NAME>;LANGUAGE=$<COMPILE_LANGUAGE>") run_compiler_launcher(${lang}) unset(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER}) endfunction() diff --git a/Tests/RunCMake/Configure/CMakeLists.txt b/Tests/RunCMake/Configure/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/Configure/CMakeLists.txt +++ b/Tests/RunCMake/Configure/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt index 2d75985..12a7fd4 100644 --- a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt +++ b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake b/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake index 208ea20..f3974ea 100644 --- a/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake +++ b/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) foreach(p CMP0029 diff --git a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt +++ b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExternalData/CMakeLists.txt b/Tests/RunCMake/ExternalData/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/ExternalData/CMakeLists.txt +++ b/Tests/RunCMake/ExternalData/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt b/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt new file mode 100644 index 0000000..650be64 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt @@ -0,0 +1,10 @@ +^(CMake Deprecation Warning at Add_StepDependencies.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0114 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/ExternalProject/Add_StepDependencies.cmake b/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake index 364bf9e..02c7c8e 100644 --- a/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION ${CMAKE_VERSION}) +cmake_policy(VERSION ${CMAKE_VERSION}) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) else() diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt new file mode 100644 index 0000000..c142541 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt @@ -0,0 +1,10 @@ +^(CMake Deprecation Warning at Add_StepDependencies_no_target.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0114 will be removed from a future version + of CMake. + + 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/ExternalProject/Add_StepDependencies_no_target.cmake b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake index da823cd..31b7baf 100644 --- a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION ${CMAKE_VERSION}) +cmake_policy(VERSION ${CMAKE_VERSION}) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) else() diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt index 2b0feb6..2428b8c 100644 --- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt +++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt @@ -1,4 +1,15 @@ -^CMake Warning \(dev\) at [^ +^CMake Deprecation Warning at NO_DEPENDS-CMP0114-OLD.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0114 will be removed from a future version + of CMake. + + 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\) at [^ ]*/Modules/ExternalProject.cmake:[0-9]+ \(message\): Using NO_DEPENDS for "configure" step might break parallel builds Call Stack \(most recent call first\): diff --git a/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt b/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt new file mode 100644 index 0000000..6dd7cb0 --- /dev/null +++ b/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at Steps-CMP0114-OLD.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0114 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/ExternalProject/UsesTerminal-check.cmake b/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake index 2946c0b..351d70f 100644 --- a/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake +++ b/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.3) - # If we are using the Ninja generator, we can check and verify that the # USES_TERMINAL option actually works by examining the Ninja build file. # This is the only way, since CMake doesn't offer a way to examine the diff --git a/Tests/RunCMake/ExtraGenerators/CMakeLists.txt b/Tests/RunCMake/ExtraGenerators/CMakeLists.txt new file mode 100644 index 0000000..93ee9df --- /dev/null +++ b/Tests/RunCMake/ExtraGenerators/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.5) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake b/Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake new file mode 100644 index 0000000..fbef79c --- /dev/null +++ b/Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake @@ -0,0 +1,19 @@ +include(RunCMake) + +foreach( + extraGenerator + IN ITEMS + "CodeBlocks" + "CodeLite" + "Eclipse CDT4" + "Kate" + "Sublime Text 2" + ) + block() + set(RunCMake_GENERATOR "${extraGenerator} - ${RunCMake_GENERATOR}") + set(RunCMake_TEST_VARIANT_DESCRIPTION ": ${RunCMake_GENERATOR}") + string(REPLACE " " "" extraGeneratorNoSpaces "${extraGenerator}") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Simple-${extraGeneratorNoSpaces}) + run_cmake(Simple) + endblock() +endforeach() diff --git a/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt b/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt new file mode 100644 index 0000000..e327a9f --- /dev/null +++ b/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt @@ -0,0 +1,8 @@ +^CMake Deprecation Warning: + Support for "Extra Generators" like + + [^ +]+ + + is deprecated and will be removed from a future version of CMake\. IDEs may + use the cmake-file-api\(7\) to view CMake-generated project build trees\. diff --git a/Tests/RunCMake/ExtraGenerators/Simple.cmake b/Tests/RunCMake/ExtraGenerators/Simple.cmake new file mode 100644 index 0000000..d077046 --- /dev/null +++ b/Tests/RunCMake/ExtraGenerators/Simple.cmake @@ -0,0 +1,4 @@ +enable_language(C) +enable_language(CXX) + +add_subdirectory(../../Simple Simple) diff --git a/Tests/RunCMake/FPHSA/CMakeLists.txt b/Tests/RunCMake/FPHSA/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/FPHSA/CMakeLists.txt +++ b/Tests/RunCMake/FPHSA/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FeatureSummary/CMakeLists.txt b/Tests/RunCMake/FeatureSummary/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/FeatureSummary/CMakeLists.txt +++ b/Tests/RunCMake/FeatureSummary/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake b/Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake new file mode 100644 index 0000000..f8ee749 --- /dev/null +++ b/Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake @@ -0,0 +1,2 @@ +set(ENV{CMAKE_TOOLCHAIN_FILE} path/to/somewhere/iDoNotExist.cmake) +include(DownloadFile.cmake) diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake index bb27491..3781089 100644 --- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake +++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake @@ -7,6 +7,7 @@ run_cmake(DirectIgnoresDetails) run_cmake(FirstDetailsWin) run_cmake(DownloadTwice) run_cmake(DownloadFile) +run_cmake(IgnoreToolchainFile) run_cmake(SameGenerator) run_cmake(System) run_cmake(VarDefinitions) diff --git a/Tests/RunCMake/File_Archive/CMakeLists.txt b/Tests/RunCMake/File_Archive/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/File_Archive/CMakeLists.txt +++ b/Tests/RunCMake/File_Archive/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/File_Generate/CMakeLists.txt b/Tests/RunCMake/File_Generate/CMakeLists.txt index 3178de5..eec672f 100644 --- a/Tests/RunCMake/File_Generate/CMakeLists.txt +++ b/Tests/RunCMake/File_Generate/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) if(NOT TEST_FILE) set(TEST_FILE ${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt index 2c385c4..f51d05a 100644 --- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt +++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt @@ -1,4 +1,4 @@ -^CMake Error at SourceProperty-CMP0070-NEW.cmake:[0-9]+ \(add_library\): +^CMake Error at SourceProperty-CMP0070-NEW.cmake:[0-9]+ \(target_sources\): Cannot find source file: .*\/relative-output-NEW\.c diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt index fcb53a7..07b0026 100644 --- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt +++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt @@ -10,7 +10,7 @@ Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) -CMake Error at SourceProperty-CMP0070-OLD.cmake:[0-9]+ \(add_library\): +CMake Error at SourceProperty-CMP0070-OLD.cmake:[0-9]+ \(target_sources\): Cannot find source file: .*\/relative-output-OLD\.c diff --git a/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake b/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake index e0585ee..b20f824 100644 --- a/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake +++ b/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.7) -project(testFindGTK2 C) +enable_language(C) # First call find_package(GTK2 REQUIRED) diff --git a/Tests/RunCMake/FindLua/CMakeLists.txt b/Tests/RunCMake/FindLua/CMakeLists.txt index a2c4d98..e6c41a5 100644 --- a/Tests/RunCMake/FindLua/CMakeLists.txt +++ b/Tests/RunCMake/FindLua/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} C) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindMatlab/CMakeLists.txt b/Tests/RunCMake/FindMatlab/CMakeLists.txt index 1b9a957..93ee9df 100644 --- a/Tests/RunCMake/FindMatlab/CMakeLists.txt +++ b/Tests/RunCMake/FindMatlab/CMakeLists.txt @@ -1,3 +1,3 @@ - -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) +project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindMatlab/MatlabTest1.cmake b/Tests/RunCMake/FindMatlab/MatlabTest1.cmake index b4cc741..8eaf903 100644 --- a/Tests/RunCMake/FindMatlab/MatlabTest1.cmake +++ b/Tests/RunCMake/FindMatlab/MatlabTest1.cmake @@ -1,7 +1,6 @@ - -cmake_minimum_required (VERSION 2.8.12) +enable_language(C) +enable_language(CXX) enable_testing() -project(test_should_fail) if(NOT "${matlab_root}" STREQUAL "") set(Matlab_ROOT_DIR ${matlab_root}) diff --git a/Tests/RunCMake/FindMatlab/MatlabTest2.cmake b/Tests/RunCMake/FindMatlab/MatlabTest2.cmake index 4295d3c..95b1c22 100644 --- a/Tests/RunCMake/FindMatlab/MatlabTest2.cmake +++ b/Tests/RunCMake/FindMatlab/MatlabTest2.cmake @@ -1,6 +1,6 @@ -cmake_minimum_required (VERSION 2.8.12) +enable_language(C) +enable_language(CXX) enable_testing() -project(findmatlab_runcmake_test2) if(NOT DEFINED matlab_required) set(matlab_required REQUIRED) diff --git a/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake b/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake index deebf89..45dc799 100644 --- a/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake @@ -1,7 +1,5 @@ - include(RunCMake) - if(NOT "${MCR_ROOT}" STREQUAL "") if(NOT EXISTS "${MCR_ROOT}") message(FATAL_ERROR "MCR does not exist ${MCR_ROOT}") diff --git a/Tests/RunCMake/FindOpenSSL/version-exact.cmake b/Tests/RunCMake/FindOpenSSL/version-exact.cmake index 29c2ce3..11826cf 100644 --- a/Tests/RunCMake/FindOpenSSL/version-exact.cmake +++ b/Tests/RunCMake/FindOpenSSL/version-exact.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.19...3.20) - find_package (OpenSSL REQUIRED COMPONENTS Crypto) # Store version without a possibly trailing letter. string (REGEX MATCH "^([0-9.]+)" version "${OPENSSL_VERSION}") diff --git a/Tests/RunCMake/FindOpenSSL/version-range.cmake b/Tests/RunCMake/FindOpenSSL/version-range.cmake index 9390032..f9689b6 100644 --- a/Tests/RunCMake/FindOpenSSL/version-range.cmake +++ b/Tests/RunCMake/FindOpenSSL/version-range.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.19...3.20) - find_package (OpenSSL REQUIRED COMPONENTS Crypto) # Store version without a possibly trailing letter. string (REGEX MATCH "^([0-9.]+)" version "${OPENSSL_VERSION}") diff --git a/Tests/RunCMake/FindOpenSSL/version.cmake b/Tests/RunCMake/FindOpenSSL/version.cmake index d06cd1f..3d151ab 100644 --- a/Tests/RunCMake/FindOpenSSL/version.cmake +++ b/Tests/RunCMake/FindOpenSSL/version.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.19...3.20) - find_package (OpenSSL REQUIRED COMPONENTS Crypto) # Store version without a possibly trailing letter. string (REGEX MATCH "^([0-9.]+)" version "${OPENSSL_VERSION}") diff --git a/Tests/RunCMake/FindPkgConfig/CMakeLists.txt b/Tests/RunCMake/FindPkgConfig/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/FindPkgConfig/CMakeLists.txt +++ b/Tests/RunCMake/FindPkgConfig/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake index 69ab4da..457747f 100644 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.12) - -project(FindPkgConfig_IMPORTED_TARGET C) +enable_language(C) find_package(PkgConfig REQUIRED) pkg_check_modules(NCURSES IMPORTED_TARGET QUIET ncurses) diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake index f7a9815..95a2e32 100644 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.12) - -project(FindPkgConfig_IMPORTED_TARGET C) +enable_language(C) find_package(PkgConfig REQUIRED) diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake index d0046ca..15baa0d 100644 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.3) - find_package(PkgConfig REQUIRED) pkg_check_modules(NCURSES QUIET ncurses) diff --git a/Tests/RunCMake/FindSWIG/version-exact.cmake b/Tests/RunCMake/FindSWIG/version-exact.cmake index ec3651f..98903ff 100644 --- a/Tests/RunCMake/FindSWIG/version-exact.cmake +++ b/Tests/RunCMake/FindSWIG/version-exact.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.18...3.19) - find_package (SWIG) if (NOT SWIG_FOUND) message (FATAL_ERROR "Failed to find SWIG") diff --git a/Tests/RunCMake/FindSWIG/version-range.cmake b/Tests/RunCMake/FindSWIG/version-range.cmake index 7ba1134..e776961 100644 --- a/Tests/RunCMake/FindSWIG/version-range.cmake +++ b/Tests/RunCMake/FindSWIG/version-range.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.18...3.19) - find_package (SWIG) if (NOT SWIG_FOUND) message (FATAL_ERROR "Failed to find SWIG") diff --git a/Tests/RunCMake/FindSWIG/version.cmake b/Tests/RunCMake/FindSWIG/version.cmake index a4f1c39..b5ed6a7 100644 --- a/Tests/RunCMake/FindSWIG/version.cmake +++ b/Tests/RunCMake/FindSWIG/version.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.18...3.19) - find_package (SWIG 1.0) if (NOT SWIG_FOUND) message (FATAL_ERROR "Failed to find SWIG with version 1.0") diff --git a/Tests/RunCMake/Framework/CMakeLists.txt b/Tests/RunCMake/Framework/CMakeLists.txt index 6dd8cdf..93ee9df 100644 --- a/Tests/RunCMake/Framework/CMakeLists.txt +++ b/Tests/RunCMake/Framework/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Framework/FrameworkConsumption.cmake b/Tests/RunCMake/Framework/FrameworkConsumption.cmake index 58b70a3..2180cf9 100644 --- a/Tests/RunCMake/Framework/FrameworkConsumption.cmake +++ b/Tests/RunCMake/Framework/FrameworkConsumption.cmake @@ -1,5 +1,3 @@ - -cmake_minimum_required(VERSION 3.22...3.24) enable_language(C) # Create framework and ensure header is placed in Headers @@ -24,10 +22,14 @@ set_target_properties(Gui2 PROPERTIES ) add_executable(app2 main2.c) -set_target_properties(Gui2 PROPERTIES - PUBLIC_HEADER "${input_header}" - FRAMEWORK TRUE +set_target_properties(app2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin ) target_link_libraries(app2 PRIVATE Gui2) + + +# Same test with STATIC consumer +add_library(Consumer STATIC consumer.c) + +target_link_libraries(Consumer PRIVATE Gui2) diff --git a/Tests/RunCMake/Framework/FrameworkLayout.cmake b/Tests/RunCMake/Framework/FrameworkLayout.cmake index 84012aa..d09e8a0 100644 --- a/Tests/RunCMake/Framework/FrameworkLayout.cmake +++ b/Tests/RunCMake/Framework/FrameworkLayout.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.4) enable_language(C) set(CMAKE_CONFIGURATION_TYPES "Debug" CACHE INTERNAL "Supported configuration types") diff --git a/Tests/RunCMake/Framework/consumer.c b/Tests/RunCMake/Framework/consumer.c new file mode 100644 index 0000000..a578976 --- /dev/null +++ b/Tests/RunCMake/Framework/consumer.c @@ -0,0 +1,9 @@ + +#include <Gui2/Gui.h> + +int consumer() +{ + foo(); + + return 0; +} diff --git a/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt b/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt +++ b/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt b/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in index cc5ff54..d1cb61b 100644 --- a/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in @@ -31,4 +31,28 @@ if (NOT output STREQUAL reference) endif() +###################################### +## tests with list of paths +###################################### +unset (reference) +foreach(item IN ITEMS "../../a/d" "/a/d/../e") + cmake_path(ABSOLUTE_PATH item BASE_DIRECTORY "/x/y/a/f") + list(APPEND reference "${item}") +endforeach() +set(output "$<PATH:ABSOLUTE_PATH,../../a/d;/a/d/../e,/x/y/a/f>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + +unset (reference) +foreach(item IN ITEMS "../../a/d" "/a/d/../e") + cmake_path(ABSOLUTE_PATH item BASE_DIRECTORY "/x/y/a/f" NORMALIZE) + list(APPEND reference "${item}") +endforeach() +set(output "$<PATH:ABSOLUTE_PATH,NORMALIZE,../../a/d;/a/d/../e,/x/y/a/f>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + + check_errors("PATH:ABSOLUTE_PATH" ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in b/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in index ab967a2..1955480 100644 --- a/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in @@ -65,4 +65,39 @@ if (WIN32) endif() endif() + +###################################### +## tests with list of paths +###################################### +unset(reference) +foreach(item IN ITEMS "/a/b" "/x/y") + cmake_path (APPEND result "${item}" "c") + list(APPEND reference "${result}") +endforeach() +set(output "$<PATH:APPEND,/a/b;/x/y,c>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + +unset(reference) +foreach(item IN ITEMS "a" "c") + cmake_path (APPEND item "") + list(APPEND reference "${item}") +endforeach() +set(output "$<PATH:APPEND,a;c,>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + +unset(reference) +foreach(item IN ITEMS "a/" "c/") + cmake_path (APPEND item "/b") + list(APPEND reference "${item}") +endforeach() +set(output "$<PATH:APPEND,a/;c/,/b>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + + check_errors ("PATH:APPEND" ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in index 41205fa..29ebf16 100644 --- a/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in @@ -50,4 +50,46 @@ if (WIN32) endif() +###################################### +## tests with list of paths +###################################### +set(reference "/x/y/z/../../a/d;/x/y/z/../../b/e") +set(output "$<PATH:CMAKE_PATH,/x/y/z/../../a/d;/x/y/z/../../b/e>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + +unset(reference) +foreach(path IN ITEMS "/x/y/z/../../a/d" "/x/y/z/../../b/e") + cmake_path(SET result NORMALIZE "${path}") + list(APPEND reference "${result}") +endforeach() +set(output "$<PATH:CMAKE_PATH,NORMALIZE,/x/y/z/../../a/d;/x/y/z/../../b/e>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + +if (WIN32) + unset(reference) + foreach(path IN ITEMS "/x\\y/z\\..\\../a/d" "/x\\y/z\\..\\../b/e") + cmake_path(SET result "${path}") + list(APPEND reference "${result}") + endforeach() + set(output "$<PATH:CMAKE_PATH,/x\y/z\..\../a/d;/x\y/z\..\../b/e>") + if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") + endif() + + unset(reference) + foreach(path IN ITEMS "/x\\y/z\\..\\../a/d" "/x\\y/z\\..\\../b/e") + cmake_path(SET result NORMALIZE "${path}") + list(APPEND reference "${result}") + endforeach() + set(output "$<PATH:CMAKE_PATH,NORMALIZE,/x\y/z\..\../a/d;/x\y/z\..\../b/e>") + if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") + endif() +endif() + + check_errors("PATH:CMAKE_PATH" ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/CMakeLists.txt b/Tests/RunCMake/GenEx-PATH/CMakeLists.txt index f9748e9..5161b99 100644 --- a/Tests/RunCMake/GenEx-PATH/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-PATH/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.18...3.24) +cmake_minimum_required(VERSION 3.18...3.25) project(${RunCMake_TEST} NONE) diff --git a/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in b/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in index b58998c..e2acde4 100644 --- a/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0140 NEW) include ("${RunCMake_SOURCE_DIR}/check_errors.cmake") unset (errors) @@ -308,4 +309,126 @@ if (NOT output STREQUAL reference) endif() +###################################### +## third, tests with list of paths +###################################### +if (WIN32) + set (paths "C:/aa/bb/cc.ext1.ext2" "D:/xx/yy/zz.ext3.ext4") +else() + set (paths "/aa/bb/cc.ext1.ext2" "/xx/yy/zz.ext3.ext4") +endif() + +function (compute_reference action) + unset(reference) + foreach (path IN LISTS paths) + cmake_path(GET path ${ARGV} result) + list(APPEND reference "${result}") + endforeach() + if (reference STREQUAL "") + # define the list as 2 empty elements + set(reference ";") + endif() + + return(PROPAGATE reference) +endfunction() + +compute_reference(ROOT_NAME) +if (WIN32) + set(output "$<PATH:GET_ROOT_NAME,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_ROOT_NAME,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "ROOT_NAME returns bad data: ${output}") +endif() + +compute_reference(ROOT_DIRECTORY) +if (WIN32) + set(output "$<PATH:GET_ROOT_DIRECTORY,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_ROOT_DIRECTORY,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "ROOT_DIRECTORY returns bad data: ${output}") +endif() + +compute_reference(ROOT_PATH) +if (WIN32) + set(output "$<PATH:GET_ROOT_PATH,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_ROOT_PATH,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "ROOT_PATH returns bad data: ${output}") +endif() + +compute_reference(FILENAME) +if (WIN32) + set(output "$<PATH:GET_FILENAME,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_FILENAME,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "FILENAME returns bad data: ${output}") +endif() + +compute_reference(EXTENSION) +if (WIN32) + set(output "$<PATH:GET_EXTENSION,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_EXTENSION,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "EXTENSION returns bad data: ${output}") +endif() +compute_reference(EXTENSION LAST_ONLY) +if (WIN32) + set(output "$<PATH:GET_EXTENSION,LAST_ONLY,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_EXTENSION,LAST_ONLY,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "EXTENSION LAST_ONLY returns bad data: ${output}") +endif() + +compute_reference(STEM) +if (WIN32) + set(output "$<PATH:GET_STEM,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_STEM,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "STEM returns bad data: ${output}") +endif() +compute_reference(STEM LAST_ONLY) +if (WIN32) + set(output "$<PATH:GET_STEM,LAST_ONLY,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_STEM,LAST_ONLY,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "STEM LAST_ONLY returns bad data: ${reference}") +endif() + +compute_reference(RELATIVE_PART) +if (WIN32) + set(output "$<PATH:GET_RELATIVE_PART,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_RELATIVE_PART,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "RELATIVE_PART returns bad data: ${output}") +endif() + +compute_reference(PARENT_PATH) +if (WIN32) + set(output "$<PATH:GET_PARENT_PATH,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>") +else() + set (output "$<PATH:GET_PARENT_PATH,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>") +endif() +if (NOT output STREQUAL reference) + list (APPEND errors "PARENT_PATH returns bad data: ${output}") +endif() + + check_errors("PATH:GET..." ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in index e6cc4a3..81e4c0d 100644 --- a/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in @@ -40,4 +40,18 @@ if (WIN32) endif() +###################################### +## tests with list of paths +###################################### +unset (reference) +foreach(item IN ITEMS "a/./b/.." "x/.//y/z//..") + cmake_path(NORMAL_PATH item) + list(APPEND reference "${item}") +endforeach() +set(output "$<PATH:NORMAL_PATH,a/./b/..;x/.//y/z//..>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + + check_errors("PATH:NORMAL_PATH" ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in index 11d73ad..7670f4f 100644 --- a/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in @@ -61,4 +61,18 @@ if (WIN32) endif() +###################################### +## tests with list of paths +###################################### +unset (reference) +foreach(item IN ITEMS "/a//d" "/a/b/e") + cmake_path(RELATIVE_PATH item BASE_DIRECTORY "/a/b/c") + list(APPEND reference "${item}") +endforeach() +set(output "$<PATH:RELATIVE_PATH,/a//d;/a/b/e,/a/b/c>") +if (NOT output STREQUAL reference) + list (APPEND errors "'${output}' instead of '${reference}'") +endif() + + check_errors("PATH:RELATIVE_PATH" ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in b/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in index cce4143..a365efe 100644 --- a/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0140 NEW) include ("${RunCMake_SOURCE_DIR}/check_errors.cmake") unset (errors) @@ -62,4 +63,39 @@ if (NOT output STREQUAL reference) endif() +###################################### +## tests with list of paths +###################################### +function (compute_reference action) + unset(reference) + foreach (path IN LISTS paths) + cmake_path(${action} path ${ARGN}) + list(APPEND reference "${path}") + endforeach() + + return(PROPAGATE reference) +endfunction() + +set (paths "a/b/c.e.f" "g/h/i.j.k") +compute_reference(REMOVE_FILENAME) +set(output "$<PATH:REMOVE_FILENAME,a/b/c.e.f;g/h/i.j.k>") +if (NOT output STREQUAL reference) + list (APPEND errors "FILENAME: '${output}' instead of '${reference}'") +endif() + +set (paths "a/b/c.e.f" "g/h/i.j.k") +compute_reference(REMOVE_EXTENSION) +set(output "$<PATH:REMOVE_EXTENSION,a/b/c.e.f;g/h/i.j.k>") +if (NOT output STREQUAL reference) + list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'") +endif() + +set (reference "a/b/c.e.f" "g/h/i.j.k") +compute_reference(REMOVE_EXTENSION LAST_ONLY) +set(output "$<PATH:REMOVE_EXTENSION,LAST_ONLY,a/b/c.e.f;g/h/i.j.k>") +if (NOT output STREQUAL reference) + list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'") +endif() + + check_errors("PATH:REMOVE..." ${errors}) diff --git a/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in b/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in index 5bb04c3..2d02152 100644 --- a/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in +++ b/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0140 NEW) include ("${RunCMake_SOURCE_DIR}/check_errors.cmake") unset (errors) @@ -70,4 +71,39 @@ if (NOT output STREQUAL reference) endif() +###################################### +## tests with list of paths +###################################### +function (compute_reference action new_value) + unset(reference) + foreach (path IN LISTS paths) + cmake_path(${action} path "${new_value}" ${ARGN}) + list(APPEND reference "${path}") + endforeach() + + return(PROPAGATE reference) +endfunction() + +set (paths "a/b/c.e.f" "g/h/i.j.k") +compute_reference(REPLACE_FILENAME "x.y") +set(output "$<PATH:REPLACE_FILENAME,a/b/c.e.f;g/h/i.j.k,x.y>") +if (NOT output STREQUAL reference) + list (APPEND errors "FILENAME: '${output}' instead of '${reference}'") +endif() + +set (paths "a/b/c.e.f" "g/h/i.j.k") +compute_reference(REPLACE_EXTENSION ".x") +set(output "$<PATH:REPLACE_EXTENSION,a/b/c.e.f;g/h/i.j.k,.x>") +if (NOT output STREQUAL reference) + list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'") +endif() + +set (paths "a/b/c.e.f" "g/h/i.j.k") +compute_reference(REPLACE_EXTENSION ".x" LAST_ONLY) +set(output "$<PATH:REPLACE_EXTENSION,LAST_ONLY,a/b/c.e.f;g/h/i.j.k,.x>") +if (NOT output STREQUAL reference) + list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'") +endif() + + check_errors("PATH:REPLACE..." ${errors}) diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt index 4b3de84..93ee9df 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake index 7eec527..187e7d6 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake +++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.14) - enable_language (C) set (GENERATE_CONTENT [[ diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake index 1963244..d550431 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake +++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.14) - enable_language (C) set (GENERATE_CONTENT [[ diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake index cc9cd5a..01926fe 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake +++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.14) - enable_language (C) set (win_platforms Windows CYGWIN MSYS) diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake index edfb40c..a9a76bf 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake +++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.14) - enable_language (C) set (win_platforms Windows CYGWIN MSYS) diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake index 3ee42a5..ddf3887 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake +++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.14) - enable_language (C) set (win_platforms Windows CYGWIN MSYS) diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake index 1fe75d9..9c5d932 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake +++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.14) - enable_language (C) set (win_platforms Windows CYGWIN MSYS) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt new file mode 100644 index 0000000..93ee9df --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.5) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake new file mode 100644 index 0000000..04ff640 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake @@ -0,0 +1,21 @@ +include(RunCMake) + +cmake_policy(SET CMP0057 NEW) + +function(run_cmake_with_config test) + if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + run_cmake(${test}) +endfunction() + +run_cmake(TARGET_LINKER_IMPORT_FILE-non-valid-target) +run_cmake(TARGET_LINKER_LIBRARY_FILE-non-valid-target) +run_cmake_with_config(TARGET_IMPORT_FILE) +run_cmake_with_config(TARGET_IMPORT_FILE_SUFFIX) + +set (Windows_platforms Windows CYGWIN MSYS) +if (NOT CMAKE_HOST_SYSTEM_NAME IN_LIST Windows_platforms) + run_cmake(TARGET_SONAME_IMPORT_FILE-non-valid-target) + run_cmake_with_config(TARGET_SONAME_IMPORT_FILE) +endif() diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake new file mode 100644 index 0000000..9a101fc --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/TARGET_IMPORT_FILE-Release-generated.cmake") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake new file mode 100644 index 0000000..08765a6 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake @@ -0,0 +1,47 @@ +enable_language(C) + +set (platforms_with_import Windows CYGWIN MSYS) + +set (GENERATE_CONTENT [[ +macro (CHECK_VALUE test_msg value expected) + if (NOT "${value}" STREQUAL "${expected}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") + endif() +endmacro() +]]) + +add_library (shared1 SHARED empty.c) +add_library (static1 STATIC empty.c) +add_executable (exec1 empty.c) + + +string (APPEND GENERATE_CONTENT +"\ncheck_value (\"TARGET_IMPORT_FILE shared library\" \"$<TARGET_IMPORT_FILE:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared1>,>\") +check_value (\"TARGET_LINKER_FILE shared library\" \"$<TARGET_LINKER_FILE:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared1>,$<TARGET_LINKER_LIBRARY_FILE:shared1>>\") +check_value (\"TARGET_IMPORT_FILE static library\" \"$<TARGET_IMPORT_FILE:static1>\" \"\") +check_value (\"TARGET_IMPORT_FILE executable\" \"$<TARGET_IMPORT_FILE:exec1>\" \"\")\n") + + +set(lib_with_import ${platforms_with_import}) +set(exec_with_import ${platforms_with_import}) +if (APPLE AND CMAKE_TAPI) + list(APPEND lib_with_import Darwin) +endif() +if (CMAKE_SYSTEM_NAME STREQUAL "AIX") + list(APPEND exec_with_import "AIX") +endif() +set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS TRUE) +set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) + +add_library (shared2 SHARED empty.c) +add_executable (exec2 empty.c) + + +string (APPEND GENERATE_CONTENT +"\ncheck_value (\"TARGET_IMPORT_FILE shared library\" \"$<TARGET_IMPORT_FILE:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${lib_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared2>,>\") +check_value (\"TARGET_LINKER_FILE shared library\" \"$<TARGET_LINKER_FILE:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${lib_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared2>,$<TARGET_LINKER_LIBRARY_FILE:shared2>>\") +check_value (\"TARGET_IMPORT_FILE executable\" \"$<TARGET_IMPORT_FILE:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${exec_with_import}>,$<TARGET_LINKER_IMPORT_FILE:exec2>,>\")\n") + + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_IMPORT_FILE-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake new file mode 100644 index 0000000..2a1357a --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/TARGET_IMPORT_FILE_SUFFIX-Release-generated.cmake") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake new file mode 100644 index 0000000..933471b --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake @@ -0,0 +1,44 @@ +enable_language (C) + +set (platforms_with_import Windows CYGWIN MSYS) + +set (GENERATE_CONTENT [[ +macro (CHECK_VALUE test_msg value expected) + if (NOT "${value}" STREQUAL "${expected}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") + endif() +endmacro() +]]) + +add_library (shared1 SHARED empty.c) +add_library (static1 STATIC empty.c) +add_executable (exec1 empty.c) + +string (APPEND GENERATE_CONTENT +"\ncheck_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec1>\" \"\") +check_value (\"TARGET_IMPORT_FILE_SUFFIX shared default\" \"$<TARGET_IMPORT_FILE_SUFFIX:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:shared1>,>\") +check_value (\"TARGET_FILE_SUFFIX static default\" \"$<TARGET_IMPORT_FILE_SUFFIX:static1>\" \"\") +check_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec1>\" \"\")\n") + + + +if (APPLE AND CMAKE_TAPI) + list(APPEND platforms_with_import Darwin) +endif() +if (CMAKE_SYSTEM_NAME STREQUAL "AIX") + list(APPEND platforms_with_import AIX) +endif() +set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS TRUE) +set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) + +add_library (shared2 SHARED empty.c) +add_executable (exec2 empty.c) + +string (APPEND GENERATE_CONTENT +"\ncheck_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:exec2>,>\") +check_value (\"TARGET_IMPORT_FILE_SUFFIX shared default\" \"$<TARGET_IMPORT_FILE_SUFFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:shared2>,>\")\n") + + + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_IMPORT_FILE_SUFFIX-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt new file mode 100644 index 0000000..8ba2223 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$<TARGET_LINKER_IMPORT_FILE:exe1> + + TARGET_LINKER_IMPORT_FILE is allowed only for libraries and executables + with ENABLE_EXPORTS. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake new file mode 100644 index 0000000..3f060cd --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +add_executable(exe1 empty.c) + +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" + CONTENT "[$<TARGET_LINKER_IMPORT_FILE:exe1>]" +) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt new file mode 100644 index 0000000..06e7b3a --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$<TARGET_LINKER_LIBRARY_FILE:exe1> + + TARGET_LINKER_LIBRARY_FILE is allowed only for libraries with + ENABLE_EXPORTS. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake new file mode 100644 index 0000000..bb95546 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +add_executable(exe1 empty.c) + +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" + CONTENT "[$<TARGET_LINKER_LIBRARY_FILE:exe1>]" +) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake new file mode 100644 index 0000000..ab4443e --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake @@ -0,0 +1 @@ +include ("${RunCMake_TEST_BINARY_DIR}/TARGET_SONAME_IMPORT_FILE-Release-generated.cmake") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt new file mode 100644 index 0000000..0640088 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$<TARGET_SONAME_IMPORT_FILE:static1> + + TARGET_SONAME_IMPORT_FILE is allowed only for SHARED libraries. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake new file mode 100644 index 0000000..cc79580 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake @@ -0,0 +1,8 @@ +enable_language(C) + +add_library (static1 STATIC empty.c) +set_property (TARGET static1 PROPERTY VERSION 2.5.0) +set_property (TARGET static1 PROPERTY SOVERSION 2.0.0) + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" + CONTENT "[$<TARGET_SONAME_IMPORT_FILE:static1>]") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake new file mode 100644 index 0000000..02ba513 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake @@ -0,0 +1,32 @@ +enable_language(C) + +set (GENERATE_CONTENT [[ +macro (CHECK_VALUE test_msg value expected) + if (NOT "${value}" STREQUAL "${expected}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") + endif() +endmacro() +]]) + +add_library (shared1 SHARED empty.c) +set_property (TARGET shared1 PROPERTY VERSION 2.5.0) +set_property (TARGET shared1 PROPERTY SOVERSION 2.0.0) + + +string (APPEND GENERATE_CONTENT +"\ncheck_value (\"TARGET_SONAME_IMPORT_FILE shared library\" \"$<TARGET_SONAME_IMPORT_FILE:shared1>\" \"\")\n") + + + +add_library (shared2 SHARED empty.c) +set_property(TARGET shared2 PROPERTY ENABLE_EXPORTS ON) +set_property (TARGET shared2 PROPERTY VERSION 2.5.0) +set_property (TARGET shared2 PROPERTY SOVERSION 2.0.0) + + +string (APPEND GENERATE_CONTENT +"\ncheck_value (\"TARGET_SONAME_IMPORT_FILE shared library\" \"$<TARGET_SONAME_IMPORT_FILE:shared2>\" \"$<$<BOOL:${CMAKE_TAPI}>:$<PATH:REPLACE_EXTENSION,LAST_ONLY,$<TARGET_SONAME_FILE:shared2>,.tbd>>\")\n") + + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_SONAME_IMPORT_FILE-$<CONFIG>-generated.cmake" + CONTENT "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake index 212c034..eed194b 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.17) - add_library(lib-global SHARED IMPORTED GLOBAL) add_library(alias-lib-global ALIAS lib-global) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt index 26a73f9..32d92d8 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) if(RunCMake_TEST STREQUAL "LOCATION") cmake_minimum_required(VERSION 2.8.12) # Leave CMP0026 unset. endif() diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake index e9855be..0f0c399 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.14) enable_language(C) add_library(foo1 STATIC empty.c) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt index a4c8dcd..fab2ce2 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt @@ -1,3 +1,10 @@ +^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ CMake Warning \(dev\) in CMakeLists\.txt: Policy CMP0026 is not set: Disallow use of the LOCATION target property. Run "cmake --help-policy CMP0026" for policy details. Use the cmake_policy diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake deleted file mode 100644 index e19598e..0000000 --- a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake +++ /dev/null @@ -1,15 +0,0 @@ -function(check_genex expected actual) - if(NOT expected STREQUAL actual) - string(APPEND RunCMake_TEST_FAILED "Expected DLLs:\n") - foreach(dll IN LISTS expected) - string(APPEND RunCMake_TEST_FAILED " ${dll}\n") - endforeach() - string(APPEND RunCMake_TEST_FAILED "Actual DLLs:\n") - foreach(dll IN LISTS actual) - string(APPEND RunCMake_TEST_FAILED " ${dll}\n") - endforeach() - endif() - set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) -endfunction() - -include("${RunCMake_TEST_BINARY_DIR}/dlls.cmake") diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake new file mode 100644 index 0000000..6b05b4e --- /dev/null +++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake @@ -0,0 +1,15 @@ +function(check_genex expected actual) + if(NOT expected STREQUAL actual) + string(APPEND RunCMake_TEST_FAILED "Expected items:\n") + foreach(item IN LISTS expected) + string(APPEND RunCMake_TEST_FAILED " ${item}\n") + endforeach() + string(APPEND RunCMake_TEST_FAILED "Actual items:\n") + foreach(item IN LISTS actual) + string(APPEND RunCMake_TEST_FAILED " ${item}\n") + endforeach() + endif() + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() + +include("${RunCMake_TEST_BINARY_DIR}/dlls.cmake") diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake index 806f0b6..c38fa39 100644 --- a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake +++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake @@ -4,6 +4,10 @@ add_executable(exe main.c) add_library(lib1 SHARED lib1.c) add_library(lib2 SHARED lib2.c) add_library(lib3 SHARED lib3.c) +if(WIN32 OR CYGWIN) + set_property(TARGET lib3 PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/SomeSubDir/") +endif() + add_library(static STATIC static.c) add_library(imported SHARED IMPORTED) set_property(TARGET imported PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/imported.dll") @@ -26,9 +30,16 @@ if(WIN32 OR CYGWIN) "$<TARGET_FILE:lib3>" "$<TARGET_FILE:lib2>" ) + set(expected_dll_dirs + "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:lib2>>" + "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:imported>>" + "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:lib3>>" + ) endif() -set(content "check_genex(\"${expected_dlls}\" \"$<TARGET_RUNTIME_DLLS:exe>\")\n") +set(content "check_genex(\"${expected_dlls}\" \"$<TARGET_RUNTIME_DLLS:exe>\") +check_genex(\"${expected_dll_dirs}\" \"$<TARGET_RUNTIME_DLL_DIRS:exe>\")\n") + set(condition) get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(multi_config) diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake index bf9c302..3e35aa3 100644 --- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake +++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake @@ -100,7 +100,9 @@ if (WIN32 OR CYGWIN) set(_platform Win32-Clang) elseif(MSVC AND COMPILER_HAS_DEPRECATED) set(_platform Win32) - elseif((MINGW OR CYGWIN) AND COMPILER_HAS_DEPRECATED) + elseif(CYGWIN AND COMPILER_HAS_DEPRECATED) + set(_platform Cygwin) + elseif(MINGW AND COMPILER_HAS_DEPRECATED) set(_platform MinGW) else() set(_platform WinEmpty) diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h new file mode 100644 index 0000000..dac4fda --- /dev/null +++ b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h @@ -0,0 +1,42 @@ + +#ifndef LIBSHARED_EXPORT_H +#define LIBSHARED_EXPORT_H + +#ifdef LIBSHARED_STATIC_DEFINE +# define LIBSHARED_EXPORT +# define LIBSHARED_NO_EXPORT +#else +# ifndef LIBSHARED_EXPORT +# ifdef libshared_EXPORTS + /* We are building this library */ +# define LIBSHARED_EXPORT __declspec(dllexport) +# else + /* We are using this library */ +# define LIBSHARED_EXPORT __declspec(dllimport) +# endif +# endif + +# ifndef LIBSHARED_NO_EXPORT +# define LIBSHARED_NO_EXPORT +# endif +#endif + +#ifndef LIBSHARED_DEPRECATED +# define LIBSHARED_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef LIBSHARED_DEPRECATED_EXPORT +# define LIBSHARED_DEPRECATED_EXPORT LIBSHARED_EXPORT LIBSHARED_DEPRECATED +#endif + +#ifndef LIBSHARED_DEPRECATED_NO_EXPORT +# define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef LIBSHARED_NO_DEPRECATED +# define LIBSHARED_NO_DEPRECATED +# endif +#endif + +#endif /* LIBSHARED_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h new file mode 100644 index 0000000..b6e2a4a --- /dev/null +++ b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h @@ -0,0 +1,42 @@ + +#ifndef LIBSTATIC_EXPORT_H +#define LIBSTATIC_EXPORT_H + +#ifdef LIBSTATIC_STATIC_DEFINE +# define LIBSTATIC_EXPORT +# define LIBSTATIC_NO_EXPORT +#else +# ifndef LIBSTATIC_EXPORT +# ifdef libstatic_EXPORTS + /* We are building this library */ +# define LIBSTATIC_EXPORT +# else + /* We are using this library */ +# define LIBSTATIC_EXPORT +# endif +# endif + +# ifndef LIBSTATIC_NO_EXPORT +# define LIBSTATIC_NO_EXPORT +# endif +#endif + +#ifndef LIBSTATIC_DEPRECATED +# define LIBSTATIC_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef LIBSTATIC_DEPRECATED_EXPORT +# define LIBSTATIC_DEPRECATED_EXPORT LIBSTATIC_EXPORT LIBSTATIC_DEPRECATED +#endif + +#ifndef LIBSTATIC_DEPRECATED_NO_EXPORT +# define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef LIBSTATIC_NO_DEPRECATED +# define LIBSTATIC_NO_DEPRECATED +# endif +#endif + +#endif /* LIBSTATIC_EXPORT_H */ diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h index dac4fda..3ba2d2e 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h @@ -22,7 +22,7 @@ #endif #ifndef LIBSHARED_DEPRECATED -# define LIBSHARED_DEPRECATED __attribute__ ((__deprecated__)) +# define LIBSHARED_DEPRECATED __declspec(deprecated) #endif #ifndef LIBSHARED_DEPRECATED_EXPORT diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h index b6e2a4a..3c7e093 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h @@ -22,7 +22,7 @@ #endif #ifndef LIBSTATIC_DEPRECATED -# define LIBSTATIC_DEPRECATED __attribute__ ((__deprecated__)) +# define LIBSTATIC_DEPRECATED __declspec(deprecated) #endif #ifndef LIBSTATIC_DEPRECATED_EXPORT diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h index dac4fda..3ba2d2e 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h @@ -22,7 +22,7 @@ #endif #ifndef LIBSHARED_DEPRECATED -# define LIBSHARED_DEPRECATED __attribute__ ((__deprecated__)) +# define LIBSHARED_DEPRECATED __declspec(deprecated) #endif #ifndef LIBSHARED_DEPRECATED_EXPORT diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h index b6e2a4a..3c7e093 100644 --- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h +++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h @@ -22,7 +22,7 @@ #endif #ifndef LIBSTATIC_DEPRECATED -# define LIBSTATIC_DEPRECATED __attribute__ ((__deprecated__)) +# define LIBSTATIC_DEPRECATED __declspec(deprecated) #endif #ifndef LIBSTATIC_DEPRECATED_EXPORT diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index 2d545d9..b139210 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(BadIF) run_cmake(BadCONFIG) diff --git a/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt b/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt +++ b/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorToolset/CMakeLists.txt b/Tests/RunCMake/GeneratorToolset/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/GeneratorToolset/CMakeLists.txt +++ b/Tests/RunCMake/GeneratorToolset/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt index 37747a1..d0aef2c 100644 --- a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt +++ b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt @@ -1,6 +1,13 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): +^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0052 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/IfacePaths/CMakeLists.txt b/Tests/RunCMake/IfacePaths/CMakeLists.txt index 5cd4825..0d707f0 100644 --- a/Tests/RunCMake/IfacePaths/CMakeLists.txt +++ b/Tests/RunCMake/IfacePaths/CMakeLists.txt @@ -1,4 +1,8 @@ -cmake_minimum_required(VERSION 3.0) +if(RunCMake_TEST MATCHES "-CMP0052") + cmake_minimum_required(VERSION 3.0) +else() + cmake_minimum_required(VERSION 3.5) +endif() project(${RunCMake_TEST} NONE) if(NOT TEST_FILE) set(TEST_FILE ${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt index 37747a1..4db8209 100644 --- a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt +++ b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt @@ -1,6 +1,13 @@ -^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\): +^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): The OLD behavior for policy CMP0052 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt b/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt index 18dfd26..93ee9df 100644 --- a/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt +++ b/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt b/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt +++ b/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/InterfaceLibrary/genex_link.cmake b/Tests/RunCMake/InterfaceLibrary/genex_link.cmake index 0dbf029..3445864 100644 --- a/Tests/RunCMake/InterfaceLibrary/genex_link.cmake +++ b/Tests/RunCMake/InterfaceLibrary/genex_link.cmake @@ -1,7 +1,4 @@ - -cmake_minimum_required(VERSION 2.8.12.20131125 FATAL_ERROR) - -project(genex_link) +enable_language(CXX) set(_main_cpp ${CMAKE_CURRENT_BINARY_DIR}/main.cpp) file(WRITE ${_main_cpp} diff --git a/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake b/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake index 575fcc6..4a8ca37 100644 --- a/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake +++ b/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_policy(SET CMP0037 OLD) add_library(if$ace INTERFACE) add_library(iface::target INTERFACE) diff --git a/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake b/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake index ed81878..eae8f57 100644 --- a/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake +++ b/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake @@ -1,5 +1,3 @@ - -cmake_minimum_required(VERSION 2.8.12.20131009) set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) add_library(foo INTERFACE) target_compile_definitions(foo INTERFACE FOO_DEFINE) diff --git a/Tests/RunCMake/Languages/CMakeLists.txt b/Tests/RunCMake/Languages/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/Languages/CMakeLists.txt +++ b/Tests/RunCMake/Languages/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt index b7a0755..55aa1bb 100644 --- a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt +++ b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt @@ -1,6 +1,13 @@ -^CMake Deprecation Warning at CMP0028-OLD-iface.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMP0028-OLD-iface\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0028 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt index 586a876..f11d8f5 100644 --- a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt +++ b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt @@ -1,6 +1,13 @@ -^CMake Deprecation Warning at CMP0028-OLD.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMP0028-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0028 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/LinkItemValidation/CMakeLists.txt b/Tests/RunCMake/LinkItemValidation/CMakeLists.txt index 185cd91..6e1f8a2 100644 --- a/Tests/RunCMake/LinkItemValidation/CMakeLists.txt +++ b/Tests/RunCMake/LinkItemValidation/CMakeLists.txt @@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 2.8.12) -if(NOT RunCMake_TEST MATCHES "^CMP0028") - cmake_minimum_required(VERSION 3.22) +cmake_minimum_required(VERSION 3.5) +if(RunCMake_TEST MATCHES "^CMP0028") + cmake_minimum_required(VERSION 2.8.12) endif() project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) # policy used at end of dir diff --git a/Tests/RunCMake/LinkStatic/CMakeLists.txt b/Tests/RunCMake/LinkStatic/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/LinkStatic/CMakeLists.txt +++ b/Tests/RunCMake/LinkStatic/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt b/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt index 18dfd26..93ee9df 100644 --- a/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt +++ b/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt index 3313e31..544b65f 100644 --- a/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.* diff --git a/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt index 3313e31..544b65f 100644 --- a/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.* diff --git a/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt index 3313e31..082c7b5 100644 --- a/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.* diff --git a/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt index 3313e31..082c7b5 100644 --- a/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.* diff --git a/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt index 3313e31..d2efd3d 100644 --- a/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.* diff --git a/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt index 3313e31..d2efd3d 100644 --- a/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.* diff --git a/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt index 3313e31..0082ab2 100644 --- a/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.* diff --git a/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt index 3313e31..0082ab2 100644 --- a/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt +++ b/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt @@ -1 +1 @@ -.*-E env USED_LAUNCHER=1.* +.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.* diff --git a/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake b/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake index 8f2bf63..025f367 100644 --- a/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake +++ b/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake @@ -17,7 +17,8 @@ endfunction() function(run_linker_launcher_env lang) string(REGEX REPLACE "-.*" "" core_lang "${lang}") - set(ENV{CMAKE_${core_lang}_LINKER_LAUNCHER} "${CMAKE_COMMAND};-E;env;USED_LAUNCHER=1") + # Use the noop genexp $<PATH:...> genexp to validate genexp support. + set(ENV{CMAKE_${core_lang}_LINKER_LAUNCHER} "$<PATH:CMAKE_PATH,${CMAKE_COMMAND}>;-E;env;USED_LAUNCHER=1;TARGET_NAME=$<TARGET_PROPERTY:NAME>;LANGUAGE=$<LINK_LANGUAGE>") run_linker_launcher(${lang}) unset(ENV{CMAKE_${core_lang}_LINKER_LAUNCHER}) endfunction() diff --git a/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt b/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt new file mode 100644 index 0000000..42742f7 --- /dev/null +++ b/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0113-OLD.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0113 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/Make/CMakeLists.txt b/Tests/RunCMake/Make/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/Make/CMakeLists.txt +++ b/Tests/RunCMake/Make/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Ninja/AssumedSources.cmake b/Tests/RunCMake/Ninja/AssumedSources.cmake index d5364f0..d68fca9 100644 --- a/Tests/RunCMake/Ninja/AssumedSources.cmake +++ b/Tests/RunCMake/Ninja/AssumedSources.cmake @@ -1,6 +1,5 @@ -cmake_minimum_required(VERSION 3.8) cmake_policy(SET CMP0118 NEW) -project(AssumedSources) +enable_language(C) set_source_files_properties( "${CMAKE_CURRENT_BINARY_DIR}/target.c" diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt index 9a606ee..6d340b0 100644 --- a/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt @@ -1,6 +1,15 @@ -^CMake Deprecation Warning at CMP0058-OLD-by.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMP0058-OLD-by\.cmake:[0-9] \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9] \(include\) ++ +CMake Deprecation Warning at CMP0058-OLD-by\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0058 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake b/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake index 92a3a0f..45e5aa3 100644 --- a/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake +++ b/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake @@ -1,3 +1,4 @@ +cmake_policy(VERSION 3.2) cmake_policy(SET CMP0058 OLD) set(byproducts BYPRODUCTS byproduct1a byproduct1b) include(CMP0058-common.cmake) diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt index ba6e5da..834c781 100644 --- a/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt @@ -1,6 +1,15 @@ -^CMake Deprecation Warning at CMP0058-OLD-no.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMP0058-OLD-no\.cmake:[0-9] \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9] \(include\) ++ +CMake Deprecation Warning at CMP0058-OLD-no\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0058 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake b/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake index 0326e07..388e018 100644 --- a/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake +++ b/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake @@ -1,2 +1,3 @@ +cmake_policy(VERSION 3.2) cmake_policy(SET CMP0058 OLD) include(CMP0058-common.cmake) diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt new file mode 100644 index 0000000..2927f52 --- /dev/null +++ b/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt @@ -0,0 +1,8 @@ +^CMake Deprecation Warning at CMP0058-WARN-by\.cmake:[0-9] \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9] \(include\)$ diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake b/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake index 6128167..6f5484a 100644 --- a/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake +++ b/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake @@ -1,2 +1,3 @@ +cmake_policy(VERSION 3.2) set(byproducts BYPRODUCTS byproduct1a byproduct1b) include(CMP0058-common.cmake) diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt index 439a2d9..1ffb416 100644 --- a/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt @@ -1,4 +1,13 @@ -^CMake Warning \(dev\): +^CMake Deprecation Warning at CMP0058-WARN-no\.cmake:[0-9] \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9] \(include\) ++ +CMake Warning \(dev\): Policy CMP0058 is not set: Ninja requires custom command byproducts to be explicit. Run "cmake --help-policy CMP0058" for policy details. Use the cmake_policy command to set the policy and suppress this warning. diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake b/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake index 7bc66ef..714ae64 100644 --- a/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake +++ b/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake @@ -1 +1,2 @@ +cmake_policy(VERSION 3.2) include(CMP0058-common.cmake) diff --git a/Tests/RunCMake/Ninja/CMakeLists.txt b/Tests/RunCMake/Ninja/CMakeLists.txt index 2a0591e..8eb5748 100644 --- a/Tests/RunCMake/Ninja/CMakeLists.txt +++ b/Tests/RunCMake/Ninja/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/Ninja/CommandConcat.cmake b/Tests/RunCMake/Ninja/CommandConcat.cmake index 790cf9d..7d6faf5 100644 --- a/Tests/RunCMake/Ninja/CommandConcat.cmake +++ b/Tests/RunCMake/Ninja/CommandConcat.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.7) -project(concat_cmd NONE) set(output1 ${CMAKE_BINARY_DIR}/out1.txt) set(output2 ${CMAKE_BINARY_DIR}/out2.txt) file(REMOVE ${output1} ${output2}) diff --git a/Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake b/Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake new file mode 100644 index 0000000..fefd86a --- /dev/null +++ b/Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.26) +project(CustomCommandExplicitDepends C) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command-option.h" + COMMAND "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/command-option.h" + COMMENT "Creating command-option.h" + DEPENDS_EXPLICIT_ONLY +) + +set(CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY ON) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command-variable-on.h" + COMMAND "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/command-variable-on.h" + COMMENT "Creating command-variable-on.h" +) + +set(CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY OFF) +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command-variable-off.h" + COMMAND "${CMAKE_COMMAND}" -E touch + "${CMAKE_CURRENT_BINARY_DIR}/command-variable-off.h" + COMMENT "Creating command-variable-off.h" +) + +add_library(dep SHARED dep.c) + +add_library(top SHARED + top.c + "${CMAKE_CURRENT_BINARY_DIR}/command-option.h" + "${CMAKE_CURRENT_BINARY_DIR}/command-variable-on.h" + "${CMAKE_CURRENT_BINARY_DIR}/command-variable-off.h" +) +target_link_libraries(top PRIVATE dep) diff --git a/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake index 8e01c8c..e04ac21 100644 --- a/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake +++ b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required(VERSION 3.5) -project(hello NONE) - add_custom_command( OUTPUT hello.copy.c COMMAND "${CMAKE_COMMAND}" -E copy diff --git a/Tests/RunCMake/Ninja/Executable.cmake b/Tests/RunCMake/Ninja/Executable.cmake index 4e17d68..2b6a61b 100644 --- a/Tests/RunCMake/Ninja/Executable.cmake +++ b/Tests/RunCMake/Ninja/Executable.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.5) -project(hello C) +enable_language(C) add_executable(hello hello.c) include(CheckOutput.cmake) include(CheckNoPrefixSubDir.cmake) diff --git a/Tests/RunCMake/Ninja/LooseObjectDepends.cmake b/Tests/RunCMake/Ninja/LooseObjectDepends.cmake index 360c7ba..90f8249 100644 --- a/Tests/RunCMake/Ninja/LooseObjectDepends.cmake +++ b/Tests/RunCMake/Ninja/LooseObjectDepends.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.8) -project(LooseObjectDepends C) +enable_language(C) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command.h" diff --git a/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake b/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake index 505f750..9615c56 100644 --- a/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake +++ b/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.12) -project(Test LANGUAGES C) +enable_language(C) configure_file(PreventConfigureFileDupBuildRule.cmake PreventTargetAliasesDupBuildRule.cmake @ONLY) add_subdirectory(SubDirConfigureFileDup) diff --git a/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake b/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake index da6f86a..81eb731 100644 --- a/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake +++ b/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.12) -project(Test LANGUAGES C) +enable_language(C) # fake launcher executable set(input_launcher_executable ${CMAKE_CURRENT_BINARY_DIR}/fake_launcher_executable) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 9214e90..91c48c6 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake @@ -190,6 +190,38 @@ function (run_LooseObjectDepends) endfunction () run_LooseObjectDepends() +function (run_CustomCommandExplictDepends) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CustomCommandExplicitDepends-build) + run_cmake(CustomCommandExplicitDepends) + + set(DEP_LIB "${RunCMake_TEST_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}dep${CMAKE_SHARED_LIBRARY_SUFFIX}") + + run_ninja("${RunCMake_TEST_BINARY_DIR}" "command-option.h") + if (EXISTS "${DEP_LIB}") + message(FATAL_ERROR + "The `dep` library was created when requesting a custom command to be " + "generated; this should no longer be necessary when passing " + "DEPENDS_EXPLICIT_ONLY option.") + endif () + + run_ninja("${RunCMake_TEST_BINARY_DIR}" "command-variable-on.h") + if (EXISTS "${DEP_LIB}") + message(FATAL_ERROR + "The `dep` library was created when requesting a custom command to be " + "generated; this should no longer be necessary when setting " + "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY variable to ON.") + endif () + + run_ninja("${RunCMake_TEST_BINARY_DIR}" "command-variable-off.h") + if (NOT EXISTS "${DEP_LIB}") + message(FATAL_ERROR + "The `dep` library was not created when requesting a custom command to be " + "generated; this should be necessary when setting " + "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY variable to OFF.") + endif () +endfunction () +run_CustomCommandExplictDepends() + function (run_AssumedSources) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build) run_cmake(AssumedSources) diff --git a/Tests/RunCMake/Ninja/SharedLib.cmake b/Tests/RunCMake/Ninja/SharedLib.cmake index 1a78390..c295c16 100644 --- a/Tests/RunCMake/Ninja/SharedLib.cmake +++ b/Tests/RunCMake/Ninja/SharedLib.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.5) -project(hello C) +enable_language(C) add_library(greeting SHARED greeting.c) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_executable(hello hello_with_greeting.c) diff --git a/Tests/RunCMake/Ninja/StaticLib.cmake b/Tests/RunCMake/Ninja/StaticLib.cmake index 0f815ae..dab6742 100644 --- a/Tests/RunCMake/Ninja/StaticLib.cmake +++ b/Tests/RunCMake/Ninja/StaticLib.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.5) -project(hello C) +enable_language(C) add_definitions(-DGREETING_STATIC) add_library(greeting STATIC greeting.c) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/RunCMake/Ninja/SubDirPrefix.cmake b/Tests/RunCMake/Ninja/SubDirPrefix.cmake index 30ad1e6..49d075f 100644 --- a/Tests/RunCMake/Ninja/SubDirPrefix.cmake +++ b/Tests/RunCMake/Ninja/SubDirPrefix.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.5) -project(hello C) +enable_language(C) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") add_subdirectory(SubDirPrefix) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/RunCMake/Ninja/TwoLibs.cmake b/Tests/RunCMake/Ninja/TwoLibs.cmake index 666452f..10ac5a6 100644 --- a/Tests/RunCMake/Ninja/TwoLibs.cmake +++ b/Tests/RunCMake/Ninja/TwoLibs.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.5) -project(hello C) +enable_language(C) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib-static") diff --git a/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt new file mode 100644 index 0000000..f5bce43 --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt @@ -0,0 +1 @@ +tgt has been built diff --git a/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake new file mode 100644 index 0000000..c4e0587 --- /dev/null +++ b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake @@ -0,0 +1 @@ +add_custom_target(tgt ALL COMMAND ${CMAKE_COMMAND} -E echo "tgt has been built") diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index c040e8f..47f5eee 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake @@ -459,6 +459,15 @@ set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release;-DCMAKE_C run_cmake(CompileCommands) unset(RunCMake_TEST_OPTIONS) +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/OutputPathPrefix-build) +run_cmake_with_options(OutputPathPrefix "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=OutputPathPrefix-build") +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}) +set(RunCMake_TEST_NO_CLEAN 1) +run_cmake_command(OutputPathPrefix-all-ninja "${RunCMake_MAKE_PROGRAM}" -f OutputPathPrefix-build/build.ninja OutputPathPrefix-build/all) +run_cmake_command(OutputPathPrefix-clean-ninja "${RunCMake_MAKE_PROGRAM}" -f OutputPathPrefix-build/build.ninja OutputPathPrefix-build/clean) +unset(RunCMake_TEST_NO_CLEAN) +unset(RunCMake_TEST_BINARY_DIR) + # CudaSimple uses separable compilation, which is currently only supported on NVCC. if(CMake_TEST_CUDA) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CudaSimple-build) diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt b/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt index 2897109..3e470a2 100644 --- a/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt +++ b/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.14) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake index 75c26a7..6027f03 100644 --- a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake +++ b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required(VERSION 3.14) -project(Minimal NONE) - # # list of targets to test. to add a target: put its files in the data # subdirectory and add it to this list... we run each target's diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt b/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt index 2897109..3e470a2 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt +++ b/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.14) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake index df4ef1f..fa7bf07 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake +++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required(VERSION 3.14) -project(Minimal NONE) - # # list of targets to test. to add a target: put its files in the data # subdirectory and add it to this list... we run each target's diff --git a/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake b/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake index abd27f4..da608b3 100644 --- a/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake +++ b/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(NotClosed) run_cmake(NotOpened) diff --git a/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt b/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt index c3922d6..42b0577 100644 --- a/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt +++ b/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt @@ -1,5 +1,4 @@ - -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} CXX) # MSVC creates extra targets which pollute the stderr unless we set this. diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index 54d7eb5..e1c923d 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -186,6 +186,18 @@ function(run_cmake test) "|[^\n]*Bullseye Testing Technology" ")[^\n]*\n)+" ) + if(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION) + string(REGEX REPLACE [[ +^CMake Deprecation Warning at [^ +]*CMakeLists.txt:1 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +]] "" actual_stderr "${actual_stderr}") + endif() foreach(o IN ITEMS stdout stderr config) string(REGEX REPLACE "\r\n" "\n" actual_${o} "${actual_${o}}") string(REGEX REPLACE "${ignore_line_regex}" "\\1" actual_${o} "${actual_${o}}") diff --git a/Tests/RunCMake/Swift/CMakeLists.txt b/Tests/RunCMake/Swift/CMakeLists.txt index 74b3ff8..77030d6 100644 --- a/Tests/RunCMake/Swift/CMakeLists.txt +++ b/Tests/RunCMake/Swift/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.15) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt b/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt index 874bdc7..16d2e21 100644 --- a/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt +++ b/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt @@ -1,4 +1,4 @@ -^CMake Error at SwiftMultiArch.cmake:3 \(project\): +^CMake Error at SwiftMultiArch.cmake:2 \(enable_language\): multiple values for CMAKE_OSX_ARCHITECTURES not supported with Swift Call Stack \(most recent call first\): CMakeLists.txt:3 diff --git a/Tests/RunCMake/Swift/SwiftMultiArch.cmake b/Tests/RunCMake/Swift/SwiftMultiArch.cmake index 5fdb688..b59bb62 100644 --- a/Tests/RunCMake/Swift/SwiftMultiArch.cmake +++ b/Tests/RunCMake/Swift/SwiftMultiArch.cmake @@ -1,4 +1,2 @@ -cmake_minimum_required(VERSION 3.15.1) set(CMAKE_OSX_ARCHITECTURES "armv7;arm64;i386;x86_64") -project(SwiftMultiArch - LANGUAGES Swift) +enable_language(Swift) diff --git a/Tests/RunCMake/SymlinkTrees/CMakeLists.txt b/Tests/RunCMake/SymlinkTrees/CMakeLists.txt index e16faea..919a168 100644 --- a/Tests/RunCMake/SymlinkTrees/CMakeLists.txt +++ b/Tests/RunCMake/SymlinkTrees/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) message(STATUS "source: '${CMAKE_SOURCE_DIR}'") diff --git a/Tests/RunCMake/Syntax/RunCMakeTest.cmake b/Tests/RunCMake/Syntax/RunCMakeTest.cmake index f0c287c..f56ac64 100644 --- a/Tests/RunCMake/Syntax/RunCMakeTest.cmake +++ b/Tests/RunCMake/Syntax/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(BOM-UTF-8) run_cmake(BOM-UTF-16-LE) diff --git a/Tests/RunCMake/Syntax/String1-stderr.txt b/Tests/RunCMake/Syntax/String1-stderr.txt index 07e98da..382ea00 100644 --- a/Tests/RunCMake/Syntax/String1-stderr.txt +++ b/Tests/RunCMake/Syntax/String1-stderr.txt @@ -1,3 +1,3 @@ -^ +^' 1 \${var} 4 - $ + '$ diff --git a/Tests/RunCMake/Syntax/String1.cmake b/Tests/RunCMake/Syntax/String1.cmake index a94c9ff..20ed3c1 100644 --- a/Tests/RunCMake/Syntax/String1.cmake +++ b/Tests/RunCMake/Syntax/String1.cmake @@ -1,3 +1,3 @@ -message(" +message("' 1 \${var} 4 - ") + '") diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt index b309c3b..c7238df 100644 --- a/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt +++ b/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt @@ -1,7 +1,7 @@ -CMake Error at UnterminatedBrace0.cmake:2 \(set\): +CMake Error at UnterminatedBrace0.cmake:1 \(set\): Syntax error in cmake code at - .*/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake:2 + .*/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake:1 when parsing string diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake b/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake index 0da1290..09af0ce 100644 --- a/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake +++ b/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake @@ -1,2 +1 @@ -cmake_minimum_required(VERSION 3.0) set(var "${") diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt index ffe0e2a..3d88f36 100644 --- a/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt +++ b/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at UnterminatedBrace1.cmake:3 \(set\): +CMake Warning \(dev\) at UnterminatedBrace1.cmake:2 \(set\): Syntax error in cmake code at - .*/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake:3 + .*/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake:2 when parsing string diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake b/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake index 93fba34..8b40b19 100644 --- a/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake +++ b/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake @@ -1,3 +1,2 @@ -cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0010 OLD) set(var "${") diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt index b332d34..0d76251 100644 --- a/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt +++ b/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt @@ -1,7 +1,7 @@ -CMake Error at UnterminatedBrace2.cmake:4 \(set\): +CMake Error at UnterminatedBrace2.cmake:3 \(set\): Syntax error in cmake code at - .*/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake:4 + .*/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake:3 when parsing string diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake b/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake index a650e5b..30d4d4d 100644 --- a/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake +++ b/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0010 OLD) cmake_policy(SET CMP0053 NEW) set(var "${") diff --git a/Tests/RunCMake/ArtifactOutputDirs/CMakeLists.txt b/Tests/RunCMake/TargetArtifacts/CMakeLists.txt index ab1a20c..ab1a20c 100644 --- a/Tests/RunCMake/ArtifactOutputDirs/CMakeLists.txt +++ b/Tests/RunCMake/TargetArtifacts/CMakeLists.txt diff --git a/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt new file mode 100644 index 0000000..b375da6 --- /dev/null +++ b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt @@ -0,0 +1,2 @@ +.*exA_name="(libexA\.so\.2|libexA\.2\.dylib|(lib|cyg|msys-|)exA-2\.dll)" +.*exB_name="(libexB\.so\.2|libexB\.2\.dylib|(lib|cyg|msys-|)exB-2\.dll)" diff --git a/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake new file mode 100644 index 0000000..82eca0b --- /dev/null +++ b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +add_library(exA SHARED dll.c) +set_target_properties(exA PROPERTIES + SOVERSION 2 + DLL_NAME_WITH_SOVERSION 1 + ) + +set(CMAKE_DLL_NAME_WITH_SOVERSION 1) +add_library(exB SHARED dll.c) +set_property(TARGET exB PROPERTY SOVERSION 2) + +add_custom_target(checkNames ALL + COMMAND ${CMAKE_COMMAND} -E echo exA_name="$<TARGET_FILE_NAME:exA>" + COMMAND ${CMAKE_COMMAND} -E echo exB_name="$<TARGET_FILE_NAME:exB>" + VERBATIM + ) +add_dependencies(checkNames exA exB) diff --git a/Tests/RunCMake/ArtifactOutputDirs/ArtifactOutputDirs.cmake b/Tests/RunCMake/TargetArtifacts/OutputDirs.cmake index d0accd7..d0accd7 100644 --- a/Tests/RunCMake/ArtifactOutputDirs/ArtifactOutputDirs.cmake +++ b/Tests/RunCMake/TargetArtifacts/OutputDirs.cmake diff --git a/Tests/RunCMake/ArtifactOutputDirs/RunCMakeTest.cmake b/Tests/RunCMake/TargetArtifacts/RunCMakeTest.cmake index 1bf8438..de69936 100644 --- a/Tests/RunCMake/ArtifactOutputDirs/RunCMakeTest.cmake +++ b/Tests/RunCMake/TargetArtifacts/RunCMakeTest.cmake @@ -2,18 +2,18 @@ include(RunCMake) function(run_cmake_and_verify_after_build case) set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${case}-build") - file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") - file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - set(RunCMake_TEST_NO_CLEAN 1) if(RunCMake_GENERATOR_IS_MULTI_CONFIG) set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) else() set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) endif() run_cmake(${case}) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command("${case}-build" ${CMAKE_COMMAND} --build .) - unset(RunCMake_TEST_NO_CLEAN) - unset(RunCMake_TEST_BINARY_DIR) endfunction() -run_cmake_and_verify_after_build(ArtifactOutputDirs) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "AIX") + run_cmake_and_verify_after_build(DLL-SOVERSION) +endif() +run_cmake_and_verify_after_build(OutputDirs) diff --git a/Tests/RunCMake/ArtifactOutputDirs/check.cmake b/Tests/RunCMake/TargetArtifacts/check.cmake index ca37eba..ca37eba 100644 --- a/Tests/RunCMake/ArtifactOutputDirs/check.cmake +++ b/Tests/RunCMake/TargetArtifacts/check.cmake diff --git a/Tests/RunCMake/TargetArtifacts/dll.c b/Tests/RunCMake/TargetArtifacts/dll.c new file mode 100644 index 0000000..31e1dbf --- /dev/null +++ b/Tests/RunCMake/TargetArtifacts/dll.c @@ -0,0 +1,6 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + void dll(void) +{ +} diff --git a/Tests/RunCMake/ArtifactOutputDirs/lib.c b/Tests/RunCMake/TargetArtifacts/lib.c index 22373f1..22373f1 100644 --- a/Tests/RunCMake/ArtifactOutputDirs/lib.c +++ b/Tests/RunCMake/TargetArtifacts/lib.c diff --git a/Tests/RunCMake/ArtifactOutputDirs/main.c b/Tests/RunCMake/TargetArtifacts/main.c index 8488f4e..8488f4e 100644 --- a/Tests/RunCMake/ArtifactOutputDirs/main.c +++ b/Tests/RunCMake/TargetArtifacts/main.c diff --git a/Tests/RunCMake/TargetProperties/CMakeLists.txt b/Tests/RunCMake/TargetProperties/CMakeLists.txt index 44b5d30..26f536b 100644 --- a/Tests/RunCMake/TargetProperties/CMakeLists.txt +++ b/Tests/RunCMake/TargetProperties/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST}) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/VSSolution/CMakeLists.txt b/Tests/RunCMake/VSSolution/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/VSSolution/CMakeLists.txt +++ b/Tests/RunCMake/VSSolution/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake b/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake index 7a000ee..133dbe1 100644 --- a/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake +++ b/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(PropertyTypo) run_cmake(CMP0063-OLD) diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake index bd914f8..623ef2a 100644 --- a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake @@ -1,7 +1,5 @@ -cmake_minimum_required(VERSION 3.22) - # a simple CSharp only test case -project (DotNetSdk CSharp) +enable_language(CSharp) set(CMAKE_DOTNET_TARGET_FRAMEWORK net472) set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt index 872338d..1d0bd70 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake b/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake index 1f3c19d..68372a1 100644 --- a/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake +++ b/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.23) - -project(BundleLinkBundle CXX) +enable_language(CXX) add_subdirectory(lib_bundle) diff --git a/Tests/RunCMake/XcodeProject/CMakeLists.txt b/Tests/RunCMake/XcodeProject/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/XcodeProject/CMakeLists.txt +++ b/Tests/RunCMake/XcodeProject/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake b/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake index 3d8fa17..234ceef 100644 --- a/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake +++ b/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.10) -project(DeploymentTarget C) +enable_language(C) # using Xcode 7.1 SDK versions for deployment targets diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake index a9ea717..b42e933 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake @@ -45,7 +45,6 @@ endforeach() file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/CMakeLists.txt [[ -cmake_minimum_required(VERSION 3.18) project(ExternalFrameworks) add_library(staticFrameworkExt STATIC func6.c) add_library(sharedFrameworkExt SHARED func7.c) diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake index e72bf4d..ab64db7 100644 --- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake +++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.18...3.19) macro(returnOnError errorMsg) if(NOT "${errorMsg}" STREQUAL "") diff --git a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake index bc14874..a9fafd2 100644 --- a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake @@ -1,6 +1,5 @@ # check if Xcode and CMake have the same understanding of Bundle layout -cmake_minimum_required(VERSION 3.3) enable_language(C) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake index 8426148..f8eccc7 100644 --- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.3) - -project(IOSInstallCombined CXX) +enable_language(CXX) set(maybe_armv7 armv7) set(maybe_i386 i386) diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake index 7d14d19..e719428 100644 --- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.3) - -project(XcodeIOSInstallCombinedPrune CXX) +enable_language(CXX) if(XCODE_VERSION VERSION_GREATER_EQUAL 9) set(CMAKE_OSX_DEPLOYMENT_TARGET 10) diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake index 5177ec2..cb22e51 100644 --- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.3) - -project(XcodeIOSInstallCombinedSingleArch CXX) +enable_language(CXX) set(iphoneos_arch armv7) if(XCODE_VERSION VERSION_GREATER_EQUAL 14) diff --git a/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake b/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake index 75da7b1..fccd4c6 100644 --- a/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.3) - -project(XcodeInstallIOS) +enable_language(CXX) set(XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") diff --git a/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake b/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake index a1064f4..b334b7d 100644 --- a/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.3) enable_language(CXX) set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME ON) diff --git a/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake b/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake index 4840276..8f54046 100644 --- a/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.15) -project(objctest LANGUAGES C OBJC) +enable_language(C) +enable_language(OBJC) include(CheckOBJCCompilerFlag) check_objc_compiler_flag(-fobjc-arc HAVE_OBJC_ARC) diff --git a/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake b/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake index 0ad942f..193860c 100644 --- a/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.15) -project(objcxxtest LANGUAGES CXX OBJCXX) +enable_language(CXX) +enable_language(OBJCXX) include(CheckOBJCXXCompilerFlag) check_objcxx_compiler_flag(-fobjc-arc HAVE_OBJC_ARC) diff --git a/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake b/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake index 3ca24af..5e66e82 100644 --- a/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake @@ -1,5 +1 @@ -cmake_minimum_required (VERSION 3.19) - -project (test_xcode_fail NONE) - add_subdirectory(subproject_two_object_libs) diff --git a/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake b/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake index 44052f0..80107cb 100644 --- a/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required (VERSION 3.14) if(NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]") set(USE_SWIFT 1) diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake index 2fe5a9f..ff3419c 100644 --- a/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.7) - -project(XcodeSchemaGeneration CXX) +enable_language(CXX) add_executable(foo main.cpp) diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake index 267e379..05a8976 100644 --- a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake @@ -1,9 +1,7 @@ -cmake_minimum_required(VERSION 3.7) +enable_language(CXX) set(CMAKE_XCODE_GENERATE_SCHEME ON) -project(XcodeSchemaProperty CXX) - function(create_scheme_for_variable variable) set(CMAKE_XCODE_SCHEME_${variable} ON) add_executable(${variable} main.cpp) diff --git a/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake b/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake index e83d7f3..55dd1a7 100644 --- a/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake @@ -1,2 +1 @@ -cmake_minimum_required(VERSION 3.3) find_package(ZLIB REQUIRED) diff --git a/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake b/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake index 58d2616..bd8995b 100644 --- a/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake +++ b/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake @@ -1,6 +1,4 @@ -cmake_minimum_required(VERSION 3.23) - -project(XcodeXCConfig C) +enable_language(C) set(CMAKE_XCODE_XCCONFIG "$<IF:$<CONFIG:Debug>,XcodeXCConfig.global.debug.xcconfig,XcodeXCConfig.global.release.xcconfig>") diff --git a/Tests/RunCMake/add_custom_command/CMakeLists.txt b/Tests/RunCMake/add_custom_command/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/add_custom_command/CMakeLists.txt +++ b/Tests/RunCMake/add_custom_command/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake b/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake index 65b7250..d8a5d86 100644 --- a/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake +++ b/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake @@ -2,7 +2,7 @@ add_custom_target(mkdir COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURR add_custom_command( OUTPUT out.txt COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/PrintDir.cmake - WORKING_DIRECTORY ${CMAKE_CFG_INTDIR} + WORKING_DIRECTORY $<CONFIG> ) set_property(SOURCE out.txt PROPERTY SYMBOLIC 1) add_custom_target(drive ALL DEPENDS out.txt) diff --git a/Tests/RunCMake/add_custom_target/CMakeLists.txt b/Tests/RunCMake/add_custom_target/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/add_custom_target/CMakeLists.txt +++ b/Tests/RunCMake/add_custom_target/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_dependencies/CMakeLists.txt b/Tests/RunCMake/add_dependencies/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/add_dependencies/CMakeLists.txt +++ b/Tests/RunCMake/add_dependencies/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake b/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake index f0e4069..547c101 100644 --- a/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake +++ b/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.7) -project(ReadOnlyProperty C) +enable_language(C) add_library(a a.c) diff --git a/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake b/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake index 45b3974..2f883af 100644 --- a/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake +++ b/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.7) -project(RetrieveDependencies C) +enable_language(C) add_library(a a.c) diff --git a/Tests/RunCMake/add_executable/CMakeLists.txt b/Tests/RunCMake/add_executable/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/add_executable/CMakeLists.txt +++ b/Tests/RunCMake/add_executable/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_library/CMakeLists.txt b/Tests/RunCMake/add_library/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/add_library/CMakeLists.txt +++ b/Tests/RunCMake/add_library/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake b/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake index c77b43c..f7c551d 100644 --- a/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake +++ b/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake @@ -11,7 +11,7 @@ string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAK add_library(example SHARED LinkOptionsLib.c) # use LAUNCH facility to dump linker command -set_property(TARGET example PROPERTY RULE_LAUNCH_LINK "\"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/dump${CMAKE_EXECUTABLE_SUFFIX}\"") +set_property(TARGET example PROPERTY RULE_LAUNCH_LINK "\"$<TARGET_FILE:dump>\"") add_dependencies (example dump) diff --git a/Tests/RunCMake/add_subdirectory/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/CMakeLists.txt index 47d249c..b5d7262 100644 --- a/Tests/RunCMake/add_subdirectory/CMakeLists.txt +++ b/Tests/RunCMake/add_subdirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) # Have to set policy here due to policy scope if(DEFINED CMP0082_VALUE) diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt new file mode 100644 index 0000000..46b32d1 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-AlphaNumeric.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-BracketArgument-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt new file mode 100644 index 0000000..906f318 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-BracketArgument.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt new file mode 100644 index 0000000..65818fa --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-EscapedSpecialChars.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt new file mode 100644 index 0000000..db88659 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-FormerInvalidSpecialChars.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt new file mode 100644 index 0000000..42cc02a --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt @@ -0,0 +1,11 @@ +^CMake Deprecation Warning at CMP0110-OLD-FormerInvalidSpecialChars.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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\): + CMP0110-OLD-FormerInvalidSpecialCharsMC.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt new file mode 100644 index 0000000..6a39db6 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-LeadingAndTrailingWhitespace.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt new file mode 100644 index 0000000..d5dcada --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-OtherSpecialChars.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-Quote-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt new file mode 100644 index 0000000..69cd304 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-Quote.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-Semicolon-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt new file mode 100644 index 0000000..e601bfc --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-Semicolon.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-Space-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt new file mode 100644 index 0000000..618d2b1 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-Space.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt new file mode 100644 index 0000000..ad67c08 --- /dev/null +++ b/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0110-OLD-ValidSpecialChars.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0110 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/alias_targets/CMakeLists.txt b/Tests/RunCMake/alias_targets/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/alias_targets/CMakeLists.txt +++ b/Tests/RunCMake/alias_targets/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/alias_targets/invalid-name.cmake b/Tests/RunCMake/alias_targets/invalid-name.cmake index 01983e5..e2ebbfa 100644 --- a/Tests/RunCMake/alias_targets/invalid-name.cmake +++ b/Tests/RunCMake/alias_targets/invalid-name.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_policy(SET CMP0037 OLD) enable_language(CXX) add_library(foo empty.cpp) diff --git a/Tests/RunCMake/build_command/CMakeLists.txt b/Tests/RunCMake/build_command/CMakeLists.txt index f1a83e8..1319aec 100644 --- a/Tests/RunCMake/build_command/CMakeLists.txt +++ b/Tests/RunCMake/build_command/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) if(NOT NoProject) project(${RunCMake_TEST} NONE) endif() diff --git a/Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt b/Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt deleted file mode 100644 index 7d40dcb..0000000 --- a/Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt +++ /dev/null @@ -1,26 +0,0 @@ -^CMake Deprecation Warning at Before2812.cmake:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) -+ -CMake Deprecation Warning at Before2812.cmake:2 \(cmake_policy\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) -+ -CMake Deprecation Warning at Before2812.cmake:6 \(cmake_policy\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. - - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\)$ diff --git a/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt b/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt new file mode 100644 index 0000000..7981235 --- /dev/null +++ b/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt @@ -0,0 +1,26 @@ +^CMake Deprecation Warning at Before3_5\.cmake:1 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) ++ +CMake Deprecation Warning at Before3_5\.cmake:2 \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) ++ +CMake Deprecation Warning at Before3_5\.cmake:6 \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\)$ diff --git a/Tests/RunCMake/cmake_minimum_required/Before2812.cmake b/Tests/RunCMake/cmake_minimum_required/Before3_5.cmake index 220e359..220e359 100644 --- a/Tests/RunCMake/cmake_minimum_required/Before2812.cmake +++ b/Tests/RunCMake/cmake_minimum_required/Before3_5.cmake diff --git a/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt b/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt index 667561e..8eb5748 100644 --- a/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt +++ b/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt b/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt index 81d26d2..3eb980a 100644 --- a/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt +++ b/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt @@ -1,9 +1,9 @@ -^CMake Deprecation Warning at CompatBefore24.cmake:1 \(cmake_minimum_required\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. +^CMake Deprecation Warning at CompatBefore24\.cmake:1 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + diff --git a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake index 3a959bb..36c44cb 100644 --- a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake @@ -4,7 +4,7 @@ run_cmake(Before24) run_cmake(CompatBefore24) run_cmake(Future) run_cmake(PolicyBefore24) -run_cmake(Before2812) +run_cmake(Before3_5) run_cmake(Range) run_cmake(RangeBad) run_cmake(Unknown) diff --git a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt index 6dd8cdf..93ee9df 100644 --- a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt +++ b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/cmake_path/call-cmake_path.cmake b/Tests/RunCMake/cmake_path/call-cmake_path.cmake index 70fd6f5..655115f 100644 --- a/Tests/RunCMake/cmake_path/call-cmake_path.cmake +++ b/Tests/RunCMake/cmake_path/call-cmake_path.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.18...3.19) - # define input variable set (path "") diff --git a/Tests/RunCMake/configure_file/CMakeLists.txt b/Tests/RunCMake/configure_file/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/configure_file/CMakeLists.txt +++ b/Tests/RunCMake/configure_file/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/continue/CMakeLists.txt b/Tests/RunCMake/continue/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/continue/CMakeLists.txt +++ b/Tests/RunCMake/continue/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt b/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt index af70ac3..f5d57e9 100644 --- a/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt +++ b/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt @@ -1,2 +1,9 @@ -^(Error\(s\) when building project +^CMake Deprecation Warning at [^ +]*/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD/test\.cmake:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++(Error\(s\) when building project )?ctest_build returned zero$ diff --git a/Tests/RunCMake/ctest_build/CMakeLists.txt.in b/Tests/RunCMake/ctest_build/CMakeLists.txt.in index 4a067fa..0a59940 100644 --- a/Tests/RunCMake/ctest_build/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_build/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) @CASE_CMAKELISTS_PREFIX_CODE@ project(CTestBuild@CASE_NAME@ @LANG@) include(CTest) diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake index 6f1b4b6..12525f2 100644 --- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake @@ -35,7 +35,9 @@ endif() run_ctest(BuildFailure) if (RunCMake_GENERATOR MATCHES "Makefiles") - set(CASE_TEST_PREFIX_CODE "") + set(CASE_TEST_PREFIX_CODE [[ +cmake_policy(VERSION 3.2) +]]) run_ctest(BuildFailure-CMP0061-OLD) endif() endfunction() diff --git a/Tests/RunCMake/ctest_build/test.cmake.in b/Tests/RunCMake/ctest_build/test.cmake.in index 9f7fa13..f92568f 100644 --- a/Tests/RunCMake/ctest_build/test.cmake.in +++ b/Tests/RunCMake/ctest_build/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_cmake_error/test.cmake.in b/Tests/RunCMake/ctest_cmake_error/test.cmake.in index 0648e7c..711a77f 100644 --- a/Tests/RunCMake/ctest_cmake_error/test.cmake.in +++ b/Tests/RunCMake/ctest_cmake_error/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_configure/CMakeLists.txt.in b/Tests/RunCMake/ctest_configure/CMakeLists.txt.in index 2fb21d4..2860b5c 100644 --- a/Tests/RunCMake/ctest_configure/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_configure/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestConfigure@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_configure/test.cmake.in b/Tests/RunCMake/ctest_configure/test.cmake.in index 72d199a..5935809 100644 --- a/Tests/RunCMake/ctest_configure/test.cmake.in +++ b/Tests/RunCMake/ctest_configure/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in b/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in index 1babd72..da7ec1a 100644 --- a/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.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_coverage/test.cmake.in b/Tests/RunCMake/ctest_coverage/test.cmake.in index 1788e66..f5a1223 100644 --- a/Tests/RunCMake/ctest_coverage/test.cmake.in +++ b/Tests/RunCMake/ctest_coverage/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in b/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in index 68a0fcb..1de338b 100644 --- a/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(CTestTestMemcheck@CASE_NAME@ NONE) include(CTest) diff --git a/Tests/RunCMake/ctest_memcheck/test.cmake.in b/Tests/RunCMake/ctest_memcheck/test.cmake.in index eedf080..af995fc 100644 --- a/Tests/RunCMake/ctest_memcheck/test.cmake.in +++ b/Tests/RunCMake/ctest_memcheck/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) # Settings: set(CTEST_SITE "@SITE@") diff --git a/Tests/RunCMake/ctest_start/CMakeLists.txt.in b/Tests/RunCMake/ctest_start/CMakeLists.txt.in index 913239c..e497a7d 100644 --- a/Tests/RunCMake/ctest_start/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_start/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestStart@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_start/test.cmake.in b/Tests/RunCMake/ctest_start/test.cmake.in index 4063ece..82acc19 100644 --- a/Tests/RunCMake/ctest_start/test.cmake.in +++ b/Tests/RunCMake/ctest_start/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_submit/test.cmake.in b/Tests/RunCMake/ctest_submit/test.cmake.in index 35cd16a..0f4885f 100644 --- a/Tests/RunCMake/ctest_submit/test.cmake.in +++ b/Tests/RunCMake/ctest_submit/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_test/CMakeLists.txt.in b/Tests/RunCMake/ctest_test/CMakeLists.txt.in index e61b556..8eb422d 100644 --- a/Tests/RunCMake/ctest_test/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_test/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestTest@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_test/test.cmake.in b/Tests/RunCMake/ctest_test/test.cmake.in index 36b1dbd..16dde1c 100644 --- a/Tests/RunCMake/ctest_test/test.cmake.in +++ b/Tests/RunCMake/ctest_test/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_update/CMakeLists.txt.in b/Tests/RunCMake/ctest_update/CMakeLists.txt.in index ecf0e54..f816fac 100644 --- a/Tests/RunCMake/ctest_update/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_update/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestTest@CASE_NAME@ NONE) include(CTest) @CASE_CMAKELISTS_SUFFIX_CODE@ diff --git a/Tests/RunCMake/ctest_update/test.cmake.in b/Tests/RunCMake/ctest_update/test.cmake.in index 01aab26..faaf9b4 100644 --- a/Tests/RunCMake/ctest_update/test.cmake.in +++ b/Tests/RunCMake/ctest_update/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_upload/CMakeLists.txt.in b/Tests/RunCMake/ctest_upload/CMakeLists.txt.in index 1fab71b..21e5273 100644 --- a/Tests/RunCMake/ctest_upload/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_upload/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(CTestUpload@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_upload/test.cmake.in b/Tests/RunCMake/ctest_upload/test.cmake.in index f13bdd1..a546d38 100644 --- a/Tests/RunCMake/ctest_upload/test.cmake.in +++ b/Tests/RunCMake/ctest_upload/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/export/CMakeLists.txt b/Tests/RunCMake/export/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/export/CMakeLists.txt +++ b/Tests/RunCMake/export/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/file/CMakeLists.txt b/Tests/RunCMake/file/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/file/CMakeLists.txt +++ b/Tests/RunCMake/file/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_dependency/CMakeLists.txt b/Tests/RunCMake/find_dependency/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/find_dependency/CMakeLists.txt +++ b/Tests/RunCMake/find_dependency/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_file/CMakeLists.txt b/Tests/RunCMake/find_file/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/find_file/CMakeLists.txt +++ b/Tests/RunCMake/find_file/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_library/CMakeLists.txt b/Tests/RunCMake/find_library/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/find_library/CMakeLists.txt +++ b/Tests/RunCMake/find_library/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt new file mode 100644 index 0000000..3ccd101 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt @@ -0,0 +1,45 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include +FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include +FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake new file mode 100644 index 0000000..f0853ee --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +cmake_policy(SET CMP0144 NEW) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt new file mode 100644 index 0000000..3ccd101 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt @@ -0,0 +1,45 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include +FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include +FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake new file mode 100644 index 0000000..f0853ee --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +cmake_policy(SET CMP0144 NEW) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt new file mode 100644 index 0000000..3274b82 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt @@ -0,0 +1,45 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake new file mode 100644 index 0000000..f1560a9 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +cmake_policy(SET CMP0144 OLD) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt new file mode 100644 index 0000000..c02a797 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt @@ -0,0 +1,45 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake new file mode 100644 index 0000000..f1560a9 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +cmake_policy(SET CMP0144 OLD) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt new file mode 100644 index 0000000..3540dc9 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt @@ -0,0 +1,63 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' ++ +CMake Warning \(dev\) at CMP0144-common.cmake:[0-9]+ \(find_package\): + Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT + variables. Run "cmake --help-policy CMP0144" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + CMake variable FOO_ROOT is set to: + + [^ +]*/Tests/RunCMake/find_package/PackageRoot/foo/cmake_root + + For compatibility, find_package is ignoring the variable, but code in a + \.cmake module might still use it\. +Call Stack \(most recent call first\): + CMP0144-common.cmake:[0-9]+ \(RunTestCase\) + CMP0144-WARN-CaseInsensitive.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. ++ +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake new file mode 100644 index 0000000..10f6b66 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +# (do not set CMP0144) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt new file mode 100644 index 0000000..3ccd101 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt @@ -0,0 +1,45 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include +FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include +FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h +FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h +FOO_TEST_PATH_FOO: <base>/foo/env_root/include +FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot +FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake new file mode 100644 index 0000000..10f6b66 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +# (do not set CMP0144) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt new file mode 100644 index 0000000..b828f05 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt @@ -0,0 +1,68 @@ +^---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '<base>/foo/env_root' ++ +CMake Warning \(dev\) at CMP0144-common.cmake:[0-9]+ \(find_package\): + Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT + variables. Run "cmake --help-policy CMP0144" for policy details\. Use the + cmake_policy command to set the policy and suppress this warning\. + + CMake variable FOO_ROOT is set to: + + [^ +]*/Tests/RunCMake/find_package/PackageRoot/foo/cmake_root + + Environment variable FOO_ROOT is set to: + + [^ +]*/Tests/RunCMake/find_package/PackageRoot/foo/env_root + + For compatibility, find_package is ignoring the variable, but code in a + \.cmake module might still use it\. +Call Stack \(most recent call first\): + CMP0144-common.cmake:[0-9]+ \(RunTestCase\) + CMP0144-WARN-CaseSensitive.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. ++ +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '<base>/foo/cmake_root' +ENV{FOO_ROOT}: '' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +---------- +FOO_ROOT : '' +ENV{FOO_ROOT}: '<base>/foo/env_root' + +find_package\(Foo\) +FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND +FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND +FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND +FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND +FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND + +----------$ diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake new file mode 100644 index 0000000..10f6b66 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake @@ -0,0 +1,3 @@ +cmake_policy(VERSION 3.26) +# (do not set CMP0144) +include(CMP0144-common.cmake) diff --git a/Tests/RunCMake/find_package/CMP0144-common.cmake b/Tests/RunCMake/find_package/CMP0144-common.cmake new file mode 100644 index 0000000..58eccca --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0144-common.cmake @@ -0,0 +1,78 @@ +# (includer selects CMP0144) +list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/PackageRoot) +set(PackageRoot_BASE ${CMAKE_CURRENT_SOURCE_DIR}/PackageRoot) + +function(PrintPath label path) + string(REPLACE "${PackageRoot_BASE}" "<base>" out "${path}") + message("${label} ${out}") +endfunction() + +macro(RunTestCase) + message("----------") + PrintPath("FOO_ROOT :" "'${FOO_ROOT}'") + PrintPath("ENV{FOO_ROOT}:" "'$ENV{FOO_ROOT}'") + message("") + + find_package(Foo) + message("find_package(Foo)") + PrintPath("FOO_TEST_FILE_FOO:" "${FOO_TEST_FILE_FOO}") + PrintPath("FOO_TEST_FILE_ZOT:" "${FOO_TEST_FILE_ZOT}") + PrintPath("FOO_TEST_PATH_FOO:" "${FOO_TEST_PATH_FOO}") + PrintPath("FOO_TEST_PATH_ZOT:" "${FOO_TEST_PATH_ZOT}") + PrintPath("FOO_TEST_PROG_FOO:" "${FOO_TEST_PROG_FOO}") + message("") + + unset(FOO_ROOT) + unset(ENV{FOO_ROOT}) + if(NOT CMAKE_HOST_WIN32) + unset(Foo_ROOT) + unset(ENV{Foo_ROOT}) + endif() + unset(FOO_TEST_FILE_FOO) + unset(FOO_TEST_FILE_ZOT) + unset(FOO_TEST_PATH_FOO) + unset(FOO_TEST_PATH_ZOT) + unset(FOO_TEST_PROG_FOO) + unset(FOO_TEST_FILE_FOO CACHE) + unset(FOO_TEST_FILE_ZOT CACHE) + unset(FOO_TEST_PATH_FOO CACHE) + unset(FOO_TEST_PATH_ZOT CACHE) + unset(FOO_TEST_PROG_FOO CACHE) +endmacro() + +RunTestCase() + +set(FOO_ROOT ${PackageRoot_BASE}/foo/cmake_root) +set(ENV{FOO_ROOT} ${PackageRoot_BASE}/foo/env_root) +if(RunCMake_TEST MATCHES "CaseSensitive") + if(RunCMake_TEST STREQUAL "CMP0144-WARN-CaseSensitive-Mixed") + set(Foo_ROOT "${FOO_ROOT}") + set(ENV{Foo_ROOT} "$ENV{FOO_ROOT}") + else() + set(Foo_ROOT ${PackageRoot_BASE}/does_not_exist) + set(ENV{Foo_ROOT} ${PackageRoot_BASE}/does_not_exist) + endif() +endif() +RunTestCase() + +set(FOO_ROOT ${PackageRoot_BASE}/foo/cmake_root) +if(RunCMake_TEST MATCHES "CaseSensitive") + if(RunCMake_TEST STREQUAL "CMP0144-WARN-CaseSensitive-Mixed") + set(Foo_ROOT "${FOO_ROOT}") + else() + set(Foo_ROOT ${PackageRoot_BASE}/does_not_exist) + endif() +endif() +RunTestCase() + +set(ENV{FOO_ROOT} ${PackageRoot_BASE}/foo/env_root) +if(RunCMake_TEST MATCHES "CaseSensitive") + if(RunCMake_TEST STREQUAL "CMP0144-WARN-CaseSensitive-Mixed") + set(ENV{Foo_ROOT} "$ENV{FOO_ROOT}") + else() + set(ENV{Foo_ROOT} ${PackageRoot_BASE}/does_not_exist) + endif() +endif() +RunTestCase() + +message("----------") diff --git a/Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt new file mode 100644 index 0000000..8249211 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt @@ -0,0 +1,4 @@ +^CMake Warning at CMP0145-NEW\.cmake:[0-9]+ \(find_package\): + No "FindDart\.cmake" found in CMAKE_MODULE_PATH\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_package/CMP0145-NEW.cmake b/Tests/RunCMake/find_package/CMP0145-NEW.cmake new file mode 100644 index 0000000..f3e7bef --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0145-NEW.cmake @@ -0,0 +1,7 @@ +cmake_policy(SET CMP0145 NEW) +set(_FindDart_testing TRUE) +find_package(Dart MODULE) + +if(_FindDart_included) + message(FATAL_ERROR "FindDart.cmake erroneously included") +endif() diff --git a/Tests/RunCMake/find_package/CMP0145-OLD.cmake b/Tests/RunCMake/find_package/CMP0145-OLD.cmake new file mode 100644 index 0000000..9a73f68 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0145-OLD.cmake @@ -0,0 +1,7 @@ +cmake_policy(SET CMP0145 OLD) +set(_FindDart_testing TRUE) +find_package(Dart MODULE) + +if(NOT _FindDart_included) + message(FATAL_ERROR "FindDart.cmake not included") +endif() diff --git a/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt new file mode 100644 index 0000000..36c66ec --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt @@ -0,0 +1,8 @@ +CMake Warning \(dev\) at CMP0145-WARN\.cmake:[0-9]+ \(find_package\): + Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run + "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. + +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/find_package/CMP0145-WARN.cmake b/Tests/RunCMake/find_package/CMP0145-WARN.cmake new file mode 100644 index 0000000..76da752 --- /dev/null +++ b/Tests/RunCMake/find_package/CMP0145-WARN.cmake @@ -0,0 +1,6 @@ +set(_FindDart_testing TRUE) +find_package(Dart MODULE) + +if(NOT _FindDart_included) + message(FATAL_ERROR "FindDart.cmake not included") +endif() diff --git a/Tests/RunCMake/find_package/CMakeLists.txt b/Tests/RunCMake/find_package/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/find_package/CMakeLists.txt +++ b/Tests/RunCMake/find_package/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index fa41fc1..f0bb011 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake @@ -36,6 +36,9 @@ run_cmake(WrongVersionConfig) run_cmake(CMP0084-OLD) run_cmake(CMP0084-WARN) run_cmake(CMP0084-NEW) +run_cmake(CMP0145-OLD) +run_cmake(CMP0145-WARN) +run_cmake(CMP0145-NEW) run_cmake(WrongVersionRange) run_cmake(EmptyVersionRange) run_cmake(VersionRangeWithEXACT) @@ -55,6 +58,17 @@ run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) run_cmake(REGISTRY_VIEW-propagated) +if(CMAKE_HOST_WIN32) + run_cmake(CMP0144-WARN-CaseInsensitive) + run_cmake(CMP0144-OLD-CaseInsensitive) + run_cmake(CMP0144-NEW-CaseInsensitive) +else() + run_cmake(CMP0144-WARN-CaseSensitive) + run_cmake(CMP0144-WARN-CaseSensitive-Mixed) + run_cmake(CMP0144-OLD-CaseSensitive) + run_cmake(CMP0144-NEW-CaseSensitive) +endif() + file( GLOB SearchPaths_TEST_CASE_LIST LIST_DIRECTORIES TRUE diff --git a/Tests/RunCMake/find_path/CMakeLists.txt b/Tests/RunCMake/find_path/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/find_path/CMakeLists.txt +++ b/Tests/RunCMake/find_path/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt b/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt new file mode 100644 index 0000000..fa767b9 --- /dev/null +++ b/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CMP0109-OLD.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0109 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/find_program/CMakeLists.txt b/Tests/RunCMake/find_program/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/find_program/CMakeLists.txt +++ b/Tests/RunCMake/find_program/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/get_filename_component/CMakeLists.txt b/Tests/RunCMake/get_filename_component/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/get_filename_component/CMakeLists.txt +++ b/Tests/RunCMake/get_filename_component/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/get_property/CMakeLists.txt b/Tests/RunCMake/get_property/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/get_property/CMakeLists.txt +++ b/Tests/RunCMake/get_property/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/if/CMakeLists.txt b/Tests/RunCMake/if/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/if/CMakeLists.txt +++ b/Tests/RunCMake/if/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/include_directories/CMakeLists.txt b/Tests/RunCMake/include_directories/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/include_directories/CMakeLists.txt +++ b/Tests/RunCMake/include_directories/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/include_external_msproject/CMakeLists.txt b/Tests/RunCMake/include_external_msproject/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/include_external_msproject/CMakeLists.txt +++ b/Tests/RunCMake/include_external_msproject/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/install/CMP0062-OLD-stderr.txt b/Tests/RunCMake/install/CMP0062-OLD-stderr.txt index de0b70f..6b4c4b0 100644 --- a/Tests/RunCMake/install/CMP0062-OLD-stderr.txt +++ b/Tests/RunCMake/install/CMP0062-OLD-stderr.txt @@ -1,10 +1,19 @@ -^CMake Deprecation Warning at CMP0062-OLD.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMP0062-OLD\.cmake:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) ++ +CMake Deprecation Warning at CMP0062-OLD\.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0062 will be removed from a future version - of CMake. + 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\)$ + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt index 6dd8cdf..93ee9df 100644 --- a/Tests/RunCMake/install/CMakeLists.txt +++ b/Tests/RunCMake/install/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 477ffe0..e5a0413 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.4) include(RunCMake) # Function to build and install a project. The latter step *-check.cmake @@ -167,7 +166,6 @@ unset(RunCMake_TEST_OPTIONS) run_install_test(Deprecated) run_install_test(PRE_POST_INSTALL_SCRIPT) -run_install_test(SCRIPT) run_install_test(TARGETS-CONFIGURATIONS) run_install_test(DIRECTORY-PATTERN) run_install_test(TARGETS-Parts) @@ -175,6 +173,10 @@ run_install_test(FILES-PERMISSIONS) run_install_test(TARGETS-RPATH) run_install_test(InstallRequiredSystemLibraries) +set(RunCMake_TEST_OPTIONS "-DCMAKE_POLICY_DEFAULT_CMP0087:STRING=NEW") +run_install_test(SCRIPT) +unset(RunCMake_TEST_OPTIONS) + if(UNIX) run_install_test(DIRECTORY-symlink-clobber) endif() diff --git a/Tests/RunCMake/install/SCRIPT-all-check.cmake b/Tests/RunCMake/install/SCRIPT-all-check.cmake index 48d8e1a..b9c38e3 100644 --- a/Tests/RunCMake/install/SCRIPT-all-check.cmake +++ b/Tests/RunCMake/install/SCRIPT-all-check.cmake @@ -1 +1 @@ -check_installed([[^empty1.txt;empty2.txt$]]) +check_installed([[^empty1.txt;empty2.txt;empty3.cmake;empty3.txt;empty4.cmake;empty4.txt$]]) diff --git a/Tests/RunCMake/install/SCRIPT.cmake b/Tests/RunCMake/install/SCRIPT.cmake index f857b54..ee0c80e 100644 --- a/Tests/RunCMake/install/SCRIPT.cmake +++ b/Tests/RunCMake/install/SCRIPT.cmake @@ -1,4 +1,10 @@ install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/empty3.cmake ${CMAKE_CURRENT_SOURCE_DIR}/empty4.cmake + DESTINATION . + ) +install( SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/install_script.cmake" CODE "write_empty_file(empty2.txt)" + SCRIPT "$<INSTALL_PREFIX>/empty3.cmake" + CODE [[include($<INSTALL_PREFIX>/empty4.cmake)]] ) diff --git a/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt b/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt index 138a69d..7d1477f 100644 --- a/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt @@ -1,11 +1,11 @@ ^CMake Warning \(dev\) at TARGETS-Defaults-Cache.cmake:[0-9]+ \(install\): Target lib3 has PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. CMake Warning \(dev\) at TARGETS-Defaults-Cache.cmake:[0-9]+ \(install\): Target lib4 has PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it.$ diff --git a/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt b/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt index 5f56986..5600801 100644 --- a/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt @@ -1,11 +1,11 @@ ^CMake Warning \(dev\) at TARGETS-Defaults.cmake:[0-9]+ \(install\): Target lib3 has PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. + CMake Warning \(dev\) at TARGETS-Defaults.cmake:[0-9]+ \(install\): Target lib4 has PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it.$ diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake index cba04b2..c72e405 100644 --- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.14) enable_language(C) # test matrix diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake index 43ae787..891b1ac 100644 --- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 3.14) enable_language(C) add_library(utils SHARED obj1.c) diff --git a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt index fe65fd3..26dcd42 100644 --- a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt @@ -1,5 +1,6 @@ ^CMake Error at TARGETS-NAMELINK_COMPONENT-bad-all\.cmake:5 \(install\): - install TARGETS given NAMELINK_COMPONENT option not in LIBRARY group\. The - NAMELINK_COMPONENT option may be specified only following LIBRARY\. + install TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE + group\. The NAMELINK_COMPONENT option may be specified only following + LIBRARY or ARCHIVE\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt index 60f52c4..8aed62b 100644 --- a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt +++ b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt @@ -1,5 +1,6 @@ ^CMake Error at TARGETS-NAMELINK_COMPONENT-bad-exc\.cmake:5 \(install\): - install TARGETS given NAMELINK_COMPONENT option not in LIBRARY group\. The - NAMELINK_COMPONENT option may be specified only following LIBRARY\. + install TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE + group\. The NAMELINK_COMPONENT option may be specified only following + LIBRARY or ARCHIVE\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/TARGETS-RPATH.cmake b/Tests/RunCMake/install/TARGETS-RPATH.cmake index b75deff..3e182f8 100644 --- a/Tests/RunCMake/install/TARGETS-RPATH.cmake +++ b/Tests/RunCMake/install/TARGETS-RPATH.cmake @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.9) - +cmake_policy(SET CMP0068 NEW) enable_language(C) set(CMAKE_BUILD_WITH_INSTALL_RPATH 1) diff --git a/Tests/RunCMake/install/empty3.cmake b/Tests/RunCMake/install/empty3.cmake new file mode 100644 index 0000000..18c2ac5 --- /dev/null +++ b/Tests/RunCMake/install/empty3.cmake @@ -0,0 +1 @@ +write_empty_file(empty3.txt) diff --git a/Tests/RunCMake/install/empty4.cmake b/Tests/RunCMake/install/empty4.cmake new file mode 100644 index 0000000..026a4d2 --- /dev/null +++ b/Tests/RunCMake/install/empty4.cmake @@ -0,0 +1 @@ +write_empty_file(empty4.txt) diff --git a/Tests/RunCMake/list/CMakeLists.txt b/Tests/RunCMake/list/CMakeLists.txt index 4b3de84..93ee9df 100644 --- a/Tests/RunCMake/list/CMakeLists.txt +++ b/Tests/RunCMake/list/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt b/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt index 9103bd2..5dd76f7 100644 --- a/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt +++ b/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt @@ -1,13 +1,13 @@ -^CMake Deprecation Warning at GET-CMP0007-WARN.cmake:1 \(cmake_policy\): - Compatibility with CMake < 2.8.12 will be removed from a future version of - CMake. +^CMake Deprecation Warning at GET-CMP0007-WARN\.cmake:1 \(cmake_policy\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. - Update the VERSION argument <min> value or use a ...<max> suffix to tell - CMake that the project does not need compatibility with older versions. + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + -CMake Warning \(dev\) at GET-CMP0007-WARN.cmake:4 \(list\): +CMake Warning \(dev\) at GET-CMP0007-WARN\.cmake:4 \(list\): Policy CMP0007 is not set: list command no longer ignores empty elements. Run "cmake --help-policy CMP0007" for policy details. Use the cmake_policy command to set the policy and suppress this warning. List has value = diff --git a/Tests/RunCMake/math/CMakeLists.txt b/Tests/RunCMake/math/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/math/CMakeLists.txt +++ b/Tests/RunCMake/math/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/message/CMakeLists.txt b/Tests/RunCMake/message/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/message/CMakeLists.txt +++ b/Tests/RunCMake/message/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/message/warnmessage-rootdir.cmake b/Tests/RunCMake/message/warnmessage-rootdir.cmake index f82efb9..f400079 100644 --- a/Tests/RunCMake/message/warnmessage-rootdir.cmake +++ b/Tests/RunCMake/message/warnmessage-rootdir.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.15) - # Generating the backtrace for this warning message used to trigger a # spurious assertion when the current directory is the root directory message(WARNING "We expect to see this warning message") diff --git a/Tests/RunCMake/no_install_prefix/CMakeLists.txt b/Tests/RunCMake/no_install_prefix/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/no_install_prefix/CMakeLists.txt +++ b/Tests/RunCMake/no_install_prefix/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/project/CMP0048-NEW-stderr.txt b/Tests/RunCMake/project/CMP0048-NEW-stderr.txt new file mode 100644 index 0000000..ad75f43 --- /dev/null +++ b/Tests/RunCMake/project/CMP0048-NEW-stderr.txt @@ -0,0 +1,6 @@ +^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\.$ diff --git a/Tests/RunCMake/project/CMP0048-OLD-stderr.txt b/Tests/RunCMake/project/CMP0048-OLD-stderr.txt index 695fb70..376249b 100644 --- a/Tests/RunCMake/project/CMP0048-OLD-stderr.txt +++ b/Tests/RunCMake/project/CMP0048-OLD-stderr.txt @@ -1,6 +1,13 @@ -^CMake Deprecation Warning at CMP0048-OLD.cmake:1 \(cmake_policy\): +^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMP0048-OLD\.cmake:1 \(cmake_policy\): The OLD behavior for policy CMP0048 will be removed from a future version - of CMake. + 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 diff --git a/Tests/RunCMake/project/CMakeLists.txt b/Tests/RunCMake/project/CMakeLists.txt index fdcaee9..a0ee6d7 100644 --- a/Tests/RunCMake/project/CMakeLists.txt +++ b/Tests/RunCMake/project/CMakeLists.txt @@ -1,5 +1,9 @@ -if(NOT "x${RunCMake_TEST}" STREQUAL "xNoMinimumRequired") +if("x${RunCMake_TEST}" STREQUAL "xNoMinimumRequired") + # No cmake_minimum_required(VERSION) +elseif(RunCMake_TEST MATCHES "^CMP0048") cmake_minimum_required(VERSION 2.8.12) +else() + cmake_minimum_required(VERSION 3.5) endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt index fc1a02b..c72534f 100644 --- a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt +++ b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt @@ -1,4 +1,4 @@ -^CMake Warning \(dev\) in CMakeLists.txt: +^CMake Warning \(dev\) in CMakeLists\.txt: No project\(\) command is present. The top-level CMakeLists.txt file must contain a literal, direct call to the project\(\) command. Add a line of code such as @@ -8,5 +8,12 @@ near the top of the file, but after cmake_minimum_required\(\). CMake is pretending there is a "project\(Project\)" command on the first - line. -This warning is for project developers. Use -Wno-dev to suppress it.$ + line\. +This warning is for project developers. Use -Wno-dev to suppress it\. ++ +CMake Deprecation Warning at CMakeLists\.txt:1 \(cmake_minimum_required\): + Compatibility with CMake < 3\.5 will be removed from a future version of + CMake\. + + Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell + CMake that the project does not need compatibility with older versions\.$ diff --git a/Tests/RunCMake/separate_arguments/CMakeLists.txt b/Tests/RunCMake/separate_arguments/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/separate_arguments/CMakeLists.txt +++ b/Tests/RunCMake/separate_arguments/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/set/CMakeLists.txt b/Tests/RunCMake/set/CMakeLists.txt index 4b3de84..93ee9df 100644 --- a/Tests/RunCMake/set/CMakeLists.txt +++ b/Tests/RunCMake/set/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/set/ParentPulling.cmake b/Tests/RunCMake/set/ParentPulling.cmake index 2614533..5649cb2 100644 --- a/Tests/RunCMake/set/ParentPulling.cmake +++ b/Tests/RunCMake/set/ParentPulling.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required(VERSION 3.0) -project(Minimal NONE) - function(test_set) set(blah "value2") message("before PARENT_SCOPE blah=${blah}") diff --git a/Tests/RunCMake/set/ParentPullingRecursive.cmake b/Tests/RunCMake/set/ParentPullingRecursive.cmake index a3e29f5..4d50561 100644 --- a/Tests/RunCMake/set/ParentPullingRecursive.cmake +++ b/Tests/RunCMake/set/ParentPullingRecursive.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required(VERSION 3.0) -project(Minimal NONE) - function(report where) message("----------") message("variable values at ${where}:") diff --git a/Tests/RunCMake/set_property/CMakeLists.txt b/Tests/RunCMake/set_property/CMakeLists.txt index 18dfd26..93ee9df 100644 --- a/Tests/RunCMake/set_property/CMakeLists.txt +++ b/Tests/RunCMake/set_property/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/string/CMakeLists.txt b/Tests/RunCMake/string/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/string/CMakeLists.txt +++ b/Tests/RunCMake/string/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/string/RegexClear.cmake b/Tests/RunCMake/string/RegexClear.cmake index d5edaac..4abe25e 100644 --- a/Tests/RunCMake/string/RegexClear.cmake +++ b/Tests/RunCMake/string/RegexClear.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required (VERSION 3.0) -project (RegexClear C) - function (output_results msg) message("results from: ${msg}") message("CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--") diff --git a/Tests/RunCMake/string/RegexMultiMatchClear.cmake b/Tests/RunCMake/string/RegexMultiMatchClear.cmake index 80b6b3c..788ba5e 100644 --- a/Tests/RunCMake/string/RegexMultiMatchClear.cmake +++ b/Tests/RunCMake/string/RegexMultiMatchClear.cmake @@ -1,6 +1,3 @@ -cmake_minimum_required (VERSION 3.0) -project (RegexClear NONE) - function (output_results msg) message("results from: ${msg}") message("CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--") diff --git a/Tests/RunCMake/target_compile_features/CMakeLists.txt b/Tests/RunCMake/target_compile_features/CMakeLists.txt index 2897109..93ee9df 100644 --- a/Tests/RunCMake/target_compile_features/CMakeLists.txt +++ b/Tests/RunCMake/target_compile_features/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake b/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake index 316b74b..4a0f068 100644 --- a/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake +++ b/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.16...3.17) - enable_language(C) add_library (func SHARED func.c) diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake index 22d3df7..6610d40 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.16...3.17) - enable_language(C) enable_language(CXX) diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake index 9feccd0..e2bd669 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake @@ -1,6 +1,3 @@ - -cmake_minimum_required(VERSION 3.16...3.17) - enable_language(C) enable_language(CXX) diff --git a/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake b/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake index 6c72546..a5bf2fb 100644 --- a/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake +++ b/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.11) +cmake_policy(VERSION 2.8.11) project(CMP0022-WARN) add_library(foo SHARED empty_vs6_1.cpp) diff --git a/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake b/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake index dfdf70b..d6f34a1 100644 --- a/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake +++ b/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.11) +cmake_policy(VERSION 2.8.11) project(CMP0022-WARN) add_library(foo SHARED empty_vs6_1.cpp) diff --git a/Tests/RunCMake/target_link_libraries/CMakeLists.txt b/Tests/RunCMake/target_link_libraries/CMakeLists.txt index 667561e..8eb5748 100644 --- a/Tests/RunCMake/target_link_libraries/CMakeLists.txt +++ b/Tests/RunCMake/target_link_libraries/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake index 189592d..7c5d77d 100644 --- a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) if(RunCMake_GENERATOR_IS_MULTI_CONFIG) set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) diff --git a/Tests/RunCMake/target_sources/CMP0076-WARN.cmake b/Tests/RunCMake/target_sources/CMP0076-WARN.cmake index 2e07331..20f1d5e 100644 --- a/Tests/RunCMake/target_sources/CMP0076-WARN.cmake +++ b/Tests/RunCMake/target_sources/CMP0076-WARN.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.12) - add_library(publiclib) add_subdirectory(CMP0076-WARN) diff --git a/Tests/RunCMake/target_sources/CMakeLists.txt b/Tests/RunCMake/target_sources/CMakeLists.txt index 727f93a..296fdda 100644 --- a/Tests/RunCMake/target_sources/CMakeLists.txt +++ b/Tests/RunCMake/target_sources/CMakeLists.txt @@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.11) project(${RunCMake_TEST} LANGUAGES NONE) -include(${RunCMake_TEST}.cmake) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/target_sources/FileSetDirect-result.txt b/Tests/RunCMake/target_sources/FileSetDirect-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDirect-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/FileSetDirect-stderr.txt b/Tests/RunCMake/target_sources/FileSetDirect-stderr.txt new file mode 100644 index 0000000..c1f7635 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDirect-stderr.txt @@ -0,0 +1,12 @@ +CMake Error at FileSetDirect.cmake:3 \(add_library\): + Cannot find source file: + + FILE_SET + + Tried extensions .c .C .c\+\+ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm .h + .hh .h\+\+ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 .f95 .f03 .hip .ispc + + Hint: the FILE_SET keyword may only appear after a visibility specifier or + another FILE_SET within the target_sources\(\) command. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/target_sources/FileSetDirect.cmake b/Tests/RunCMake/target_sources/FileSetDirect.cmake new file mode 100644 index 0000000..9f412c8 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetDirect.cmake @@ -0,0 +1,3 @@ +enable_language(C) + +add_library(lib1 STATIC empty.c FILE_SET h1.h TYPE HEADERS) diff --git a/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt b/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt index 9a2ca6a..2b5db43 100644 --- a/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt @@ -1,4 +1,4 @@ -^CMake Error at FileSetFileNoExist\.cmake:[0-9]+ \(add_library\): +^CMake Error at FileSetFileNoExist\.cmake:[0-9]+ \(target_sources\): Cannot find source file: [^ diff --git a/Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt b/Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt new file mode 100644 index 0000000..abfbe29 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt @@ -0,0 +1,12 @@ +CMake Error at FileSetWrongSyntax.cmake:4 \(target_sources\): + Cannot find source file: + + FILE_SET + + Tried extensions .c .C .c\+\+ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm .h + .hh .h\+\+ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 .f95 .f03 .hip .ispc + + Hint: the FILE_SET keyword may only appear after a visibility specifier or + another FILE_SET within the target_sources\(\) command. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake b/Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake new file mode 100644 index 0000000..709fb23 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake @@ -0,0 +1,4 @@ +enable_language(C) + +add_library(lib1 STATIC) +target_sources(lib1 PRIVATE empty.c FILE_SET h1.h TYPE HEADERS) diff --git a/Tests/RunCMake/target_sources/MissingSource-result.txt b/Tests/RunCMake/target_sources/MissingSource-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/target_sources/MissingSource-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/target_sources/MissingSource-stderr.txt b/Tests/RunCMake/target_sources/MissingSource-stderr.txt new file mode 100644 index 0000000..b4c81c2 --- /dev/null +++ b/Tests/RunCMake/target_sources/MissingSource-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at MissingSource\.cmake:[0-9]+ \(target_sources\): + Cannot find source file: + + missing\.txt +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/target_sources/MissingSource.cmake b/Tests/RunCMake/target_sources/MissingSource.cmake new file mode 100644 index 0000000..2bb71e4 --- /dev/null +++ b/Tests/RunCMake/target_sources/MissingSource.cmake @@ -0,0 +1,3 @@ +cmake_policy(SET CMP0115 NEW) +add_custom_target(foo) +target_sources(foo PRIVATE missing.txt) diff --git a/Tests/RunCMake/target_sources/OriginDebug-stderr.txt b/Tests/RunCMake/target_sources/OriginDebug-stderr.txt index 502d5f1..fd797cf 100644 --- a/Tests/RunCMake/target_sources/OriginDebug-stderr.txt +++ b/Tests/RunCMake/target_sources/OriginDebug-stderr.txt @@ -1,4 +1,4 @@ -CMake Debug Log at OriginDebug.cmake:13 \(add_library\): +CMake Debug Log at OriginDebug\.cmake:10 \(add_library\): Used sources for target OriginDebug: \* .*Tests/RunCMake/target_sources/empty_2.cpp @@ -6,7 +6,7 @@ CMake Debug Log at OriginDebug.cmake:13 \(add_library\): Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) .* -CMake Debug Log at OriginDebug.cmake:16 \(set_property\): +CMake Debug Log at OriginDebug\.cmake:13 \(set_property\): Used sources for target OriginDebug: \* .*Tests/RunCMake/target_sources/empty_3.cpp @@ -14,7 +14,7 @@ CMake Debug Log at OriginDebug.cmake:16 \(set_property\): Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) .* -CMake Debug Log at OriginDebug.cmake:20 \(target_sources\): +CMake Debug Log at OriginDebug\.cmake:17 \(target_sources\): Used sources for target OriginDebug: \* .*Tests/RunCMake/target_sources/empty_4.cpp @@ -22,7 +22,7 @@ CMake Debug Log at OriginDebug.cmake:20 \(target_sources\): Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) .* -CMake Debug Log at OriginDebug.cmake:14 \(target_link_libraries\): +CMake Debug Log at OriginDebug\.cmake:11 \(target_link_libraries\): Used sources for target OriginDebug: \* .*Tests/RunCMake/target_sources/empty_1.cpp diff --git a/Tests/RunCMake/target_sources/OriginDebug.cmake b/Tests/RunCMake/target_sources/OriginDebug.cmake index d40a1d8..e2d8477 100644 --- a/Tests/RunCMake/target_sources/OriginDebug.cmake +++ b/Tests/RunCMake/target_sources/OriginDebug.cmake @@ -1,7 +1,4 @@ - -cmake_minimum_required(VERSION 3.0) - -project(OriginDebug) +enable_language(CXX) set(CMAKE_DEBUG_TARGET_PROPERTIES SOURCES) diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake index 7c67c3f..90915cd 100644 --- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake @@ -9,6 +9,7 @@ run_cmake(OriginDebug) run_cmake(CMP0026-LOCATION) run_cmake(CMP0076-OLD) run_cmake(CMP0076-WARN) +run_cmake(MissingSource) run_cmake(RelativePathInInterface) run_cmake(RelativePathInSubdirGenEx) run_cmake(RelativePathInSubdirInterface) @@ -43,6 +44,8 @@ run_cmake(FileSetNoExistInstall) run_cmake(FileSetDirectories) run_cmake(FileSetCustomTarget) run_cmake(FileSetBadName) +run_cmake(FileSetWrongSyntax) +run_cmake(FileSetDirect) if(APPLE) run_cmake(FileSetFramework) endif() diff --git a/Tests/RunCMake/try_compile/CMP0056.cmake b/Tests/RunCMake/try_compile/CMP0056.cmake index 2ab79d5..634576e 100644 --- a/Tests/RunCMake/try_compile/CMP0056.cmake +++ b/Tests/RunCMake/try_compile/CMP0056.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_policy(VERSION 3.1) enable_language(C) set(obj "${CMAKE_C_OUTPUT_EXTENSION}") if(BORLAND) diff --git a/Tests/RunCMake/try_compile/CMakeLists.txt b/Tests/RunCMake/try_compile/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/try_compile/CMakeLists.txt +++ b/Tests/RunCMake/try_compile/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/try_run/CMakeLists.txt b/Tests/RunCMake/try_run/CMakeLists.txt index e93f0b6..e6c41a5 100644 --- a/Tests/RunCMake/try_run/CMakeLists.txt +++ b/Tests/RunCMake/try_run/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} C) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/variable_watch/CMakeLists.txt b/Tests/RunCMake/variable_watch/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/variable_watch/CMakeLists.txt +++ b/Tests/RunCMake/variable_watch/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/while/CMakeLists.txt b/Tests/RunCMake/while/CMakeLists.txt index 74b3ff8..93ee9df 100644 --- a/Tests/RunCMake/while/CMakeLists.txt +++ b/Tests/RunCMake/while/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/StagingPrefix/Consumer/CMakeLists.txt b/Tests/StagingPrefix/Consumer/CMakeLists.txt index a230441..f7195b0 100644 --- a/Tests/StagingPrefix/Consumer/CMakeLists.txt +++ b/Tests/StagingPrefix/Consumer/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(Consumer) diff --git a/Tests/StagingPrefix/Producer/CMakeLists.txt b/Tests/StagingPrefix/Producer/CMakeLists.txt index eb3d98f..c02d5de 100644 --- a/Tests/StagingPrefix/Producer/CMakeLists.txt +++ b/Tests/StagingPrefix/Producer/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(Producer) add_library(foo SHARED foo.cpp) diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt index 068fae9..6c0de9d 100644 --- a/Tests/StringFileTest/CMakeLists.txt +++ b/Tests/StringFileTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(StringFileTest) include_directories(${StringFileTest_BINARY_DIR}) diff --git a/Tests/SubDirSpaces/CMakeLists.txt b/Tests/SubDirSpaces/CMakeLists.txt index ecd4353..26a7da9 100644 --- a/Tests/SubDirSpaces/CMakeLists.txt +++ b/Tests/SubDirSpaces/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(SUBDIR) # Some systems do not seem to support rpath with spaces. diff --git a/Tests/TargetName/CMakeLists.txt b/Tests/TargetName/CMakeLists.txt index 21752b7..9864001 100644 --- a/Tests/TargetName/CMakeLists.txt +++ b/Tests/TargetName/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(TargetName) add_subdirectory(executables) diff --git a/Tests/TestDriver/CMakeLists.txt b/Tests/TestDriver/CMakeLists.txt index 3cc69c0..cd79372 100644 --- a/Tests/TestDriver/CMakeLists.txt +++ b/Tests/TestDriver/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(TestDriverTest) set(Extra_SRCS testExtraStuff.cxx testExtraStuff2.cxx ) diff --git a/Tests/Testing/CMakeLists.txt b/Tests/Testing/CMakeLists.txt index 8f69cbe..44afd4e 100644 --- a/Tests/Testing/CMakeLists.txt +++ b/Tests/Testing/CMakeLists.txt @@ -4,6 +4,8 @@ cmake_minimum_required (VERSION 2.7) project (Testing) +include (CTest) + # # Lib and exe path # @@ -25,12 +27,6 @@ else () endif () # -# Include Dart -# (will also set NSLOOKUP, HOSTNAME, etc.) -# -include (${CMAKE_ROOT}/Modules/Dart.cmake) - -# # Extra coverage # build_command(BUILD_COMMAND_VAR ${CMAKE_MAKE_PROGRAM}) diff --git a/Tests/TestsWorkingDirectory/CMakeLists.txt b/Tests/TestsWorkingDirectory/CMakeLists.txt index 2a0b015..f77370c 100644 --- a/Tests/TestsWorkingDirectory/CMakeLists.txt +++ b/Tests/TestsWorkingDirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(TestsWorkingDirectoryProj) add_executable(WorkingDirectory main.c) diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index 3e46ed5..ab2e007 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/TryCompile/Inner/CMakeLists.txt b/Tests/TryCompile/Inner/CMakeLists.txt index 9f89af2..262662d 100644 --- a/Tests/TryCompile/Inner/CMakeLists.txt +++ b/Tests/TryCompile/Inner/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(TryCompileInner C) try_compile(SHOULD_PASS diff --git a/Tests/VSExternalInclude/CMakeLists.txt b/Tests/VSExternalInclude/CMakeLists.txt index a3fd8d8..a44988e 100644 --- a/Tests/VSExternalInclude/CMakeLists.txt +++ b/Tests/VSExternalInclude/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.12) +cmake_minimum_required (VERSION 3.5) project(VSExternalInclude) if(${CMAKE_GENERATOR} MATCHES "Visual Studio 1[0124567]") diff --git a/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt b/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt index 950ec25..8918d98 100644 --- a/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt +++ b/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(FortranHello Fortran C) # add a function to test for -lsunquad on sunpro sun systems. diff --git a/Tests/VSMidl/CMakeLists.txt b/Tests/VSMidl/CMakeLists.txt index 342b8fb..f24640f 100644 --- a/Tests/VSMidl/CMakeLists.txt +++ b/Tests/VSMidl/CMakeLists.txt @@ -12,7 +12,7 @@ endif() message(STATUS "CMAKE_BUILDNAME='${CMAKE_BUILDNAME}'") message(STATUS "THIS_TESTNAME='${THIS_TESTNAME}'") -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(${THIS_TESTNAME}) include(ExternalProject) diff --git a/Tests/VSMidl/src/CMakeLists.txt b/Tests/VSMidl/src/CMakeLists.txt index 7e838b4..d8fd934 100644 --- a/Tests/VSMidl/src/CMakeLists.txt +++ b/Tests/VSMidl/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(VSMidl) include_directories("${CMAKE_CURRENT_BINARY_DIR}/\$(IntDir)") diff --git a/Tests/VSNASM/CMakeLists.txt b/Tests/VSNASM/CMakeLists.txt index a038ddd..6f82425 100644 --- a/Tests/VSNASM/CMakeLists.txt +++ b/Tests/VSNASM/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(VSNASM C ASM_NASM) if(CMAKE_SIZEOF_VOID_P EQUAL 8) diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index b084dd5..fee21b6 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.24 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 856ae81..791c8b5 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream <curl-library@lists.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-7_87_0" +readonly tag="curl-7_88_1" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index a9aa47d..bde6c6b 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.24 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py index 47e4909..9043709 100644 --- a/Utilities/Sphinx/cmake.py +++ b/Utilities/Sphinx/cmake.py @@ -16,6 +16,9 @@ from pygments.lexers import CMakeLexer from pygments.token import Name, Operator, Punctuation, String, Text, Comment, Generic, Whitespace, Number from pygments.lexer import bygroups +# RE to split multiple command signatures +sig_end_re = re.compile(r'(?<=[)])\n') + # Notes on regular expressions below: # - [\.\+-] are needed for string constants like gtk+-2.0 # - Unix paths are recognized by '/'; support for Windows paths may be added if needed @@ -57,14 +60,16 @@ CMakeLexer.tokens["root"] = [ # (r'[^<>\])\}\|$"# \t\n]+', Name.Exception), # fallback, for debugging only ] +from docutils.utils.code_analyzer import Lexer, LexerError from docutils.parsers.rst import Directive, directives from docutils.transforms import Transform from docutils import io, nodes -from sphinx.directives import ObjectDescription +from sphinx.directives import ObjectDescription, nl_escape_re from sphinx.domains import Domain, ObjType from sphinx.roles import XRefRole from sphinx.util.nodes import make_refnode +from sphinx.util import ws_re from sphinx import addnodes sphinx_before_1_4 = False @@ -286,9 +291,9 @@ class CMakeObject(ObjectDescription): def add_target_and_index(self, name, sig, signode): if self.objtype == 'command': - targetname = name.lower() + targetname = name.lower() else: - targetname = name + targetname = name targetid = '%s:%s' % (self.objtype, targetname) if targetid not in self.state.document.ids: signode['names'].append(targetid) @@ -302,6 +307,79 @@ class CMakeObject(ObjectDescription): if make_index_entry: self.indexnode['entries'].append(make_index_entry(name, targetid)) +class CMakeSignatureObject(CMakeObject): + object_type = 'signature' + + option_spec = { + 'target': directives.unchanged, + } + + def get_signatures(self): + content = nl_escape_re.sub('', self.arguments[0]) + lines = sig_end_re.split(content) + return [ws_re.sub(' ', line.strip()) for line in lines] + + def handle_signature(self, sig, signode): + language = 'cmake' + classes = ['code', 'cmake', 'highlight'] + + node = addnodes.desc_name(sig, '', classes=classes) + + try: + tokens = Lexer(sig, language, 'short') + except LexerError as error: + if self.state.document.settings.report_level > 2: + # Silently insert without syntax highlighting. + tokens = Lexer(sig, language, 'none') + else: + raise self.warning(error) + + for classes, value in tokens: + if classes: + node += nodes.inline(value, value, classes=classes) + else: + node += nodes.Text(value) + + signode.clear() + signode += node + + return sig + + def __init__(self, *args, **kwargs): + self.targetnames = {} + super().__init__(*args, **kwargs) + + def add_target_and_index(self, name, sig, signode): + if name in self.targetnames: + targetname = self.targetnames[name].lower() + else: + def extract_keywords(params): + for p in params: + if p[0].isalpha(): + yield p + else: + return + + keywords = extract_keywords(name.split('(')[1].split()) + targetname = ' '.join(keywords).lower() + targetid = nodes.make_id(targetname) + + if targetid not in self.state.document.ids: + signode['names'].append(targetname) + signode['ids'].append(targetid) + signode['first'] = (not self.names) + self.state.document.note_explicit_target(signode) + + def run(self): + targets = self.options.get('target') + if targets is not None: + signatures = self.get_signatures() + targets = [t.strip() for t in targets.split('\n')] + for signature, target in zip(signatures, targets): + self.targetnames[signature] = target + + return super().run() + class CMakeXRefRole(XRefRole): # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. @@ -411,19 +489,9 @@ class CMakeDomain(Domain): 'command': CMakeObject, 'envvar': CMakeObject, 'genex': CMakeObject, + 'signature': CMakeSignatureObject, 'variable': CMakeObject, - # Other object types cannot be created except by the CMakeTransform - # 'generator': CMakeObject, - # 'module': CMakeObject, - # 'policy': CMakeObject, - # 'prop_cache': CMakeObject, - # 'prop_dir': CMakeObject, - # 'prop_gbl': CMakeObject, - # 'prop_inst': CMakeObject, - # 'prop_sf': CMakeObject, - # 'prop_test': CMakeObject, - # 'prop_tgt': CMakeObject, - # 'manual': CMakeObject, + # Other `object_types` cannot be created except by the `CMakeTransform` } roles = { 'command': CMakeXRefRole(fix_parens = True, lowercase = True), diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in index fc3ecb5..d4e4059 100644 --- a/Utilities/Sphinx/conf.py.in +++ b/Utilities/Sphinx/conf.py.in @@ -89,3 +89,11 @@ html_favicon = '@conf_path@/static/cmake-favicon.ico' # qthelp_qch_name = "CMake.qch" linkcheck_ignore = [r'about:|https://gitlab.kitware.com/cmake/community/-/wikis/doc/cpack'] + +linkcheck_allowed_redirects = { + r'https://cdash\.org': r'https://www\.cdash\.org/', + r'https://docs\.nvidia\.com/cuda/': r'https://docs\.nvidia\.com/cuda/index\.html', + r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments': r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments\?.*', + r'https://openjdk\.java\.net/jeps/313': r'https://openjdk\.org:443/jeps/313', + r'https://www\.sphinx-doc\.org': r'https://www\.sphinx-doc\.org/en/master/', +} diff --git a/Utilities/Sphinx/static/cmake.css b/Utilities/Sphinx/static/cmake.css index 324cd92..dd0dd02 100644 --- a/Utilities/Sphinx/static/cmake.css +++ b/Utilities/Sphinx/static/cmake.css @@ -17,6 +17,29 @@ div.sphinxsidebarwrapper { background-color: #dfdfdf; } +/* Apply <pre> style (from classic.css) to signature directive argument. */ +.signature .sig { + padding: 5px; + background-color: #eeeeee; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +/* Add additional styling to signature directive argument. */ +.signature .sig { + margin-bottom: 5px; + padding-left: calc(5px + 3em); + text-indent: -3em; + font-family: monospace; +} + +.signature .sig .code.sig-name { + font-weight: normal; +} + /* Remove unwanted margin in case list item contains a div-wrapping directive like `.. versionadded` or `.. deprecated`. */ dd > :first-child > p { diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake index a38c101..0f2bdb4 100644 --- a/Utilities/cmThirdPartyChecks.cmake +++ b/Utilities/cmThirdPartyChecks.cmake @@ -161,6 +161,7 @@ if(WIN32) set(HAVE_REGEX_H 0) set(HAVE_RSA_H 0) set(HAVE_SELECT 0) + set(HAVE_SENDMSG 0) set(HAVE_SETENV 0) set(HAVE_SETMODE 1) set(HAVE_SETRLIMIT 0) diff --git a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in index b93e753..a3d2bc4 100644 --- a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in +++ b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake index 75215a1..142e919 100644 --- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake +++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index 6a9fdea..3dbba3c 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindBearSSL.cmake b/Utilities/cmcurl/CMake/FindBearSSL.cmake index 88d5e87..56a064e 100644 --- a/Utilities/cmcurl/CMake/FindBearSSL.cmake +++ b/Utilities/cmcurl/CMake/FindBearSSL.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake index 833e181..11ab7f8 100644 --- a/Utilities/cmcurl/CMake/FindBrotli.cmake +++ b/Utilities/cmcurl/CMake/FindBrotli.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -28,7 +28,7 @@ find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon) find_library(BROTLIDEC_LIBRARY NAMES brotlidec) -find_package_handle_standard_args(BROTLI +find_package_handle_standard_args(Brotli FOUND_VAR BROTLI_FOUND REQUIRED_VARS @@ -36,7 +36,7 @@ find_package_handle_standard_args(BROTLI BROTLICOMMON_LIBRARY BROTLI_INCLUDE_DIR FAIL_MESSAGE - "Could NOT find BROTLI" + "Could NOT find Brotli" ) set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) diff --git a/Utilities/cmcurl/CMake/FindCARES.cmake b/Utilities/cmcurl/CMake/FindCARES.cmake index 99cf31d..fa75891 100644 --- a/Utilities/cmcurl/CMake/FindCARES.cmake +++ b/Utilities/cmcurl/CMake/FindCARES.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake index ec2bd57..b244e61 100644 --- a/Utilities/cmcurl/CMake/FindGSS.cmake +++ b/Utilities/cmcurl/CMake/FindGSS.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -181,14 +181,14 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac set(GSS_FLAVOUR "MIT") else() # prevent compiling the header - just check if we can include it - set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D__ROKEN_H__") + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__) check_include_file( "roken.h" _GSS_HAVE_ROKEN_H) check_include_file( "heimdal/roken.h" _GSS_HAVE_HEIMDAL_ROKEN_H) if(_GSS_HAVE_ROKEN_H OR _GSS_HAVE_HEIMDAL_ROKEN_H) set(GSS_FLAVOUR "Heimdal") endif() - set(CMAKE_REQUIRED_DEFINITIONS "") + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__) endif() else() # I'm not convinced if this is the right way but this is what autotools do at the moment diff --git a/Utilities/cmcurl/CMake/FindLibPSL.cmake b/Utilities/cmcurl/CMake/FindLibPSL.cmake index 66abdd7..e3bd68d 100644 --- a/Utilities/cmcurl/CMake/FindLibPSL.cmake +++ b/Utilities/cmcurl/CMake/FindLibPSL.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindLibSSH2.cmake b/Utilities/cmcurl/CMake/FindLibSSH2.cmake index 0ec7f7e..a0c251a 100644 --- a/Utilities/cmcurl/CMake/FindLibSSH2.cmake +++ b/Utilities/cmcurl/CMake/FindLibSSH2.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindMSH3.cmake b/Utilities/cmcurl/CMake/FindMSH3.cmake index 96477e2..7d9c6b6 100644 --- a/Utilities/cmcurl/CMake/FindMSH3.cmake +++ b/Utilities/cmcurl/CMake/FindMSH3.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake index fcd6717..814bd97 100644 --- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake +++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake index 6d70c4a..3957646 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake index 8d8ebc1..9b13e6c 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake index 61e54c2..9f4e9f2 100644 --- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindNSS.cmake b/Utilities/cmcurl/CMake/FindNSS.cmake index 6742dda..ccddf42 100644 --- a/Utilities/cmcurl/CMake/FindNSS.cmake +++ b/Utilities/cmcurl/CMake/FindNSS.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindQUICHE.cmake b/Utilities/cmcurl/CMake/FindQUICHE.cmake index fc47027..0488463 100644 --- a/Utilities/cmcurl/CMake/FindQUICHE.cmake +++ b/Utilities/cmcurl/CMake/FindQUICHE.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake index 986f01e..d67c0eb 100644 --- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake +++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake index 2d65404..973e6ad 100644 --- a/Utilities/cmcurl/CMake/FindZstd.cmake +++ b/Utilities/cmcurl/CMake/FindZstd.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index 4d7380e..e12bf30 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index ed8d28a..fa1e458 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index 3cb4ffe..cef31b5 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake index 78bfd6f..9ff38e3 100644 --- a/Utilities/cmcurl/CMake/Utilities.cmake +++ b/Utilities/cmcurl/CMake/Utilities.cmake @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/cmake_uninstall.cmake.in b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in index 2549d41..3e0742d 100644 --- a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in +++ b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in index 496a92d..dbe4ed2 100644 --- a/Utilities/cmcurl/CMake/curl-config.cmake.in +++ b/Utilities/cmcurl/CMake/curl-config.cmake.in @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 49016c8..6fc316d 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -150,7 +150,7 @@ endif() # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -253,7 +253,7 @@ if(WIN32) set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") if(CURL_TARGET_WINDOWS_VERSION) add_definitions(-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}) - set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}) set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") endif() endif() @@ -488,7 +488,7 @@ include(CheckCSourceCompiles) # On windows preload settings if(WIN32) - set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WINSOCKAPI_=") + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WINSOCKAPI_=) include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake) endif() @@ -514,24 +514,6 @@ if(WIN32) endif() if(0) # This code not needed for building within CMake. -# This check below for use of deprecated symbols is only temporary and is to -# be removed again after a year's service. Remove after November 25, 2022. -set(CURL_RECONFIG_REQUIRED 0) -foreach(_LIB GSSAPI OPENLDAP LIBSSH LIBSSH2 BEARSSL MBEDTLS NSS OPENSSL - SCHANNEL SECTRANSP WOLFSSL) - if(CMAKE_USE_${_LIB}) - set(CURL_RECONFIG_REQUIRED 1) - message(SEND_ERROR "The option CMAKE_USE_${_LIB} was renamed to CURL_USE_${_LIB}.") - endif() -endforeach() -if(CMAKE_USE_WINSSL) - set(CURL_RECONFIG_REQUIRED 1) - message(SEND_ERROR "The option CMAKE_USE_WINSSL was renamed to CURL_USE_SCHANNEL.") -endif() -if(CURL_RECONFIG_REQUIRED) - message(FATAL_ERROR "Reconfig required") -endif() - # check SSL libraries # TODO support GnuTLS option(CURL_ENABLE_SSL "Enable SSL support" ON) @@ -577,7 +559,6 @@ if(CURL_USE_SCHANNEL) endif() if(CURL_WINDOWS_SSPI) set(USE_WINDOWS_SSPI ON) - set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DSECURITY_WIN32") endif() if(CURL_USE_SECTRANSP) @@ -630,8 +611,6 @@ if(CURL_USE_OPENSSL) if(CURL_CA_PATH) add_definitions(-DCURL_CA_PATH="${CURL_CA_PATH}") endif() - - add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED) endif() if(CURL_USE_MBEDTLS) @@ -826,7 +805,7 @@ if(NOT CURL_DISABLE_LDAP) return 0; }" ) - set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DLDAP_DEPRECATED=1") + list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1) list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) if(HAVE_LIBLBER) list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB}) @@ -1204,6 +1183,7 @@ check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) check_symbol_exists(socketpair "${CURL_INCLUDES}" HAVE_SOCKETPAIR) check_symbol_exists(recv "${CURL_INCLUDES}" HAVE_RECV) check_symbol_exists(send "${CURL_INCLUDES}" HAVE_SEND) +check_symbol_exists(sendmsg "${CURL_INCLUDES}" HAVE_SENDMSG) check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT) check_symbol_exists(strdup "${CURL_INCLUDES}" HAVE_STRDUP) check_symbol_exists(strtok_r "${CURL_INCLUDES}" HAVE_STRTOK_R) @@ -1248,7 +1228,7 @@ check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT) if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900)) # earlier MSVC compilers had faulty snprintf implementations - check_symbol_exists(snprintf "${CURL_INCLUDES}" HAVE_SNPRINTF) + check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF) endif() check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME) check_symbol_exists(inet_ntop "${CURL_INCLUDES}" HAVE_INET_NTOP) @@ -1451,7 +1431,7 @@ if(WIN32) # Check if crypto functions in wincrypt.h are actually available if(HAVE_WINCRYPT_H) - check_symbol_exists(CryptAcquireContext "${CURL_INCLUDES}" USE_WINCRYPT) + check_symbol_exists(CryptAcquireContext "windows.h;wincrypt.h" USE_WINCRYPT) endif() if(USE_WINCRYPT) set(USE_WIN32_CRYPTO ON) diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING index 90f05ad..d1eab3e 100644 --- a/Utilities/cmcurl/COPYING +++ b/Utilities/cmcurl/COPYING @@ -1,6 +1,6 @@ COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2022, Daniel Stenberg, <daniel@haxx.se>, and many +Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many contributors, see the THANKS file. All rights reserved. diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index b354757..c5ce0a5 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -167,7 +167,7 @@ typedef enum { CURLSSLBACKEND_SECURETRANSPORT = 9, CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10, CURLSSLBACKEND_MBEDTLS = 11, - CURLSSLBACKEND_MESALINK = 12, + CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12, CURLSSLBACKEND_BEARSSL = 13, CURLSSLBACKEND_RUSTLS = 14 } curl_sslbackend; @@ -248,7 +248,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_READ_SIZE /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ -#define CURL_MAX_READ_SIZE 524288 +#define CURL_MAX_READ_SIZE (10*1024*1024) #endif #ifndef CURL_MAX_WRITE_SIZE @@ -2259,8 +2259,13 @@ enum { CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 Upgrade */ - CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback. - Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */ + CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if + needed. For HTTPS only. For HTTP, this option + makes libcurl return error. */ + CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS + only. For HTTP, this makes libcurl + return error. */ + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ }; @@ -2953,6 +2958,7 @@ typedef enum { CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_DATA_CONNECT, CURL_LOCK_DATA_PSL, + CURL_LOCK_DATA_HSTS, CURL_LOCK_DATA_LAST } curl_lock_data; diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 94db4cd..ce39cdc 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -28,17 +28,17 @@ a script at release-time. This was made its own header file in 7.11.2 */ /* This is the global package copyright */ -#define LIBCURL_COPYRIGHT "1996 - 2022 Daniel Stenberg, <daniel@haxx.se>." +#define LIBCURL_COPYRIGHT "Daniel Stenberg, <daniel@haxx.se>." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.87.0" +#define LIBCURL_VERSION "7.88.1" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 87 -#define LIBCURL_VERSION_PATCH 0 +#define LIBCURL_VERSION_MINOR 88 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -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 0x075700 +#define LIBCURL_VERSION_NUM 0x075801 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h index 98ee888..394668a 100644 --- a/Utilities/cmcurl/include/curl/easy.h +++ b/Utilities/cmcurl/include/curl/easy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/include/curl/header.h b/Utilities/cmcurl/include/curl/header.h index 1598c6f..8df11e1 100644 --- a/Utilities/cmcurl/include/curl/header.h +++ b/Utilities/cmcurl/include/curl/header.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.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/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h index 06ef5c6..e652a65 100644 --- a/Utilities/cmcurl/include/curl/mprintf.h +++ b/Utilities/cmcurl/include/curl/mprintf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index c956d28..30a3d93 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/include/curl/options.h b/Utilities/cmcurl/include/curl/options.h index a792687..1ed76a9 100644 --- a/Utilities/cmcurl/include/curl/options.h +++ b/Utilities/cmcurl/include/curl/options.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.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/include/curl/stdcheaders.h b/Utilities/cmcurl/include/curl/stdcheaders.h index 82e1b5f..7451aa3 100644 --- a/Utilities/cmcurl/include/curl/stdcheaders.h +++ b/Utilities/cmcurl/include/curl/stdcheaders.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index 11db51e..def7739 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -227,16 +227,14 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int #elif defined(__OS400__) -# if defined(__ILEC400__) -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t -# define CURL_PULL_SYS_TYPES_H 1 -# define CURL_PULL_SYS_SOCKET_H 1 -# endif +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__MVS__) # if defined(__IBMC__) || defined(__IBMCPP__) diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index bf655bb..bc8d7a7 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -42,9 +42,8 @@ */ #define curl_easy_setopt(handle, option, value) \ __extension__({ \ - CURL_IGNORE_DEPRECATION(__typeof__(option) _curl_opt = option;) \ + CURLoption _curl_opt = (option); \ if(__builtin_constant_p(_curl_opt)) { \ - (void) option; \ CURL_IGNORE_DEPRECATION( \ if(curlcheck_long_option(_curl_opt)) \ if(!curlcheck_long(value)) \ @@ -120,9 +119,8 @@ /* wraps curl_easy_getinfo() with typechecking */ #define curl_easy_getinfo(handle, info, arg) \ __extension__({ \ - CURL_IGNORE_DEPRECATION(__typeof__(info) _curl_info = info;) \ + CURLINFO _curl_info = (info); \ if(__builtin_constant_p(_curl_info)) { \ - (void) info; \ CURL_IGNORE_DEPRECATION( \ if(curlcheck_string_info(_curl_info)) \ if(!curlcheck_arr((arg), char *)) \ diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index e15c213..b97b534 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -62,6 +62,7 @@ typedef enum { CURLUE_BAD_SCHEME, /* 27 */ CURLUE_BAD_SLASHES, /* 28 */ CURLUE_BAD_USER, /* 29 */ + CURLUE_LACKS_IDN, /* 30 */ CURLUE_LAST } CURLUcode; @@ -95,6 +96,7 @@ typedef enum { #define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the scheme is unknown. */ #define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ +#define CURLU_PUNYCODE (1<<12) /* get the host name in pynycode */ typedef struct Curl_URL CURLU; diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h index 4d57f91..fd6a916 100644 --- a/Utilities/cmcurl/include/curl/websockets.h +++ b/Utilities/cmcurl/include/curl/websockets.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -33,6 +33,7 @@ struct curl_ws_frame { int flags; /* See the CURLWS_* defines */ curl_off_t offset; /* the offset of this data into the frame */ curl_off_t bytesleft; /* number of pending bytes left of the payload */ + size_t len; /* size of the current data chunk */ }; /* flag bits */ diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 074ab9d..15853ff 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -136,11 +136,12 @@ set_target_properties(${LIB_NAME} PROPERTIES if(0) # This code not needed for building within CMake. if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR # FreeBSD comes with the a.out and elf flavours # but a.out was supported up to version 3.x and - # elf from 3.x. I cannot imagine someone runnig + # elf from 3.x. I cannot imagine someone running # CMake on those ancient systems CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 9eafa93..c28b475 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -79,16 +79,17 @@ LIB_VTLS_HFILES = \ vtls/x509asn1.h LIB_VQUIC_CFILES = \ - vquic/msh3.c \ - vquic/ngtcp2.c \ - vquic/quiche.c \ + vquic/curl_msh3.c \ + vquic/curl_ngtcp2.c \ + vquic/curl_quiche.c \ vquic/vquic.c LIB_VQUIC_HFILES = \ - vquic/msh3.h \ - vquic/ngtcp2.h \ - vquic/quiche.h \ - vquic/vquic.h + vquic/curl_msh3.h \ + vquic/curl_ngtcp2.h \ + vquic/curl_quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h LIB_VSSH_CFILES = \ vssh/libssh.c \ @@ -106,6 +107,8 @@ LIB_CFILES = \ base64.c \ bufref.c \ c-hyper.c \ + cf-http.c \ + cf-socket.c \ cfilters.c \ conncache.c \ connect.c \ @@ -118,6 +121,7 @@ LIB_CFILES = \ curl_get_line.c \ curl_gethostname.c \ curl_gssapi.c \ + curl_log.c \ curl_memrchr.c \ curl_multibyte.c \ curl_ntlm_core.c \ @@ -229,6 +233,8 @@ LIB_HFILES = \ asyn.h \ bufref.h \ c-hyper.h \ + cf-http.h \ + cf-socket.h \ cfilters.h \ conncache.h \ connect.h \ @@ -246,6 +252,7 @@ LIB_HFILES = \ curl_hmac.h \ curl_krb5.h \ curl_ldap.h \ + curl_log.h \ curl_md4.h \ curl_md5.h \ curl_memory.h \ @@ -312,7 +319,6 @@ LIB_HFILES = \ pop3.h \ progress.h \ psl.h \ - quic.h \ rand.h \ rename.h \ rtsp.h \ diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index ec18e38..31a7abc 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.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/altsvc.h b/Utilities/cmcurl/lib/altsvc.h index 2751d27..7fea143 100644 --- a/Utilities/cmcurl/lib/altsvc.h +++ b/Utilities/cmcurl/lib/altsvc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.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/amigaos.c b/Utilities/cmcurl/lib/amigaos.c index e8c2fc0..b0a9500 100644 --- a/Utilities/cmcurl/lib/amigaos.c +++ b/Utilities/cmcurl/lib/amigaos.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/amigaos.h b/Utilities/cmcurl/lib/amigaos.h index 9abfb59..7997ede 100644 --- a/Utilities/cmcurl/lib/amigaos.h +++ b/Utilities/cmcurl/lib/amigaos.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h index 523f7f5..de13738 100644 --- a/Utilities/cmcurl/lib/arpa_telnet.h +++ b/Utilities/cmcurl/lib/arpa_telnet.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 4436da3..19fe853 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index 705f0f6..4d7f860 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/asyn.h b/Utilities/cmcurl/lib/asyn.h index 1aab21a..7e207c4 100644 --- a/Utilities/cmcurl/lib/asyn.h +++ b/Utilities/cmcurl/lib/asyn.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/base64.c b/Utilities/cmcurl/lib/base64.c index bacd627..e1b7b72 100644 --- a/Utilities/cmcurl/lib/base64.c +++ b/Utilities/cmcurl/lib/base64.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/bufref.c b/Utilities/cmcurl/lib/bufref.c index 91b0374..ce686b6 100644 --- a/Utilities/cmcurl/lib/bufref.c +++ b/Utilities/cmcurl/lib/bufref.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2021 - 2022, Daniel Stenberg, <daniel@haxx.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/bufref.h b/Utilities/cmcurl/lib/bufref.h index 96b818b..dd424f1 100644 --- a/Utilities/cmcurl/lib/bufref.h +++ b/Utilities/cmcurl/lib/bufref.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2021 - 2022, Daniel Stenberg, <daniel@haxx.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/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index 65f5581..9c7632d 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -485,7 +485,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, if(k->upgr101 == UPGR101_WS) { if(http_status == 101) { /* verify the response */ - result = Curl_ws_accept(data); + result = Curl_ws_accept(data, NULL, 0); if(result) return result; } @@ -1128,6 +1128,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) goto error; } +#ifdef HAVE_LIBZ + /* we only consider transfer-encoding magic if libz support is built-in */ + result = Curl_transferencode(data); + if(result) + goto error; + result = Curl_hyper_header(data, headers, data->state.aptr.te); + if(result) + goto error; +#endif + if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { Curl_safefree(data->state.aptr.accept_encoding); @@ -1144,16 +1154,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) else Curl_safefree(data->state.aptr.accept_encoding); -#ifdef HAVE_LIBZ - /* we only consider transfer-encoding magic if libz support is built-in */ - result = Curl_transferencode(data); - if(result) - goto error; - result = Curl_hyper_header(data, headers, data->state.aptr.te); - if(result) - goto error; -#endif - result = cookies(data, conn, headers); if(result) goto error; diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h index 70507ad..4218cda 100644 --- a/Utilities/cmcurl/lib/c-hyper.h +++ b/Utilities/cmcurl/lib/c-hyper.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/cf-http.c b/Utilities/cmcurl/lib/cf-http.c new file mode 100644 index 0000000..2ee3d4d --- /dev/null +++ b/Utilities/cmcurl/lib/cf-http.c @@ -0,0 +1,518 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_HYPER) + +#include "urldata.h" +#include <curl/curl.h> +#include "curl_log.h" +#include "cfilters.h" +#include "connect.h" +#include "multiif.h" +#include "cf-http.h" +#include "http2.h" +#include "vquic/vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +typedef enum { + CF_HC_INIT, + CF_HC_CONNECT, + CF_HC_SUCCESS, + CF_HC_FAILURE +} cf_hc_state; + +struct cf_hc_baller { + const char *name; + struct Curl_cfilter *cf; + CURLcode result; + struct curltime started; + int reply_ms; + bool enabled; +}; + +static void cf_hc_baller_reset(struct cf_hc_baller *b, + struct Curl_easy *data) +{ + if(b->cf) { + Curl_conn_cf_close(b->cf, data); + Curl_conn_cf_discard_chain(&b->cf, data); + b->cf = NULL; + } + b->result = CURLE_OK; + b->reply_ms = -1; +} + +static bool cf_hc_baller_is_active(struct cf_hc_baller *b) +{ + return b->enabled && b->cf && !b->result; +} + +static bool cf_hc_baller_has_started(struct cf_hc_baller *b) +{ + return !!b->cf; +} + +static int cf_hc_baller_reply_ms(struct cf_hc_baller *b, + struct Curl_easy *data) +{ + if(b->reply_ms < 0) + b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS, + &b->reply_ms, NULL); + return b->reply_ms; +} + +static bool cf_hc_baller_data_pending(struct cf_hc_baller *b, + const struct Curl_easy *data) +{ + return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data); +} + +struct cf_hc_ctx { + cf_hc_state state; + const struct Curl_dns_entry *remotehost; + struct curltime started; /* when connect started */ + 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; +}; + +static void cf_hc_baller_init(struct cf_hc_baller *b, + struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *name, + int transport) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct Curl_cfilter *save = cf->next; + + b->name = name; + cf->next = NULL; + b->started = Curl_now(); + b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost, + transport, CURL_CF_SSL_ENABLE); + b->cf = cf->next; + cf->next = save; +} + +static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b, + struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct Curl_cfilter *save = cf->next; + + cf->next = b->cf; + b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done); + b->cf = cf->next; /* it might mutate */ + cf->next = save; + return b->result; +} + +static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + + if(ctx) { + cf_hc_baller_reset(&ctx->h3_baller, data); + cf_hc_baller_reset(&ctx->h21_baller, data); + ctx->state = CF_HC_INIT; + ctx->result = CURLE_OK; + ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; + ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; + } +} + +static CURLcode baller_connected(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_hc_baller *winner) +{ + struct cf_hc_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(winner->cf); + if(winner != &ctx->h3_baller) + cf_hc_baller_reset(&ctx->h3_baller, data); + if(winner != &ctx->h21_baller) + cf_hc_baller_reset(&ctx->h21_baller, data); + + DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", + winner->name, (int)Curl_timediff(Curl_now(), winner->started), + cf_hc_baller_reply_ms(winner, data))); + cf->next = winner->cf; + winner->cf = NULL; + + switch(cf->conn->alpn) { + case CURL_HTTP_VERSION_3: + infof(data, "using HTTP/3"); + break; + case CURL_HTTP_VERSION_2: +#ifdef USE_NGHTTP2 + /* Using nghttp2, we add the filter "below" us, so when the conn + * closes, we tear it down for a fresh reconnect */ + result = Curl_http2_switch_at(cf, data); + if(result) { + ctx->state = CF_HC_FAILURE; + ctx->result = result; + return result; + } +#endif + infof(data, "using HTTP/2"); + break; + case CURL_HTTP_VERSION_1_1: + infof(data, "using HTTP/1.1"); + break; + default: + infof(data, "using HTTP/1.x"); + break; + } + ctx->state = CF_HC_SUCCESS; + cf->connected = TRUE; + Curl_conn_cf_cntrl(cf->next, data, TRUE, + CF_CTRL_CONN_INFO_UPDATE, 0, NULL); + return result; +} + + +static bool time_to_start_h21(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct curltime now) +{ + struct cf_hc_ctx *ctx = cf->ctx; + timediff_t elapsed_ms; + + if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller)) + return FALSE; + + if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller)) + return TRUE; + + elapsed_ms = Curl_timediff(now, ctx->started); + if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { + DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21", + ctx->hard_eyeballs_timeout_ms)); + return TRUE; + } + + if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { + if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) { + DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not " + "seen any data, starting h21", + ctx->soft_eyeballs_timeout_ms)); + return TRUE; + } + /* set the effective hard timeout again */ + Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, + EXPIRE_ALPN_EYEBALLS); + } + return FALSE; +} + +static CURLcode cf_hc_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct curltime now; + CURLcode result = CURLE_OK; + + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + now = Curl_now(); + switch(ctx->state) { + case CF_HC_INIT: + DEBUGASSERT(!ctx->h3_baller.cf); + DEBUGASSERT(!ctx->h21_baller.cf); + DEBUGASSERT(!cf->next); + DEBUGF(LOG_CF(data, cf, "connect, init")); + ctx->started = now; + if(ctx->h3_baller.enabled) { + cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC); + if(ctx->h21_baller.enabled) + Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); + } + else if(ctx->h21_baller.enabled) + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP); + ctx->state = CF_HC_CONNECT; + /* FALLTHROUGH */ + + case CF_HC_CONNECT: + if(cf_hc_baller_is_active(&ctx->h3_baller)) { + result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done); + if(!result && *done) { + result = baller_connected(cf, data, &ctx->h3_baller); + goto out; + } + } + + if(time_to_start_h21(cf, data, now)) { + cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21", TRNSPRT_TCP); + } + + if(cf_hc_baller_is_active(&ctx->h21_baller)) { + DEBUGF(LOG_CF(data, cf, "connect, check h21")); + result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done); + if(!result && *done) { + result = baller_connected(cf, data, &ctx->h21_baller); + goto out; + } + } + + if((!ctx->h3_baller.enabled || ctx->h3_baller.result) && + (!ctx->h21_baller.enabled || ctx->h21_baller.result)) { + /* both failed or disabled. we give up */ + DEBUGF(LOG_CF(data, cf, "connect, all failed")); + result = ctx->result = ctx->h3_baller.enabled? + ctx->h3_baller.result : ctx->h21_baller.result; + ctx->state = CF_HC_FAILURE; + goto out; + } + result = CURLE_OK; + *done = FALSE; + break; + + case CF_HC_FAILURE: + result = ctx->result; + cf->connected = FALSE; + *done = FALSE; + break; + + case CF_HC_SUCCESS: + result = CURLE_OK; + cf->connected = TRUE; + *done = TRUE; + break; + } + +out: + DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); + return result; +} + +static int cf_hc_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct cf_hc_ctx *ctx = cf->ctx; + size_t i, j, s; + int brc, rc = GETSOCK_BLANK; + curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE]; + struct cf_hc_baller *ballers[2]; + + if(cf->connected) + return cf->next->cft->get_select_socks(cf->next, data, socks); + + ballers[0] = &ctx->h3_baller; + ballers[1] = &ctx->h21_baller; + for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + struct cf_hc_baller *b = ballers[i]; + if(!cf_hc_baller_is_active(b)) + continue; + brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks); + DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc)); + if(!brc) + continue; + for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) { + if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) { + socks[s] = bsocks[j]; + if(brc & GETSOCK_WRITESOCK(j)) + rc |= GETSOCK_WRITESOCK(s); + if(brc & GETSOCK_READSOCK(j)) + rc |= GETSOCK_READSOCK(s); + s++; + } + } + } + DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc)); + return rc; +} + +static bool cf_hc_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + + if(cf->connected) + return cf->next->cft->has_data_pending(cf->next, data); + + DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); + return cf_hc_baller_data_pending(&ctx->h3_baller, data) + || cf_hc_baller_data_pending(&ctx->h21_baller, data); +} + +static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + DEBUGF(LOG_CF(data, cf, "close")); + cf_hc_reset(cf, data); + cf->connected = FALSE; + + if(cf->next) { + cf->next->cft->close(cf->next, data); + Curl_conn_cf_discard_chain(&cf->next, data); + } +} + +static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + + (void)data; + DEBUGF(LOG_CF(data, cf, "destroy")); + cf_hc_reset(cf, data); + Curl_safefree(ctx); +} + +struct Curl_cftype Curl_cft_http_connect = { + "HTTPS-CONNECT", + 0, + CURL_LOG_DEFAULT, + cf_hc_destroy, + cf_hc_connect, + cf_hc_close, + Curl_cf_def_get_host, + cf_hc_get_select_socks, + cf_hc_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_hc_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21) +{ + struct Curl_cfilter *cf = NULL; + struct cf_hc_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->remotehost = remotehost; + ctx->h3_baller.enabled = try_h3; + ctx->h21_baller.enabled = try_h21; + + result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); + if(result) + goto out; + ctx = NULL; + cf_hc_reset(cf, data); + +out: + *pcf = result? NULL : cf; + free(ctx); + return result; +} + +CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +CURLcode +Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21) +{ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(data); + result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + return result; +} + +CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost) +{ + bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */ + CURLcode result = CURLE_OK; + + (void)sockindex; + (void)remotehost; + + if(!conn->bits.tls_enable_alpn) + goto out; + + if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { + result = Curl_conn_may_http3(data, conn); + if(result) /* can't do it */ + goto out; + try_h3 = TRUE; + try_h21 = FALSE; + } + else if(data->state.httpwant >= CURL_HTTP_VERSION_3) { + /* We assume that silently not even trying H3 is ok here */ + /* TODO: should we fail instead? */ + try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK); + try_h21 = TRUE; + } + + result = Curl_cf_http_connect_add(data, conn, sockindex, remotehost, + try_h3, try_h21); +out: + return result; +} + +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ diff --git a/Utilities/cmcurl/lib/cf-http.h b/Utilities/cmcurl/lib/cf-http.h new file mode 100644 index 0000000..6a39527 --- /dev/null +++ b/Utilities/cmcurl/lib/cf-http.h @@ -0,0 +1,58 @@ +#ifndef HEADER_CURL_CF_HTTP_H +#define HEADER_CURL_CF_HTTP_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_HYPER) + +struct Curl_cfilter; +struct Curl_easy; +struct connectdata; +struct Curl_cftype; +struct Curl_dns_entry; + +extern struct Curl_cftype Curl_cft_http_connect; + +CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21); + +CURLcode +Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + bool try_h3, bool try_h21); + + +CURLcode Curl_cf_https_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost); + + +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ +#endif /* HEADER_CURL_CF_HTTP_H */ diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c new file mode 100644 index 0000000..2549f34 --- /dev/null +++ b/Utilities/cmcurl/lib/cf-socket.c @@ -0,0 +1,1908 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> /* <netinet/tcp.h> may need it */ +#endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> /* for sockaddr_un */ +#endif +#ifdef HAVE_LINUX_TCP_H +#include <linux/tcp.h> +#elif defined(HAVE_NETINET_TCP_H) +#include <netinet/tcp.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef __VMS +#include <in.h> +#include <inet.h> +#endif + +#include "urldata.h" +#include "sendf.h" +#include "if2ip.h" +#include "strerror.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "select.h" +#include "url.h" /* for Curl_safefree() */ +#include "multiif.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ +#include "inet_ntop.h" +#include "inet_pton.h" +#include "progress.h" +#include "warnless.h" +#include "conncache.h" +#include "multihandle.h" +#include "share.h" +#include "version_win32.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) +{ +#if defined(TCP_NODELAY) + curl_socklen_t onoff = (curl_socklen_t) 1; + int level = IPPROTO_TCP; +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; +#else + (void) data; +#endif + + if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, + sizeof(onoff)) < 0) + infof(data, "Could not set TCP_NODELAY: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); +#else + (void)data; + (void)sockfd; +#endif +} + +#ifdef SO_NOSIGPIPE +/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when + sending data to a dead peer (instead of relying on the 4th argument to send + being MSG_NOSIGNAL). Possibly also existing and in use on other BSD + systems? */ +static void nosigpipe(struct Curl_easy *data, + curl_socket_t sockfd) +{ + int onoff = 1; + if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, + sizeof(onoff)) < 0) { +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) + char buffer[STRERROR_LEN]; + infof(data, "Could not set SO_NOSIGPIPE: %s", + Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); +#endif + } +} +#else +#define nosigpipe(x,y) Curl_nop_stmt +#endif + +#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H) +/* DragonFlyBSD and Windows use millisecond units */ +#define KEEPALIVE_FACTOR(x) (x *= 1000) +#else +#define KEEPALIVE_FACTOR(x) +#endif + +#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS) +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) + +struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; +}; +#endif + +static void +tcpkeepalive(struct Curl_easy *data, + curl_socket_t sockfd) +{ + int optval = data->set.tcp_keepalive?1:0; + + /* only set IDLE and INTVL if setting KEEPALIVE is successful */ + if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd); + } + else { +#if defined(SIO_KEEPALIVE_VALS) + struct tcp_keepalive vals; + DWORD dummy; + vals.onoff = 1; + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + vals.keepalivetime = optval; + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + vals.keepaliveinterval = optval; + if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), + NULL, 0, &dummy, NULL, NULL) != 0) { + infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d", + (int)sockfd, WSAGetLastError()); + } +#else +#ifdef TCP_KEEPIDLE + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd); + } +#elif defined(TCP_KEEPALIVE) + /* Mac OS X style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); + } +#endif +#ifdef TCP_KEEPINTVL + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd); + } +#endif +#endif + } +} + +void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, + const struct Curl_addrinfo *ai, + int transport) +{ + /* + * The Curl_sockaddr_ex structure is basically libcurl's external API + * curl_sockaddr structure with enough space available to directly hold + * any protocol-specific address structures. The variable declared here + * will be used to pass / receive data to/from the fopensocket callback + * if this has been set, before that, it is initialized from parameters. + */ + dest->family = ai->ai_family; + switch(transport) { + case TRNSPRT_TCP: + dest->socktype = SOCK_STREAM; + dest->protocol = IPPROTO_TCP; + break; + case TRNSPRT_UNIX: + dest->socktype = SOCK_STREAM; + dest->protocol = IPPROTO_IP; + break; + default: /* UDP and QUIC */ + dest->socktype = SOCK_DGRAM; + dest->protocol = IPPROTO_UDP; + break; + } + dest->addrlen = ai->ai_addrlen; + + if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) + dest->addrlen = sizeof(struct Curl_sockaddr_storage); + memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen); +} + +static CURLcode socket_open(struct Curl_easy *data, + struct Curl_sockaddr_ex *addr, + curl_socket_t *sockfd) +{ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + if(data->set.fopensocket) { + /* + * If the opensocket callback is set, all the destination address + * information is passed to the callback. Depending on this information the + * callback may opt to abort the connection, this is indicated returning + * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When + * the callback returns a valid socket the destination address information + * might have been changed and this 'new' address will actually be used + * here to connect. + */ + Curl_set_in_callback(data, true); + *sockfd = data->set.fopensocket(data->set.opensocket_client, + CURLSOCKTYPE_IPCXN, + (struct curl_sockaddr *)addr); + Curl_set_in_callback(data, false); + } + else { + /* opensocket callback not set, so simply create the socket now */ + *sockfd = socket(addr->family, addr->socktype, addr->protocol); + if(!*sockfd && addr->socktype == SOCK_DGRAM) { + /* This is icky and seems, at least, to happen on macOS: + * we get sockfd == 0 and if called again, we get a valid one > 0. + * If we close the 0, we sometimes get failures in multi poll, as + * 0 seems also be the fd for the sockpair used for WAKEUP polling. + * Very strange. Maybe this code should be ifdef'ed for macOS, but + * on "real" OS, fd 0 is stdin and we never see that. So... + */ + fake_sclose(*sockfd); + *sockfd = socket(addr->family, addr->socktype, addr->protocol); + DEBUGF(infof(data, "QUIRK: UDP socket() gave handle 0, 2nd attempt %d", + (int)*sockfd)); + } + } + + if(*sockfd == CURL_SOCKET_BAD) + /* no socket, no connection */ + return CURLE_COULDNT_CONNECT; + +#if defined(ENABLE_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; + } +#endif + return CURLE_OK; +} + +/* + * Create a socket based on info from 'conn' and 'ai'. + * + * 'addr' should be a pointer to the correct struct to get data back, or NULL. + * 'sockfd' must be a pointer to a socket descriptor. + * + * If the open socket callback is set, used that! + * + */ +CURLcode Curl_socket_open(struct Curl_easy *data, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + int transport, + curl_socket_t *sockfd) +{ + struct Curl_sockaddr_ex dummy; + + if(!addr) + /* if the caller doesn't want info back, use a local temp copy */ + addr = &dummy; + + Curl_sock_assign_addr(addr, ai, transport); + return socket_open(data, addr, sockfd); +} + +static int socket_close(struct Curl_easy *data, struct connectdata *conn, + int use_callback, curl_socket_t sock) +{ + if(use_callback && conn && conn->fclosesocket) { + int rc; + Curl_multi_closed(data, sock); + Curl_set_in_callback(data, true); + rc = conn->fclosesocket(conn->closesocket_client, sock); + Curl_set_in_callback(data, false); + return rc; + } + + if(conn) + /* tell the multi-socket code about this */ + Curl_multi_closed(data, sock); + + sclose(sock); + + return 0; +} + +/* + * Close a socket. + * + * 'conn' can be NULL, beware! + */ +int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sock) +{ + return socket_close(data, conn, FALSE, sock); +} + +bool Curl_socket_is_dead(curl_socket_t sock) +{ + int sval; + bool ret_val = TRUE; + + sval = SOCKET_READABLE(sock, 0); + if(sval == 0) + /* timeout */ + ret_val = FALSE; + + return ret_val; +} + + +#ifdef USE_WINSOCK +/* When you run a program that uses the Windows Sockets API, you may + experience slow performance when you copy data to a TCP server. + + https://support.microsoft.com/kb/823764 + + Work-around: Make the Socket Send Buffer Size Larger Than the Program Send + Buffer Size + + The problem described in this knowledge-base is applied only to pre-Vista + Windows. Following function trying to detect OS version and skips + SO_SNDBUF adjustment for Windows Vista and above. +*/ +#define DETECT_OS_NONE 0 +#define DETECT_OS_PREVISTA 1 +#define DETECT_OS_VISTA_OR_LATER 2 + +void Curl_sndbufset(curl_socket_t sockfd) +{ + int val = CURL_MAX_WRITE_SIZE + 32; + int curval = 0; + int curlen = sizeof(curval); + + static int detectOsState = DETECT_OS_NONE; + + if(detectOsState == DETECT_OS_NONE) { + if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) + detectOsState = DETECT_OS_VISTA_OR_LATER; + else + detectOsState = DETECT_OS_PREVISTA; + } + + if(detectOsState == DETECT_OS_VISTA_OR_LATER) + return; + + if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) + if(curval > val) + return; + + setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); +} +#endif + +static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sockfd, int af, unsigned int scope) +{ + struct Curl_sockaddr_storage sa; + 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 + struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; +#endif + + struct Curl_dns_entry *h = NULL; + unsigned short port = data->set.localport; /* use this port number, 0 for + "random" */ + /* how many port numbers to try to bind to, increasing one at a time */ + int portnum = data->set.localportrange; + const char *dev = data->set.str[STRING_DEVICE]; + int error; +#ifdef IP_BIND_ADDRESS_NO_PORT + int on = 1; +#endif +#ifndef ENABLE_IPV6 + (void)scope; +#endif + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if(!dev && !port) + /* no local kind of binding was requested */ + return CURLE_OK; + + memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); + + if(dev && (strlen(dev)<255) ) { + char myhost[256] = ""; + int done = 0; /* -1 for error, 1 for address found */ + bool is_interface = FALSE; + bool is_host = FALSE; + static const char *if_prefix = "if!"; + static const char *host_prefix = "host!"; + + if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { + dev += strlen(if_prefix); + is_interface = TRUE; + } + else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { + dev += strlen(host_prefix); + is_host = TRUE; + } + + /* interface */ + if(!is_host) { +#ifdef SO_BINDTODEVICE + /* I am not sure any other OSs than Linux that provide this feature, + * and at the least I cannot test. --Ben + * + * This feature allows one to tightly bind the local socket to a + * particular interface. This will force even requests to other + * local interfaces to go out the external interface. + * + * + * Only bind to the interface when specified as interface, not just + * as a hostname or ip address. + * + * interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try + * to use it straight away. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + dev, (curl_socklen_t)strlen(dev) + 1) == 0) { + /* This is typically "errno 1, error: Operation not permitted" if + * you're not running as root or another suitable privileged + * user. + * If it succeeds it means the parameter was a valid interface and + * not an IP address. Return immediately. + */ + return CURLE_OK; + } +#endif + + switch(Curl_if2ip(af, +#ifdef ENABLE_IPV6 + scope, conn->scope_id, +#endif + dev, myhost, sizeof(myhost))) { + case IF2IP_NOT_FOUND: + if(is_interface) { + /* Do not fall back to treating it as a host name */ + failf(data, "Couldn't bind to interface '%s'", dev); + return CURLE_INTERFACE_FAILED; + } + break; + case IF2IP_AF_NOT_SUPPORTED: + /* Signal the caller to try another address family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + case IF2IP_FOUND: + is_interface = TRUE; + /* + * We now have the numerical IP address in the 'myhost' buffer + */ + infof(data, "Local Interface %s is ip %s using address family %i", + dev, myhost, af); + done = 1; + break; + } + } + if(!is_interface) { + /* + * This was not an interface, resolve the name as a host name + * or IP number + * + * Temporarily force name resolution to use only the address type + * of the connection. The resolve functions should really be changed + * to take a type parameter instead. + */ + unsigned char ipver = conn->ip_version; + int rc; + + if(af == AF_INET) + conn->ip_version = CURL_IPRESOLVE_V4; +#ifdef ENABLE_IPV6 + else if(af == AF_INET6) + conn->ip_version = CURL_IPRESOLVE_V6; +#endif + + rc = Curl_resolv(data, dev, 0, FALSE, &h); + if(rc == CURLRESOLV_PENDING) + (void)Curl_resolver_wait_resolv(data, &h); + conn->ip_version = ipver; + + if(h) { + /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ + Curl_printable_address(h->addr, myhost, sizeof(myhost)); + infof(data, "Name '%s' family %i resolved to '%s' family %i", + dev, af, myhost, h->addr->ai_family); + Curl_resolv_unlock(data, h); + if(af != h->addr->ai_family) { + /* bad IP version combo, signal the caller to try another address + family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + } + done = 1; + } + else { + /* + * provided dev was no interface (or interfaces are not supported + * e.g. solaris) no ip address and no domain we fail here + */ + done = -1; + } + } + + if(done > 0) { +#ifdef ENABLE_IPV6 + /* IPv6 address */ + if(af == AF_INET6) { +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + char *scope_ptr = strchr(myhost, '%'); + if(scope_ptr) + *(scope_ptr++) = '\0'; +#endif + if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + if(scope_ptr) { + /* The "myhost" string either comes from Curl_if2ip or from + Curl_printable_address. The latter returns only numeric scope + IDs and the former returns none at all. So the scope ID, if + present, is known to be numeric */ + unsigned long scope_id = strtoul(scope_ptr, NULL, 10); + if(scope_id > UINT_MAX) + return CURLE_UNSUPPORTED_PROTOCOL; + + si6->sin6_scope_id = (unsigned int)scope_id; + } +#endif + } + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + /* IPv4 address */ + if((af == AF_INET) && + (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + } + + if(done < 1) { + /* errorbuf is set false so failf will overwrite any message already in + the error buffer, so the user receives this error message instead of a + generic resolve error. */ + data->state.errorbuf = FALSE; + failf(data, "Couldn't bind to '%s'", dev); + return CURLE_INTERFACE_FAILED; + } + } + else { + /* no device was given, prepare sa to match af's needs */ +#ifdef ENABLE_IPV6 + if(af == AF_INET6) { + si6->sin6_family = AF_INET6; + si6->sin6_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in6); + } + else +#endif + if(af == AF_INET) { + si4->sin_family = AF_INET; + si4->sin_port = htons(port); + sizeof_sa = sizeof(struct sockaddr_in); + } + } +#ifdef IP_BIND_ADDRESS_NO_PORT + (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); +#endif + 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; + } + + if(--portnum > 0) { + port++; /* try next port */ + if(port == 0) + break; + infof(data, "Bind to local port %hu failed, trying next", port - 1); + /* We re-use/clobber the port variable here below */ + if(sock->sa_family == AF_INET) + si4->sin_port = ntohs(port); +#ifdef ENABLE_IPV6 + else + si6->sin6_port = ntohs(port); +#endif + } + else + break; + } + { + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "bind failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + } + + return CURLE_INTERFACE_FAILED; +} + +/* + * verifyconnect() returns TRUE if the connect really has happened. + */ +static bool verifyconnect(curl_socket_t sockfd, int *error) +{ + bool rc = TRUE; +#ifdef SO_ERROR + int err = 0; + curl_socklen_t errSize = sizeof(err); + +#ifdef WIN32 + /* + * In October 2003 we effectively nullified this function on Windows due to + * problems with it using all CPU in multi-threaded cases. + * + * In May 2004, we bring it back to offer more info back on connect failures. + * Gisle Vanem could reproduce the former problems with this function, but + * could avoid them by adding this SleepEx() call below: + * + * "I don't have Rational Quantify, but the hint from his post was + * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe + * just Sleep(0) would be enough?) would release whatever + * mutex/critical-section the ntdll call is waiting on. + * + * Someone got to verify this on Win-NT 4.0, 2000." + */ + +#ifdef _WIN32_WCE + Sleep(0); +#else + SleepEx(0, FALSE); +#endif + +#endif + + if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) + err = SOCKERRNO; +#ifdef _WIN32_WCE + /* Old WinCE versions don't support SO_ERROR */ + if(WSAENOPROTOOPT == err) { + SET_SOCKERRNO(0); + err = 0; + } +#endif +#if defined(EBADIOCTL) && defined(__minix) + /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ + if(EBADIOCTL == err) { + SET_SOCKERRNO(0); + err = 0; + } +#endif + if((0 == err) || (EISCONN == err)) + /* we are connected, awesome! */ + rc = TRUE; + else + /* This wasn't a successful connect */ + rc = FALSE; + if(error) + *error = err; +#else + (void)sockfd; + if(error) + *error = SOCKERRNO; +#endif + return rc; +} + +CURLcode Curl_socket_connect_result(struct Curl_easy *data, + const char *ipaddress, int error) +{ + char buffer[STRERROR_LEN]; + + switch(error) { + case EINPROGRESS: + case EWOULDBLOCK: +#if defined(EAGAIN) +#if (EAGAIN) != (EWOULDBLOCK) + /* On some platforms EAGAIN and EWOULDBLOCK are the + * same value, and on others they are different, hence + * the odd #if + */ + case EAGAIN: +#endif +#endif + return CURLE_OK; + + default: + /* unknown error, fallthrough and try another address! */ + infof(data, "Immediate connect fail for %s: %s", + ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); + data->state.os_errno = error; + /* connect failed */ + return CURLE_COULDNT_CONNECT; + } +} + +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND +struct io_buffer { + char *bufr; + size_t allc; /* size of the current allocation */ + size_t head; /* bufr index for next read */ + size_t tail; /* bufr index for next write */ +}; + +static void io_buffer_reset(struct io_buffer *iob) +{ + if(iob->bufr) + free(iob->bufr); + memset(iob, 0, sizeof(*iob)); +} +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ + +struct cf_socket_ctx { + int transport; + struct Curl_sockaddr_ex addr; /* address to connect to */ + curl_socket_t sock; /* current attempt socket */ +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + struct io_buffer recv_buffer; +#endif + 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 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 */ + int error; /* errno of last failure or 0 */ + BIT(got_first_byte); /* if first byte was received */ + BIT(accepted); /* socket was accepted, not connected */ + BIT(active); +}; + +static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, + const struct Curl_addrinfo *ai, + int transport) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->sock = CURL_SOCKET_BAD; + ctx->transport = transport; + Curl_sock_assign_addr(&ctx->addr, ai, transport); +} + +static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + if(ctx && CURL_SOCKET_BAD != ctx->sock) { + if(ctx->active) { + /* We share our socket at cf->conn->sock[cf->sockindex] when active. + * If it is no longer there, someone has stolen (and hopefully + * closed it) and we just forget about it. + */ + if(ctx->sock == cf->conn->sock[cf->sockindex]) { + DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d, active)", + (int)ctx->sock)); + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + } + else { + DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at " + "conn->sock[], discarding", (int)ctx->sock)); + /* TODO: we do not want this to happen. Need to check which + * code is messing with conn->sock[cf->sockindex] */ + } + ctx->sock = CURL_SOCKET_BAD; + if(cf->sockindex == FIRSTSOCKET) + cf->conn->remote_addr = NULL; + } + else { + /* this is our local socket, we did never publish it */ + DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d, not active)", + (int)ctx->sock)); + sclose(ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + } +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + io_buffer_reset(&ctx->recv_buffer); +#endif + ctx->active = FALSE; + memset(&ctx->started_at, 0, sizeof(ctx->started_at)); + memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); + } + + cf->connected = FALSE; +} + +static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + cf_socket_close(cf, data); + DEBUGF(LOG_CF(data, cf, "destroy")); + free(ctx); + cf->ctx = NULL; +} + +static CURLcode set_local_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + +#ifdef HAVE_GETSOCKNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssloc; + curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); + + memset(&ssloc, 0, sizeof(ssloc)); + if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { + int error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, + ctx->l_ip, &ctx->l_port)) { + failf(data, "ssloc inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } +#else + (void)data; + ctx->l_ip[0] = 0; + ctx->l_port = -1; +#endif + return CURLE_OK; +} + +static CURLcode set_remote_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + /* store remote address and port used in this connection attempt */ + if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen, + ctx->r_ip, &ctx->r_port)) { + char buffer[STRERROR_LEN]; + + ctx->error = errno; + /* malformed address or bug in inet_ntop, try next address */ + failf(data, "sa_addr inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return CURLE_FAILED_INIT; + } + return CURLE_OK; +} + +static CURLcode cf_socket_open(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int error = 0; + bool isconnected = FALSE; + CURLcode result = CURLE_COULDNT_CONNECT; + bool is_tcp; + const char *ipmsg; + + (void)data; + DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); + ctx->started_at = Curl_now(); + result = socket_open(data, &ctx->addr, &ctx->sock); + if(result) + goto out; + + result = set_remote_ip(cf, data); + if(result) + goto out; + +#ifdef ENABLE_IPV6 + if(ctx->addr.family == AF_INET6) + ipmsg = " Trying [%s]:%d..."; + else +#endif + ipmsg = " Trying %s:%d..."; + infof(data, ipmsg, ctx->r_ip, ctx->r_port); + +#ifdef ENABLE_IPV6 + is_tcp = (ctx->addr.family == AF_INET + || ctx->addr.family == AF_INET6) && + ctx->addr.socktype == SOCK_STREAM; +#else + is_tcp = (ctx->addr.family == AF_INET) && + ctx->addr.socktype == SOCK_STREAM; +#endif + if(is_tcp && data->set.tcp_nodelay) + tcpnodelay(data, ctx->sock); + + nosigpipe(data, ctx->sock); + + Curl_sndbufset(ctx->sock); + + if(is_tcp && data->set.tcp_keepalive) + tcpkeepalive(data, ctx->sock); + + if(data->set.fsockopt) { + /* activate callback for setting socket options */ + Curl_set_in_callback(data, true); + error = data->set.fsockopt(data->set.sockopt_client, + ctx->sock, + CURLSOCKTYPE_IPCXN); + Curl_set_in_callback(data, false); + + if(error == CURL_SOCKOPT_ALREADY_CONNECTED) + isconnected = TRUE; + else if(error) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; + } + } + + /* possibly bind the local end to an IP, interface or port */ + if(ctx->addr.family == AF_INET +#ifdef ENABLE_IPV6 + || ctx->addr.family == AF_INET6 +#endif + ) { + result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, + Curl_ipv6_scope(&ctx->addr.sa_addr)); + if(result) { + if(result == CURLE_UNSUPPORTED_PROTOCOL) { + /* The address family is not supported on this interface. + We can continue trying addresses */ + result = CURLE_COULDNT_CONNECT; + } + goto out; + } + } + + /* set socket non-blocking */ + (void)curlx_nonblock(ctx->sock, TRUE); + +out: + if(result) { + if(ctx->sock != CURL_SOCKET_BAD) { + socket_close(data, cf->conn, TRUE, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + } + } + else if(isconnected) { + set_local_ip(cf, data); + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + } + DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%d", result, ctx->sock)); + return result; +} + +static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, + bool is_tcp_fastopen) +{ + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef TCP_FASTOPEN_CONNECT + int optval = 1; +#endif + int rc = -1; + + (void)data; + if(is_tcp_fastopen) { +#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ +# if defined(HAVE_BUILTIN_AVAILABLE) + /* while connectx function is available since macOS 10.11 / iOS 9, + it did not have the interface declared correctly until + Xcode 9 / macOS SDK 10.13 */ + if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = &ctx->addr.sa_addr; + endpoints.sae_dstaddrlen = ctx->addr.addrlen; + + rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY, + CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, + NULL, 0, NULL, NULL); + } + else { + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + } +# else + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); +# endif /* HAVE_BUILTIN_AVAILABLE */ +#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ + if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + (void *)&optval, sizeof(optval)) < 0) + infof(data, "Failed to enable TCP Fast Open on fd %d", ctx->sock); + + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); +#elif defined(MSG_FASTOPEN) /* old Linux */ + if(cf->conn->given->flags & PROTOPT_SSL) + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + else + rc = 0; /* Do nothing */ +#endif + } + else { + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + } + return rc; +} + +static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result = CURLE_COULDNT_CONNECT; + int rc = 0; + + (void)data; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* TODO: need to support blocking connect? */ + if(blocking) + return CURLE_UNSUPPORTED_PROTOCOL; + + *done = FALSE; /* a very negative world view is best */ + if(ctx->sock == CURL_SOCKET_BAD) { + + result = cf_socket_open(cf, data); + if(result) + goto out; + + /* Connect TCP socket */ + rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); + if(-1 == rc) { + result = Curl_socket_connect_result(data, ctx->r_ip, SOCKERRNO); + goto out; + } + } + +#ifdef mpeix + /* Call this function once now, and ignore the results. We do this to + "clear" the error state on the socket so that we can later read it + reliably. This is reported necessary on the MPE/iX operating + system. */ + (void)verifyconnect(ctx->sock, NULL); +#endif + /* check socket for connect */ + rc = SOCKET_WRITABLE(ctx->sock, 0); + + if(rc == 0) { /* no connection yet */ + DEBUGF(LOG_CF(data, cf, "not connected yet")); + return CURLE_OK; + } + else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { + if(verifyconnect(ctx->sock, &ctx->error)) { + /* we are connected with TCP, awesome! */ + ctx->connected_at = Curl_now(); + set_local_ip(cf, data); + *done = TRUE; + cf->connected = TRUE; + DEBUGF(LOG_CF(data, cf, "connected")); + return CURLE_OK; + } + } + else if(rc & CURL_CSELECT_ERR) { + (void)verifyconnect(ctx->sock, &ctx->error); + result = CURLE_COULDNT_CONNECT; + } + +out: + if(result) { + if(ctx->error) { + data->state.os_errno = ctx->error; + SET_SOCKERRNO(ctx->error); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + { + char buffer[STRERROR_LEN]; + infof(data, "connect to %s port %u failed: %s", + ctx->r_ip, ctx->r_port, + Curl_strerror(ctx->error, buffer, sizeof(buffer))); + } +#endif + } + if(ctx->sock != CURL_SOCKET_BAD) { + socket_close(data, cf->conn, TRUE, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + } + *done = FALSE; + } + return result; +} + +static void cf_socket_get_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char **phost, + const char **pdisplay_host, + int *pport) +{ + (void)data; + *phost = cf->conn->host.name; + *pdisplay_host = cf->conn->host.dispname; + *pport = cf->conn->port; +} + +static int cf_socket_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int rc = GETSOCK_BLANK; + + (void)data; + if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) { + socks[0] = ctx->sock; + rc |= GETSOCK_WRITESOCK(0); + } + + return rc; +} + +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + +static CURLcode pre_receive_plain(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + struct io_buffer * const iob = &ctx->recv_buffer; + + /* WinSock will destroy unread received data if send() is + failed. + To avoid lossage of received data, recv() must be + performed before every send() if any incoming data is + available. However, skip this, if buffer is already full. */ + if((cf->conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 && + cf->conn->recv[cf->sockindex] == Curl_conn_recv && + (!iob->bufr || (iob->allc > iob->tail))) { + const int readymask = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, 0); + if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) { + size_t bytestorecv = iob->allc - iob->tail; + ssize_t nread; + /* Have some incoming data */ + if(!iob->bufr) { + /* Use buffer double default size for intermediate buffer */ + iob->allc = 2 * data->set.buffer_size; + iob->bufr = malloc(iob->allc); + if(!iob->bufr) + return CURLE_OUT_OF_MEMORY; + iob->tail = 0; + iob->head = 0; + bytestorecv = iob->allc; + } + + nread = sread(ctx->sock, iob->bufr + iob->tail, bytestorecv); + if(nread > 0) + iob->tail += (size_t)nread; + } + } + return CURLE_OK; +} + +static ssize_t get_pre_recved(struct Curl_cfilter *cf, char *buf, size_t len) +{ + struct cf_socket_ctx *ctx = cf->ctx; + struct io_buffer * const iob = &ctx->recv_buffer; + size_t copysize; + if(!iob->bufr) + return 0; + + DEBUGASSERT(iob->allc > 0); + DEBUGASSERT(iob->tail <= iob->allc); + DEBUGASSERT(iob->head <= iob->tail); + /* Check and process data that already received and storied in internal + intermediate buffer */ + if(iob->tail > iob->head) { + copysize = CURLMIN(len, iob->tail - iob->head); + memcpy(buf, iob->bufr + iob->head, copysize); + iob->head += copysize; + } + else + copysize = 0; /* buffer was allocated, but nothing was received */ + + /* Free intermediate buffer if it has no unprocessed data */ + if(iob->head == iob->tail) + io_buffer_reset(iob); + + return (ssize_t)copysize; +} +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ + +static bool cf_socket_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int readable; + +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + if(ctx->recv_buffer.bufr && ctx->recv_buffer.allc && + ctx->recv_buffer.tail > ctx->recv_buffer.head) + return TRUE; +#endif + + (void)data; + readable = SOCKET_READABLE(ctx->sock, 0); + return (readable > 0 && (readable & CURL_CSELECT_IN)); +} + +static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_socket_ctx *ctx = cf->ctx; + curl_socket_t fdsave; + ssize_t nwritten; + + *err = CURLE_OK; + +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + /* WinSock will destroy unread received data if send() is + failed. + To avoid lossage of received data, recv() must be + performed before every send() if any incoming data is + available. */ + if(pre_receive_plain(cf, data)) { + *err = CURLE_OUT_OF_MEMORY; + return -1; + } +#endif + + fdsave = cf->conn->sock[cf->sockindex]; + cf->conn->sock[cf->sockindex] = ctx->sock; + +#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ + if(cf->conn->bits.tcp_fastopen) { + nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, + &cf->conn->remote_addr->sa_addr, + cf->conn->remote_addr->addrlen); + cf->conn->bits.tcp_fastopen = FALSE; + } + else +#endif + nwritten = swrite(ctx->sock, buf, len); + + if(-1 == nwritten) { + int sockerr = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) || + (EINPROGRESS == sockerr) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *err = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Send failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_SEND_ERROR; + } + } + + DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d", + len, (int)nwritten, *err)); + cf->conn->sock[cf->sockindex] = fdsave; + return nwritten; +} + +static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct cf_socket_ctx *ctx = cf->ctx; + curl_socket_t fdsave; + ssize_t nread; + + *err = CURLE_OK; + +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND + /* Check and return data that already received and storied in internal + intermediate buffer */ + nread = get_pre_recved(cf, buf, len); + if(nread > 0) { + *err = CURLE_OK; + return nread; + } +#endif + + fdsave = cf->conn->sock[cf->sockindex]; + cf->conn->sock[cf->sockindex] = ctx->sock; + + nread = sread(ctx->sock, buf, len); + + if(-1 == nread) { + int sockerr = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *err = CURLE_AGAIN; + } + else { + char buffer[STRERROR_LEN]; + failf(data, "Recv failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_RECV_ERROR; + } + } + + DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, + *err)); + if(nread > 0 && !ctx->got_first_byte) { + ctx->first_byte_at = Curl_now(); + ctx->got_first_byte = TRUE; + } + cf->conn->sock[cf->sockindex] = fdsave; + return nread; +} + +static void conn_set_primary_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef HAVE_GETPEERNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + curl_socklen_t plen; + int port; + + plen = sizeof(ssrem); + memset(&ssrem, 0, plen); + if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + cf->conn->primary_ip, &port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } +#else + cf->conn->primary_ip[0] = 0; + (void)data; +#endif +} + +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 */ + 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 + conn_set_primary_ip(cf, data); + set_local_ip(cf, data); + Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); + } + ctx->active = TRUE; +} + +static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_CONN_INFO_UPDATE: + cf_socket_active(cf, data); + break; + case CF_CTRL_CONN_REPORT_STATS: + switch(ctx->transport) { + case TRNSPRT_UDP: + case TRNSPRT_QUIC: + /* Since UDP connected sockets work different from TCP, we use the + * time of the first byte from the peer as the "connect" time. */ + if(ctx->got_first_byte) { + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at); + break; + } + /* FALLTHROUGH */ + default: + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->connected_at); + break; + } + break; + case CF_CTRL_DATA_SETUP: + Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); + break; + } + return CURLE_OK; +} + +static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int sval; + + (void)data; + if(!ctx || ctx->sock == CURL_SOCKET_BAD) + return FALSE; + + sval = SOCKET_READABLE(ctx->sock, 0); + if(sval == 0) { + /* timeout */ + return TRUE; + } + else if(sval & CURL_CSELECT_ERR) { + /* socket is in an error state */ + return FALSE; + } + else if(sval & CURL_CSELECT_IN) { + /* readable with no error. could still be closed */ +/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ +#ifdef MSG_PEEK + /* use the socket */ + char buf; + if(recv((RECV_TYPE_ARG1)ctx->sock, (RECV_TYPE_ARG2)&buf, + (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { + return FALSE; /* FIN received */ + } +#endif + return TRUE; + } + + return TRUE; +} + +static CURLcode cf_socket_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_socket_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_SOCKET: + DEBUGASSERT(pres2); + *((curl_socket_t *)pres2) = ctx->sock; + return CURLE_OK; + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->got_first_byte) { + timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +struct Curl_cftype Curl_cft_tcp = { + "TCP", + CF_TYPE_IP_CONNECT, + CURL_LOG_DEFAULT, + cf_socket_destroy, + cf_tcp_connect, + cf_socket_close, + cf_socket_get_host, + cf_socket_get_select_socks, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + struct cf_socket_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + DEBUGASSERT(transport == TRNSPRT_TCP); + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_socket_ctx_init(ctx, ai, transport); + + result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + int rc; + + /* QUIC needs a connected socket, nonblocking */ + DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); + + rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + if(-1 == rc) { + return Curl_socket_connect_result(data, ctx->r_ip, SOCKERRNO); + } + set_local_ip(cf, data); + DEBUGF(LOG_CF(data, cf, "%s socket %d connected: [%s:%d] -> [%s:%d]", + (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", + ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port)); + + (void)curlx_nonblock(ctx->sock, TRUE); + switch(ctx->addr.family) { +#if defined(__linux__) && defined(IP_MTU_DISCOVER) + case AF_INET: { + int val = IP_PMTUDISC_DO; + (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, + sizeof(val)); + break; + } +#endif +#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) + case AF_INET6: { + int val = IPV6_PMTUDISC_DO; + (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, + sizeof(val)); + break; + } +#endif + } + return CURLE_OK; +} + +static CURLcode cf_udp_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result = CURLE_COULDNT_CONNECT; + + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + *done = FALSE; + if(ctx->sock == CURL_SOCKET_BAD) { + result = cf_socket_open(cf, data); + if(result) { + DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result)); + if(ctx->sock != CURL_SOCKET_BAD) { + socket_close(data, cf->conn, TRUE, ctx->sock); + ctx->sock = CURL_SOCKET_BAD; + } + goto out; + } + + if(ctx->transport == TRNSPRT_QUIC) { + result = cf_udp_setup_quic(cf, data); + if(result) + goto out; + DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%d (%s:%d)", + ctx->sock, ctx->l_ip, ctx->l_port)); + } + else { + DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%d " + "(unconnected)", ctx->sock)); + } + *done = TRUE; + cf->connected = TRUE; + } +out: + return result; +} + +struct Curl_cftype Curl_cft_udp = { + "UDP", + CF_TYPE_IP_CONNECT, + CURL_LOG_DEFAULT, + cf_socket_destroy, + cf_udp_connect, + cf_socket_close, + cf_socket_get_host, + cf_socket_get_select_socks, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + struct cf_socket_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_socket_ctx_init(ctx, ai, transport); + + result = Curl_cf_create(&cf, &Curl_cft_udp, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +/* this is the TCP filter which can also handle this case */ +struct Curl_cftype Curl_cft_unix = { + "UNIX", + CF_TYPE_IP_CONNECT, + CURL_LOG_DEFAULT, + cf_socket_destroy, + cf_tcp_connect, + cf_socket_close, + cf_socket_get_host, + cf_socket_get_select_socks, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + struct cf_socket_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + DEBUGASSERT(transport == TRNSPRT_UNIX); + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_socket_ctx_init(ctx, ai, transport); + + result = Curl_cf_create(&cf, &Curl_cft_unix, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + /* we start accepted, if we ever close, we cannot go on */ + (void)data; + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + return CURLE_FAILED_INIT; +} + +struct Curl_cftype Curl_cft_tcp_accept = { + "TCP-ACCEPT", + CF_TYPE_IP_CONNECT, + CURL_LOG_DEFAULT, + cf_socket_destroy, + cf_tcp_accept_connect, + cf_socket_close, + cf_socket_get_host, /* TODO: not accurate */ + cf_socket_get_select_socks, + cf_socket_data_pending, + cf_socket_send, + cf_socket_recv, + cf_socket_cntrl, + cf_socket_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_socket_query, +}; + +CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, curl_socket_t *s) +{ + CURLcode result; + struct Curl_cfilter *cf = NULL; + struct cf_socket_ctx *ctx = NULL; + + /* replace any existing */ + Curl_conn_cf_discard_all(data, conn, sockindex); + DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); + + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->transport = conn->transport; + ctx->sock = *s; + ctx->accepted = FALSE; + result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); + + conn->sock[sockindex] = ctx->sock; + set_remote_ip(cf, data); + set_local_ip(cf, data); + ctx->active = TRUE; + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%d)", (int)ctx->sock)); + +out: + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + return result; +} + +CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, curl_socket_t *s) +{ + struct Curl_cfilter *cf = NULL; + struct cf_socket_ctx *ctx = NULL; + + cf = conn->cfilter[sockindex]; + if(!cf || cf->cft != &Curl_cft_tcp_accept) + return CURLE_FAILED_INIT; + + ctx = cf->ctx; + /* discard the listen socket */ + socket_close(data, conn, TRUE, ctx->sock); + ctx->sock = *s; + conn->sock[sockindex] = ctx->sock; + set_remote_ip(cf, data); + set_local_ip(cf, data); + ctx->active = TRUE; + ctx->accepted = TRUE; + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_accepted_set(%d)", (int)ctx->sock)); + + return CURLE_OK; +} + +bool Curl_cf_is_socket(struct Curl_cfilter *cf) +{ + return cf && (cf->cft == &Curl_cft_tcp || + cf->cft == &Curl_cft_udp || + cf->cft == &Curl_cft_unix || + cf->cft == &Curl_cft_tcp_accept); +} + +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) +{ + if(Curl_cf_is_socket(cf) && cf->ctx) { + struct cf_socket_ctx *ctx = cf->ctx; + + if(psock) + *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; + } + return CURLE_OK; + } + return CURLE_FAILED_INIT; +} + diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h new file mode 100644 index 0000000..f6eb810 --- /dev/null +++ b/Utilities/cmcurl/lib/cf-socket.h @@ -0,0 +1,192 @@ +#ifndef HEADER_CURL_CF_SOCKET_H +#define HEADER_CURL_CF_SOCKET_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 "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */ +#include "sockaddr.h" + +struct Curl_addrinfo; +struct Curl_cfilter; +struct Curl_easy; +struct connectdata; +struct Curl_sockaddr_ex; + +/* + * The Curl_sockaddr_ex structure is basically libcurl's external API + * curl_sockaddr structure with enough space available to directly hold any + * protocol-specific address structures. The variable declared here will be + * used to pass / receive data to/from the fopensocket callback if this has + * been set, before that, it is initialized from parameters. + */ +struct Curl_sockaddr_ex { + int family; + int socktype; + int protocol; + unsigned int addrlen; + union { + struct sockaddr addr; + struct Curl_sockaddr_storage buff; + } _sa_ex_u; +}; +#define sa_addr _sa_ex_u.addr + + +/* + * Create a socket based on info from 'conn' and 'ai'. + * + * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open + * socket callback is set, used that! + * + */ +CURLcode Curl_socket_open(struct Curl_easy *data, + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + int transport, + curl_socket_t *sockfd); + +int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, + curl_socket_t sock); + +/* + * This function should return TRUE if the socket is to be assumed to + * be dead. Most commonly this happens when the server has closed the + * connection due to inactivity. + */ +bool Curl_socket_is_dead(curl_socket_t sock); + +/** + * Determine the curl code for a socket connect() == -1 with errno. + */ +CURLcode Curl_socket_connect_result(struct Curl_easy *data, + const char *ipaddress, int error); + +#ifdef USE_WINSOCK +/* When you run a program that uses the Windows Sockets API, you may + experience slow performance when you copy data to a TCP server. + + https://support.microsoft.com/kb/823764 + + Work-around: Make the Socket Send Buffer Size Larger Than the Program Send + Buffer Size + +*/ +void Curl_sndbufset(curl_socket_t sockfd); +#else +#define Curl_sndbufset(y) Curl_nop_stmt +#endif + +/** + * Assign the address `ai` to the Curl_sockaddr_ex `dest` and + * set the transport used. + */ +void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that opens a TCP socket to the given address + * when calling its `connect` implementation. + * The filter will not touch any connection/data flags and can be + * used in happy eyeballing. Once selected for use, its `_active()` + * method needs to be called. + */ +CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that opens a UDP socket to the given address + * when calling its `connect` implementation. + * The filter will not touch any connection/data flags and can be + * used in happy eyeballing. Once selected for use, its `_active()` + * method needs to be called. + */ +CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that opens a UNIX socket to the given address + * when calling its `connect` implementation. + * The filter will not touch any connection/data flags and can be + * used in happy eyeballing. Once selected for use, its `_active()` + * method needs to be called. + */ +CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Creates a cfilter that keeps a listening socket. + */ +CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + curl_socket_t *s); + +/** + * Replace the listen socket with the accept()ed one. + */ +CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + curl_socket_t *s); + +/** + * Return TRUE iff `cf` is a socket filter. + */ +bool Curl_cf_is_socket(struct Curl_cfilter *cf); + +/** + * Peek at the socket and remote ip/port the socket filter is using. + * 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 + * 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); + +extern struct Curl_cftype Curl_cft_tcp; +extern struct Curl_cftype Curl_cft_udp; +extern struct Curl_cftype Curl_cft_unix; +extern struct Curl_cftype Curl_cft_tcp_accept; + +#endif /* HEADER_CURL_CF_SOCKET_H */ diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index bcb33da..2af0dd8 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -34,9 +34,6 @@ #include "multiif.h" #include "progress.h" #include "warnless.h" -#include "http_proxy.h" -#include "socks.h" -#include "vtls/vtls.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -54,89 +51,116 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data) (void)data; } -CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost) -{ - DEBUGASSERT(cf->next); - return cf->next->cft->setup(cf->next, data, remotehost); -} - -void Curl_cf_def_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)cf; - (void)data; -} - -void Curl_cf_def_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)cf; - (void)data; -} - void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGASSERT(cf->next); cf->connected = FALSE; - cf->next->cft->close(cf->next, data); + if(cf->next) + cf->next->cft->close(cf->next, data); } CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) { - DEBUGASSERT(cf->next); - return cf->next->cft->connect(cf->next, data, blocking, done); + CURLcode result; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + if(cf->next) { + result = cf->next->cft->connect(cf->next, data, blocking, done); + if(!result && *done) { + cf->connected = TRUE; + } + return result; + } + *done = FALSE; + return CURLE_FAILED_INIT; } void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, const char **phost, const char **pdisplay_host, int *pport) { - DEBUGASSERT(cf->next); - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + if(cf->next) + cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + else { + *phost = cf->conn->host.name; + *pdisplay_host = cf->conn->host.dispname; + *pport = cf->conn->port; + } } int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) { - DEBUGASSERT(cf->next); - return cf->next->cft->get_select_socks(cf->next, data, socks); + return cf->next? + cf->next->cft->get_select_socks(cf->next, data, socks) : 0; } bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { - DEBUGASSERT(cf->next); - return cf->next->cft->has_data_pending(cf->next, data); + return cf->next? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; } ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { - DEBUGASSERT(cf->next); - return cf->next->cft->do_send(cf->next, data, buf, len, err); + return cf->next? + cf->next->cft->do_send(cf->next, data, buf, len, err) : + CURLE_RECV_ERROR; } ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { - DEBUGASSERT(cf->next); - return cf->next->cft->do_recv(cf->next, data, buf, len, err); + return cf->next? + cf->next->cft->do_recv(cf->next, data, buf, len, err) : + CURLE_SEND_ERROR; } -void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, int index) +bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct Curl_cfilter *cfn, *cf = conn->cfilter[index]; + return cf->next? + cf->next->cft->is_alive(cf->next, data) : + FALSE; /* pessimistic in absence of data */ +} + +CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + return cf->next? + cf->next->cft->keep_alive(cf->next, data) : + CURLE_OK; +} + +CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, + struct Curl_easy *data) +{ + struct Curl_cfilter *cfn, *cf = *pcf; if(cf) { - conn->cfilter[index] = NULL; + *pcf = NULL; while(cf) { cfn = cf->next; + /* prevent destroying filter to mess with its sub-chain, since + * we have the reference now and will call destroy on it. + */ + cf->next = NULL; cf->cft->destroy(cf, data); free(cf); cf = cfn; @@ -144,6 +168,12 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, } } +void Curl_conn_cf_discard_all(struct Curl_easy *data, + struct connectdata *conn, int index) +{ + Curl_conn_cf_discard_chain(&conn->cfilter[index], data); +} + void Curl_conn_close(struct Curl_easy *data, int index) { struct Curl_cfilter *cf; @@ -160,7 +190,6 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf, size_t len, CURLcode *code) { struct Curl_cfilter *cf; - ssize_t nread; DEBUGASSERT(data); DEBUGASSERT(data->conn); @@ -169,13 +198,9 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf, cf = cf->next; } if(cf) { - nread = cf->cft->do_recv(cf, data, buf, len, code); - /* DEBUGF(infof(data, "Curl_conn_recv(handle=%p, index=%d)" - "-> %ld, err=%d", data, num, nread, *code));*/ - return nread; + return cf->cft->do_recv(cf, data, buf, len, code); } - failf(data, "no filter connected, conn=%ld, sockindex=%d", - data->conn->connection_id, num); + failf(data, CMSGI(data->conn, num, "recv: no filter connected")); *code = CURLE_FAILED_INIT; return -1; } @@ -184,7 +209,6 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num, const void *mem, size_t len, CURLcode *code) { struct Curl_cfilter *cf; - ssize_t nwritten; DEBUGASSERT(data); DEBUGASSERT(data->conn); @@ -193,13 +217,10 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num, cf = cf->next; } if(cf) { - nwritten = cf->cft->do_send(cf, data, mem, len, code); - /* DEBUGF(infof(data, "Curl_conn_send(handle=%p, index=%d, len=%ld)" - " -> %ld, err=%d", data, num, len, nwritten, *code));*/ - return nwritten; + return cf->cft->do_send(cf, data, mem, len, code); } - failf(data, "no filter connected, conn=%ld, sockindex=%d", - data->conn->connection_id, num); + failf(data, CMSGI(data->conn, num, "send: no filter connected")); + DEBUGASSERT(0); *code = CURLE_FAILED_INIT; return -1; } @@ -234,12 +255,31 @@ void Curl_conn_cf_add(struct Curl_easy *data, DEBUGASSERT(!cf->conn); DEBUGASSERT(!cf->next); - DEBUGF(infof(data, CMSGI(conn, index, "cf_add(filter=%s)"), - cf->cft->name)); cf->next = conn->cfilter[index]; cf->conn = conn; cf->sockindex = index; conn->cfilter[index] = cf; + DEBUGF(LOG_CF(data, cf, "added")); +} + +void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, + struct Curl_cfilter *cf_new) +{ + struct Curl_cfilter *tail, **pnext; + + DEBUGASSERT(cf_at); + DEBUGASSERT(cf_new); + DEBUGASSERT(!cf_new->conn); + + tail = cf_at->next; + cf_at->next = cf_new; + do { + cf_new->conn = cf_at->conn; + cf_new->sockindex = cf_at->sockindex; + pnext = &cf_new->next; + cf_new = cf_new->next; + } while(cf_new); + *pnext = tail; } void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -259,97 +299,54 @@ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data) free(cf); } -ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) +CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) { - return cf->cft->do_send(cf, data, buf, len, err); + if(cf) + return cf->cft->connect(cf, data, blocking, done); + return CURLE_FAILED_INIT; } -ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - return cf->cft->do_recv(cf, data, buf, len, err); + if(cf) + cf->cft->close(cf, data); } -CURLcode Curl_conn_setup(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - int ssl_mode) +int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) { - struct Curl_cfilter *cf; - CURLcode result; - - DEBUGASSERT(data); - /* If no filter is set, we have the "default" setup of connection filters. - * The filter chain from botton to top will be: - * - SOCKET socket filter for outgoing connection to remotehost - * if http_proxy tunneling is engaged: - * - SSL if proxytype is CURLPROXY_HTTPS - * - HTTP_PROXY_TUNNEL - * otherwise, if socks_proxy is engaged: - * - SOCKS_PROXY_TUNNEL - * - SSL if conn->handler has PROTOPT_SSL - */ - if(!conn->cfilter[sockindex]) { - DEBUGF(infof(data, DMSGI(data, sockindex, "setup, init filter chain"))); - result = Curl_conn_socket_set(data, conn, sockindex); - if(result) - goto out; - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) { - result = Curl_conn_socks_proxy_add(data, conn, sockindex); - if(result) - goto out; - } + if(cf) + return cf->cft->get_select_socks(cf, data, socks); + return 0; +} - if(conn->bits.httpproxy) { -#ifdef USE_SSL - if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { - result = Curl_ssl_cfilter_proxy_add(data, conn, sockindex); - if(result) - goto out; - } -#endif /* USE_SSL */ - -#if !defined(CURL_DISABLE_HTTP) - if(conn->bits.tunnel_proxy) { - result = Curl_conn_http_proxy_add(data, conn, sockindex); - if(result) - goto out; - } -#endif /* !CURL_DISABLE_HTTP */ - } -#endif /* !CURL_DISABLE_PROXY */ - -#ifdef USE_SSL - if(ssl_mode == CURL_CF_SSL_ENABLE - || (ssl_mode != CURL_CF_SSL_DISABLE - && conn->handler->flags & PROTOPT_SSL)) { - result = Curl_ssl_cfilter_add(data, conn, sockindex); - if(result) - goto out; - } -#else - (void)ssl_mode; -#endif /* USE_SSL */ - -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) - if(data->set.haproxyprotocol) { - result = Curl_conn_haproxy_add(data, conn, sockindex); - if(result) - goto out; - } -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ +bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + if(cf) + return cf->cft->has_data_pending(cf, data); + return FALSE; +} - } - DEBUGASSERT(conn->cfilter[sockindex]); - cf = data->conn->cfilter[sockindex]; - result = cf->cft->setup(cf, data, remotehost); +ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + if(cf) + return cf->cft->do_send(cf, data, buf, len, err); + *err = CURLE_SEND_ERROR; + return -1; +} -out: - return result; +ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + if(cf) + return cf->cft->do_recv(cf, data, buf, len, err); + *err = CURLE_RECV_ERROR; + return -1; } CURLcode Curl_conn_connect(struct Curl_easy *data, @@ -358,16 +355,26 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, bool *done) { struct Curl_cfilter *cf; - CURLcode result; + CURLcode result = CURLE_OK; DEBUGASSERT(data); + DEBUGASSERT(data->conn); cf = data->conn->cfilter[sockindex]; DEBUGASSERT(cf); - result = cf->cft->connect(cf, data, blocking, done); + if(!cf) + return CURLE_FAILED_INIT; + + *done = cf->connected; + if(!*done) { + result = cf->cft->connect(cf, data, blocking, done); + if(!result && *done) { + Curl_conn_ev_update_info(data, data->conn); + Curl_conn_ev_report_stats(data, data->conn); + data->conn->keepalive = Curl_now(); + } + } - DEBUGF(infof(data, DMSGI(data, sockindex, "connect(block=%d)-> %d, done=%d"), - blocking, result, *done)); return result; } @@ -394,11 +401,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) return FALSE; } -bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex) +bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) { - struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL; + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; - (void)data; for(; cf; cf = cf->next) { if(cf->cft->flags & CF_TYPE_SSL) return TRUE; @@ -408,6 +414,19 @@ bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex) return FALSE; } +bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_MULTIPLEX) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT + || cf->cft->flags & CF_TYPE_SSL) + return FALSE; + } + return FALSE; +} bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) { @@ -416,8 +435,6 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) (void)data; DEBUGASSERT(data); DEBUGASSERT(data->conn); - if(Curl_recv_has_postponed_data(data->conn, sockindex)) - return TRUE; cf = data->conn->cfilter[sockindex]; while(cf && !cf->connected) { @@ -437,46 +454,16 @@ int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex, DEBUGASSERT(data); DEBUGASSERT(data->conn); cf = data->conn->cfilter[sockindex]; + + /* if the next one is not yet connected, that's the one we want */ + while(cf && cf->next && !cf->next->connected) + cf = cf->next; if(cf) { return cf->cft->get_select_socks(cf, data, socks); } return GETSOCK_BLANK; } -void Curl_conn_attach_data(struct connectdata *conn, - struct Curl_easy *data) -{ - size_t i; - struct Curl_cfilter *cf; - - for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { - cf = conn->cfilter[i]; - if(cf) { - while(cf) { - cf->cft->attach_data(cf, data); - cf = cf->next; - } - } - } -} - -void Curl_conn_detach_data(struct connectdata *conn, - struct Curl_easy *data) -{ - size_t i; - struct Curl_cfilter *cf; - - for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { - cf = conn->cfilter[i]; - if(cf) { - while(cf) { - cf->cft->detach_data(cf, data); - cf = cf->next; - } - } - } -} - void Curl_conn_get_host(struct Curl_easy *data, int sockindex, const char **phost, const char **pdisplay_host, int *pport) @@ -491,7 +478,7 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex, else { /* Some filter ask during shutdown for this, mainly for debugging * purposes. We hand out the defaults, however this is not always - * accurate, as the connction might be tunneled, etc. But all that + * accurate, as the connection might be tunneled, etc. But all that * state is already gone here. */ *phost = data->conn->host.name; *pdisplay_host = data->conn->host.dispname; @@ -499,4 +486,158 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex, } } +CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + (void)cf; + (void)data; + (void)event; + (void)arg1; + (void)arg2; + return CURLE_OK; +} + +CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + + for(; cf; cf = cf->next) { + if(Curl_cf_def_cntrl == cf->cft->cntrl) + continue; + result = cf->cft->cntrl(cf, data, event, arg1, arg2); + if(!ignore_result && result) + break; + } + return result; +} + +curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + curl_socket_t sock; + if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock)) + return sock; + return CURL_SOCKET_BAD; +} + +curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf; + + cf = data->conn? data->conn->cfilter[sockindex] : NULL; + /* if the top filter has not connected, ask it (and its sub-filters) + * for the socket. Otherwise conn->sock[sockindex] should have it. + */ + if(cf && !cf->connected) + return Curl_conn_cf_get_socket(cf, data); + return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD; +} + +static CURLcode cf_cntrl_all(struct connectdata *conn, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + size_t i; + + for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) { + result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result, + event, arg1, arg2); + if(!ignore_result && result) + break; + } + return result; +} + +void Curl_conn_ev_data_attach(struct connectdata *conn, + struct Curl_easy *data) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL); +} + +void Curl_conn_ev_data_detach(struct connectdata *conn, + struct Curl_easy *data) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL); +} + +CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data) +{ + return cf_cntrl_all(data->conn, data, FALSE, + CF_CTRL_DATA_SETUP, 0, NULL); +} + +CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data) +{ + return cf_cntrl_all(data->conn, data, FALSE, + CF_CTRL_DATA_IDLE, 0, NULL); +} + +/** + * Notify connection filters that the transfer represented by `data` + * is donw with sending data (e.g. has uploaded everything). + */ +void Curl_conn_ev_data_done_send(struct Curl_easy *data) +{ + cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL); +} + +/** + * Notify connection filters that the transfer represented by `data` + * is finished - eventually premature, e.g. before being complete. + */ +void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature) +{ + cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL); +} + +CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause) +{ + return cf_cntrl_all(data->conn, data, FALSE, + CF_CTRL_DATA_PAUSE, do_pause, NULL); +} + +void Curl_conn_ev_update_info(struct Curl_easy *data, + struct connectdata *conn) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); +} + +void Curl_conn_ev_report_stats(struct Curl_easy *data, + struct connectdata *conn) +{ + cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_REPORT_STATS, 0, NULL); +} + +bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn) +{ + struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; + return cf && !cf->conn->bits.close && cf->cft->is_alive(cf, data); +} + +CURLcode Curl_conn_keep_alive(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + return cf? cf->cft->keep_alive(cf, data) : CURLE_OK; +} + +size_t Curl_conn_get_max_concurrent(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_MAX_CONCURRENT, + &n, NULL) : CURLE_UNKNOWN_OPTION; + return (result || n <= 0)? 1 : (size_t)n; +} diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index 4b81b42..94dc53f 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -36,11 +36,6 @@ struct connectdata; typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data); -/* Setup the connection for `data`, using destination `remotehost`. - */ -typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost); typedef void Curl_cft_close(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -89,29 +84,89 @@ typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, size_t len, /* amount to read */ CURLcode *err); /* error to return */ -typedef void Curl_cft_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); -typedef void Curl_cft_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); +typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data); + +typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data); /** - * The easy handle `data` is being detached (no longer served) - * by connection `conn`. All filters are informed to release any resources - * related to `data`. - * Note: there may be several `data` attached to a connection at the same - * time. + * Events/controls for connection filters, their arguments and + * return code handling. Filter callbacks are invoked "top down". + * Return code handling: + * "first fail" meaning that the first filter returning != CURLE_OK, will + * abort further event distribution and determine the result. + * "ignored" meaning return values are ignored and the event is distributed + * to all filters in the chain. Overall result is always CURLE_OK. */ -void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data); +/* data event arg1 arg2 return */ +#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */ +#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */ +#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ +#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */ +#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ +#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */ +#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ +/* update conn info at connection and data */ +#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ +/* report conn statistics (timers) for connection and data */ +#define CF_CTRL_CONN_REPORT_STATS (256+1) /* 0 NULL ignored */ +/** + * Handle event/control for the filter. + * Implementations MUST NOT chain calls to cf->next. + */ +typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2); + + +/** + * Queries to ask via a `Curl_cft_query *query` method on a cfilter chain. + * - MAX_CONCURRENT: the maximum number of parallel transfers the filter + * chain expects to handle at the same time. + * default: 1 if no filter overrides. + * - CONNECT_REPLY_MS: milliseconds until the first indication of a server + * response was received on a connect. For TCP, this + * reflects the time until the socket connected. On UDP + * this gives the time the first bytes from the server + * were received. + * -1 if not determined yet. + * - CF_QUERY_SOCKET: the socket used by the filter chain + */ +/* query res1 res2 */ +#define CF_QUERY_MAX_CONCURRENT 1 /* number - */ +#define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */ +#define CF_QUERY_SOCKET 3 /* - curl_socket_t */ + +/** + * Query the cfilter for properties. Filters ignorant of a query will + * pass it "down" the filter chain. + */ +typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2); + +/** + * Type flags for connection filters. A filter can have none, one or + * many of those. Use to evaluate state/capabilities of a filter chain. + * + * CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like + * a CONNECT tunnel, a UNIX domain socket, a QUIC + * connection, etc. + * CF_TYPE_SSL: provide SSL/TLS + * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles + */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) +#define CF_TYPE_MULTIPLEX (1 << 2) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { const char *name; /* name of the filter type */ - long flags; /* flags of filter type */ + int flags; /* flags of filter type */ + int log_level; /* log level for such filters */ Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ - Curl_cft_setup *setup; /* setup for a connection */ Curl_cft_connect *connect; /* establish connection */ Curl_cft_close *close; /* close conn */ Curl_cft_get_host *get_host; /* host filter talks to */ @@ -119,8 +174,10 @@ struct Curl_cftype { Curl_cft_data_pending *has_data_pending;/* conn has data pending */ Curl_cft_send *do_send; /* send data */ Curl_cft_recv *do_recv; /* receive data */ - Curl_cft_attach_data *attach_data; /* data is being handled here */ - Curl_cft_detach_data *detach_data; /* data is no longer handled here */ + Curl_cft_cntrl *cntrl; /* events/control */ + Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */ + Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */ + Curl_cft_query *query; /* query filter chain */ }; /* A connection filter instance, e.g. registered at a connection */ @@ -129,7 +186,7 @@ struct Curl_cfilter { struct Curl_cfilter *next; /* next filter in chain */ void *ctx; /* filter type specific settings */ struct connectdata *conn; /* the connection this filter belongs to */ - int sockindex; /* TODO: like to get rid off this */ + int sockindex; /* the index the filter is installed at */ BIT(connected); /* != 0 iff this filter is connected */ }; @@ -139,9 +196,6 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, /* Default implementations for the type functions, implementing pass-through * the filter chain. */ -CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost); void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -158,16 +212,22 @@ ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err); ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err); -void Curl_cf_def_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); -void Curl_cf_def_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); +CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2); +bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data); +CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data); +CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2); /** * Create a new filter instance, unattached to the filter chain. * Use Curl_conn_cf_add() to add it to the chain. * @param pcf on success holds the created instance - * @parm cft the filter type + * @param cft the filter type * @param ctx the type specific context to use */ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, @@ -176,7 +236,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, /** * Add a filter instance to the `sockindex` filter chain at connection - * `data->conn`. The filter must not already be attached. It is inserted at + * `conn`. The filter must not already be attached. It is inserted at * the start of the chain (top). */ void Curl_conn_cf_add(struct Curl_easy *data, @@ -185,11 +245,11 @@ void Curl_conn_cf_add(struct Curl_easy *data, struct Curl_cfilter *cf); /** - * Remove and destroy all filters at chain `sockindex` on connection `conn`. + * Insert a filter (chain) after `cf_at`. + * `cf_new` must not already be attached. */ -void Curl_conn_cf_discard_all(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); +void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, + struct Curl_cfilter *cf_new); /** * Discard, e.g. remove and destroy a specific filter instance. @@ -198,29 +258,51 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, */ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data); +/** + * Discard all cfilters starting with `*pcf` and clearing it afterwards. + */ +void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, + struct Curl_easy *data); + +/** + * Remove and destroy all filters at chain `sockindex` on connection `conn`. + */ +void Curl_conn_cf_discard_all(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done); +void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); +int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks); +bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data); ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err); ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err); +CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool ignore_result, + int event, int arg1, void *arg2); + +/** + * Get the socket used by the filter chain starting at `cf`. + * Returns CURL_SOCKET_BAD if not available. + */ +curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, + struct Curl_easy *data); + #define CURL_CF_SSL_DEFAULT -1 #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 /** - * Setup the filter chain at `sockindex` in connection `conn`, invoking - * the instance `setup(remotehost)` methods. If no filter chain is - * installed yet, inspects the configuration in `data` to install a - * suitable filter chain. - */ -CURLcode Curl_conn_setup(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - const struct Curl_dns_entry *remotehost, - int ssl_mode); - -/** * Bring the filter chain at `sockindex` for connection `data->conn` into * connected state. Which will set `*done` to TRUE. * This can be called on an already connected chain with no side effects. @@ -248,7 +330,12 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); * (or will be once connected). This will return FALSE, if SSL * is only used in proxying and not for the tunnel itself. */ -bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex); +bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); + +/** + * Connection provides multiplexing of easy handles at `socketindex`. + */ +bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); /** * Close the filter chain at `sockindex` for connection `data->conn`. @@ -264,6 +351,12 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex); /** + * Return the socket used on data's connection for the index. + * Returns CURL_SOCKET_BAD if not available. + */ +curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex); + +/** * Get any select fd flags and the socket filters at chain `sockindex` * at connection `conn` might be waiting for. */ @@ -289,13 +382,12 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex, const void *buf, size_t len, CURLcode *code); /** - * The easy handle `data` is being attached (served) by connection `conn`. - * All filters are informed to adapt to handling `data`. - * Note: there may be several `data` attached to a connection at the same - * time. + * The easy handle `data` is being attached to `conn`. This does + * not mean that data will actually do a transfer. Attachment is + * also used for temporary actions on the connection. */ -void Curl_conn_attach_data(struct connectdata *conn, - struct Curl_easy *data); +void Curl_conn_ev_data_attach(struct connectdata *conn, + struct Curl_easy *data); /** * The easy handle `data` is being detached (no longer served) @@ -304,12 +396,141 @@ void Curl_conn_attach_data(struct connectdata *conn, * Note: there may be several `data` attached to a connection at the same * time. */ -void Curl_conn_detach_data(struct connectdata *conn, - struct Curl_easy *data); +void Curl_conn_ev_data_detach(struct connectdata *conn, + struct Curl_easy *data); + +/** + * Notify connection filters that they need to setup data for + * a transfer. + */ +CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data); + +/** + * Notify connection filters that now would be a good time to + * perform any idle, e.g. time related, actions. + */ +CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data); + +/** + * Notify connection filters that the transfer represented by `data` + * is donw with sending data (e.g. has uploaded everything). + */ +void Curl_conn_ev_data_done_send(struct Curl_easy *data); + +/** + * Notify connection filters that the transfer represented by `data` + * is finished - eventually premature, e.g. before being complete. + */ +void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature); + +/** + * Notify connection filters that the transfer of data is paused/unpaused. + */ +CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause); + +/** + * Inform connection filters to update their info in `conn`. + */ +void Curl_conn_ev_update_info(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Inform connection filters to report statistics. + */ +void Curl_conn_ev_report_stats(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Check if FIRSTSOCKET's cfilter chain deems connection alive. + */ +bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn); + +/** + * Try to upkeep the connection filters at sockindex. + */ +CURLcode Curl_conn_keep_alive(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); void Curl_conn_get_host(struct Curl_easy *data, int sockindex, const char **phost, const char **pdisplay_host, int *pport); +/** + * Get the maximum number of parallel transfers the connection + * expects to be able to handle at `sockindex`. + */ +size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + + +/** + * Types and macros used to keep the current easy handle in filter calls, + * allowing for nested invocations. See #10336. + * + * `cf_call_data` is intended to be a member of the cfilter's `ctx` type. + * A filter defines the macro `CF_CTX_CALL_DATA` to give access to that. + * + * With all values 0, the default, this indicates that there is no cfilter + * call with `data` ongoing. + * Macro `CF_DATA_SAVE` preserves the current `cf_call_data` in a local + * variable and sets the `data` given, incrementing the `depth` counter. + * + * Macro `CF_DATA_RESTORE` restores the old values from the local variable, + * while checking that `depth` values are as expected (debug build), catching + * cases where a "lower" RESTORE was not called. + * + * Finally, macro `CF_DATA_CURRENT` gives the easy handle of the current + * invocation. + */ +struct cf_call_data { + struct Curl_easy *data; +#ifdef DEBUGBUILD + int depth; +#endif +}; + +/** + * define to access the `struct cf_call_data for a cfilter. Normally + * a member in the cfilter's `ctx`. + * + * #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance +*/ + +#ifdef DEBUGBUILD + +#define CF_DATA_SAVE(save, cf, data) \ + do { \ + (save) = CF_CTX_CALL_DATA(cf); \ + DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + CF_CTX_CALL_DATA(cf).depth++; \ + CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \ + } while(0) + +#define CF_DATA_RESTORE(cf, save) \ + do { \ + DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \ + DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + CF_CTX_CALL_DATA(cf) = (save); \ + } while(0) + +#else /* DEBUGBUILD */ + +#define CF_DATA_SAVE(save, cf, data) \ + do { \ + (save) = CF_CTX_CALL_DATA(cf); \ + CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \ + } while(0) + +#define CF_DATA_RESTORE(cf, save) \ + do { \ + CF_CTX_CALL_DATA(cf) = (save); \ + } while(0) + +#endif /* !DEBUGBUILD */ + +#define CF_DATA_CURRENT(cf) \ + ((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL) #endif /* HEADER_CURL_CFILTERS_H */ diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index a557ac6..c21b96c 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se> + * 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/conncache.h b/Utilities/cmcurl/lib/conncache.h index 94664bc..959767d 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, <linus@haxx.se> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se> * * 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/connect.c b/Utilities/cmcurl/lib/connect.c index af04138..993a7f9 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -59,106 +59,30 @@ #include "strerror.h" #include "cfilters.h" #include "connect.h" +#include "cf-http.h" +#include "cf-socket.h" #include "select.h" #include "url.h" /* for Curl_safefree() */ #include "multiif.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "inet_ntop.h" #include "inet_pton.h" -#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */ +#include "vtls/vtls.h" /* for vtsl cfilters */ #include "progress.h" #include "warnless.h" #include "conncache.h" #include "multihandle.h" #include "share.h" #include "version_win32.h" -#include "quic.h" +#include "vquic/vquic.h" /* for quic cfilters */ +#include "http_proxy.h" +#include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -static bool verifyconnect(curl_socket_t sockfd, int *error); - -#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H) -/* DragonFlyBSD and Windows use millisecond units */ -#define KEEPALIVE_FACTOR(x) (x *= 1000) -#else -#define KEEPALIVE_FACTOR(x) -#endif - -#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS) -#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) - -struct tcp_keepalive { - u_long onoff; - u_long keepalivetime; - u_long keepaliveinterval; -}; -#endif - -static void -tcpkeepalive(struct Curl_easy *data, - curl_socket_t sockfd) -{ - int optval = data->set.tcp_keepalive?1:0; - - /* only set IDLE and INTVL if setting KEEPALIVE is successful */ - if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd); - } - else { -#if defined(SIO_KEEPALIVE_VALS) - struct tcp_keepalive vals; - DWORD dummy; - vals.onoff = 1; - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - vals.keepalivetime = optval; - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - vals.keepaliveinterval = optval; - if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), - NULL, 0, &dummy, NULL, NULL) != 0) { - infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d", - (int)sockfd, WSAGetLastError()); - } -#else -#ifdef TCP_KEEPIDLE - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd); - } -#elif defined(TCP_KEEPALIVE) - /* Mac OS X style */ - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); - } -#endif -#ifdef TCP_KEEPINTVL - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd); - } -#endif -#endif - } -} - -static CURLcode -singleipconnect(struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, /* start connecting to this */ - int tempindex); /* 0 or 1 among the temp ones */ /* * Curl_timeleft() returns the amount of milliseconds left allowed for the @@ -230,387 +154,6 @@ timediff_t Curl_timeleft(struct Curl_easy *data, return timeout_ms; } -static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd, int af, unsigned int scope) -{ - struct Curl_sockaddr_storage sa; - 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 - struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; -#endif - - struct Curl_dns_entry *h = NULL; - unsigned short port = data->set.localport; /* use this port number, 0 for - "random" */ - /* how many port numbers to try to bind to, increasing one at a time */ - int portnum = data->set.localportrange; - const char *dev = data->set.str[STRING_DEVICE]; - int error; -#ifdef IP_BIND_ADDRESS_NO_PORT - int on = 1; -#endif -#ifndef ENABLE_IPV6 - (void)scope; -#endif - - /************************************************************* - * Select device to bind socket to - *************************************************************/ - if(!dev && !port) - /* no local kind of binding was requested */ - return CURLE_OK; - - memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); - - if(dev && (strlen(dev)<255) ) { - char myhost[256] = ""; - int done = 0; /* -1 for error, 1 for address found */ - bool is_interface = FALSE; - bool is_host = FALSE; - static const char *if_prefix = "if!"; - static const char *host_prefix = "host!"; - - if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { - dev += strlen(if_prefix); - is_interface = TRUE; - } - else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { - dev += strlen(host_prefix); - is_host = TRUE; - } - - /* interface */ - if(!is_host) { -#ifdef SO_BINDTODEVICE - /* I am not sure any other OSs than Linux that provide this feature, - * and at the least I cannot test. --Ben - * - * This feature allows one to tightly bind the local socket to a - * particular interface. This will force even requests to other - * local interfaces to go out the external interface. - * - * - * Only bind to the interface when specified as interface, not just - * as a hostname or ip address. - * - * interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try - * to use it straight away. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - dev, (curl_socklen_t)strlen(dev) + 1) == 0) { - /* This is typically "errno 1, error: Operation not permitted" if - * you're not running as root or another suitable privileged - * user. - * If it succeeds it means the parameter was a valid interface and - * not an IP address. Return immediately. - */ - return CURLE_OK; - } -#endif - - switch(Curl_if2ip(af, -#ifdef ENABLE_IPV6 - scope, conn->scope_id, -#endif - dev, myhost, sizeof(myhost))) { - case IF2IP_NOT_FOUND: - if(is_interface) { - /* Do not fall back to treating it as a host name */ - failf(data, "Couldn't bind to interface '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - break; - case IF2IP_AF_NOT_SUPPORTED: - /* Signal the caller to try another address family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - case IF2IP_FOUND: - is_interface = TRUE; - /* - * We now have the numerical IP address in the 'myhost' buffer - */ - infof(data, "Local Interface %s is ip %s using address family %i", - dev, myhost, af); - done = 1; - break; - } - } - if(!is_interface) { - /* - * This was not an interface, resolve the name as a host name - * or IP number - * - * Temporarily force name resolution to use only the address type - * of the connection. The resolve functions should really be changed - * to take a type parameter instead. - */ - unsigned char ipver = conn->ip_version; - int rc; - - if(af == AF_INET) - conn->ip_version = CURL_IPRESOLVE_V4; -#ifdef ENABLE_IPV6 - else if(af == AF_INET6) - conn->ip_version = CURL_IPRESOLVE_V6; -#endif - - rc = Curl_resolv(data, dev, 0, FALSE, &h); - if(rc == CURLRESOLV_PENDING) - (void)Curl_resolver_wait_resolv(data, &h); - conn->ip_version = ipver; - - if(h) { - /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ - Curl_printable_address(h->addr, myhost, sizeof(myhost)); - infof(data, "Name '%s' family %i resolved to '%s' family %i", - dev, af, myhost, h->addr->ai_family); - Curl_resolv_unlock(data, h); - if(af != h->addr->ai_family) { - /* bad IP version combo, signal the caller to try another address - family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - } - done = 1; - } - else { - /* - * provided dev was no interface (or interfaces are not supported - * e.g. solaris) no ip address and no domain we fail here - */ - done = -1; - } - } - - if(done > 0) { -#ifdef ENABLE_IPV6 - /* IPv6 address */ - if(af == AF_INET6) { -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - char *scope_ptr = strchr(myhost, '%'); - if(scope_ptr) - *(scope_ptr++) = '\0'; -#endif - if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - if(scope_ptr) { - /* The "myhost" string either comes from Curl_if2ip or from - Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if - present, is known to be numeric */ - unsigned long scope_id = strtoul(scope_ptr, NULL, 10); - if(scope_id > UINT_MAX) - return CURLE_UNSUPPORTED_PROTOCOL; - - si6->sin6_scope_id = (unsigned int)scope_id; - } -#endif - } - sizeof_sa = sizeof(struct sockaddr_in6); - } - else -#endif - /* IPv4 address */ - if((af == AF_INET) && - (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { - si4->sin_family = AF_INET; - si4->sin_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in); - } - } - - if(done < 1) { - /* errorbuf is set false so failf will overwrite any message already in - the error buffer, so the user receives this error message instead of a - generic resolve error. */ - data->state.errorbuf = FALSE; - failf(data, "Couldn't bind to '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - } - else { - /* no device was given, prepare sa to match af's needs */ -#ifdef ENABLE_IPV6 - if(af == AF_INET6) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in6); - } - else -#endif - if(af == AF_INET) { - si4->sin_family = AF_INET; - si4->sin_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in); - } - } -#ifdef IP_BIND_ADDRESS_NO_PORT - (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); -#endif - 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; - } - - if(--portnum > 0) { - port++; /* try next port */ - if(port == 0) - break; - infof(data, "Bind to local port %hu failed, trying next", port - 1); - /* We re-use/clobber the port variable here below */ - if(sock->sa_family == AF_INET) - si4->sin_port = ntohs(port); -#ifdef ENABLE_IPV6 - else - si6->sin6_port = ntohs(port); -#endif - } - else - break; - } - { - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "bind failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - } - - return CURLE_INTERFACE_FAILED; -} - -/* - * verifyconnect() returns TRUE if the connect really has happened. - */ -static bool verifyconnect(curl_socket_t sockfd, int *error) -{ - bool rc = TRUE; -#ifdef SO_ERROR - int err = 0; - curl_socklen_t errSize = sizeof(err); - -#ifdef WIN32 - /* - * In October 2003 we effectively nullified this function on Windows due to - * problems with it using all CPU in multi-threaded cases. - * - * In May 2004, we bring it back to offer more info back on connect failures. - * Gisle Vanem could reproduce the former problems with this function, but - * could avoid them by adding this SleepEx() call below: - * - * "I don't have Rational Quantify, but the hint from his post was - * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe - * just Sleep(0) would be enough?) would release whatever - * mutex/critical-section the ntdll call is waiting on. - * - * Someone got to verify this on Win-NT 4.0, 2000." - */ - -#ifdef _WIN32_WCE - Sleep(0); -#else - SleepEx(0, FALSE); -#endif - -#endif - - if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) - err = SOCKERRNO; -#ifdef _WIN32_WCE - /* Old WinCE versions don't support SO_ERROR */ - if(WSAENOPROTOOPT == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif -#if defined(EBADIOCTL) && defined(__minix) - /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ - if(EBADIOCTL == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif - if((0 == err) || (EISCONN == err)) - /* we are connected, awesome! */ - rc = TRUE; - else - /* This wasn't a successful connect */ - rc = FALSE; - if(error) - *error = err; -#else - (void)sockfd; - if(error) - *error = SOCKERRNO; -#endif - return rc; -} - -/* update tempaddr[tempindex] (to the next entry), makes sure to stick - to the correct family */ -static struct Curl_addrinfo *ainext(struct connectdata *conn, - int tempindex, - bool next) /* use next entry? */ -{ - struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; - if(ai && next) - ai = ai->ai_next; - while(ai && (ai->ai_family != conn->tempfamily[tempindex])) - ai = ai->ai_next; - conn->tempaddr[tempindex] = ai; - return ai; -} - -/* Used within the multi interface. Try next IP address, returns error if no - more address exists or error */ -static CURLcode trynextip(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - int tempindex) -{ - CURLcode result = CURLE_COULDNT_CONNECT; - - /* First clean up after the failed socket. - Don't close it yet to ensure that the next IP's socket gets a different - file descriptor, which can prevent bugs when the curl_multi_socket_action - interface is used with certain select() replacements such as kqueue. */ - curl_socket_t fd_to_close = conn->tempsock[tempindex]; - conn->tempsock[tempindex] = CURL_SOCKET_BAD; - - if(sockindex == FIRSTSOCKET) { - struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; - - while(ai) { - result = singleipconnect(data, conn, ai, tempindex); - if(result == CURLE_COULDNT_CONNECT) { - ai = ainext(conn, tempindex, TRUE); - continue; - } - break; - } - } - - if(fd_to_close != CURL_SOCKET_BAD) - Curl_closesocket(data, conn, fd_to_close); - - return result; -} - /* 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, @@ -629,6 +172,28 @@ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, data->info.conn_local_port = local_port; } +static const struct Curl_addrinfo * +addr_first_match(const struct Curl_addrinfo *addr, int family) +{ + while(addr) { + if(addr->ai_family == family) + return addr; + addr = addr->ai_next; + } + return NULL; +} + +static const struct Curl_addrinfo * +addr_next_match(const struct Curl_addrinfo *addr, int family) +{ + while(addr && addr->ai_next) { + addr = addr->ai_next; + if(addr->ai_family == family) + return addr; + } + return NULL; +} + /* retrieves ip address and port from a sockaddr structure. note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, @@ -686,623 +251,493 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, return FALSE; } -/* retrieves the start/end point information of a socket of an established - connection */ -void Curl_conninfo_remote(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t sockfd) -{ -#ifdef HAVE_GETPEERNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - curl_socklen_t plen; - int port; - plen = sizeof(struct Curl_sockaddr_storage); - memset(&ssrem, 0, sizeof(ssrem)); - if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - conn->primary_ip, &port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } -#else - (void)data; - (void)conn; - (void)sockfd; -#endif -} +struct connfind { + long id_tofind; + struct connectdata *found; +}; -/* retrieves the start/end point information of a socket of an established - connection */ -void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd, - char *local_ip, int *local_port) +static int conn_is_conn(struct Curl_easy *data, + struct connectdata *conn, void *param) { -#ifdef HAVE_GETSOCKNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssloc; - curl_socklen_t slen; - slen = sizeof(struct Curl_sockaddr_storage); - memset(&ssloc, 0, sizeof(ssloc)); - if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) { - int error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, - local_ip, local_port)) { - failf(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } -#else + struct connfind *f = (struct connfind *)param; (void)data; - (void)sockfd; - (void)local_ip; - (void)local_port; -#endif -} - -/* retrieves the start/end point information of a socket of an established - connection */ -void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd) -{ - /* 'local_ip' and 'local_port' get filled with local's numerical - ip address and port number whenever an outgoing connection is - **established** from the primary socket to a remote address. */ - char local_ip[MAX_IPADR_LEN] = ""; - int local_port = -1; - - if(!conn->bits.reuse && - (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen)) - Curl_conninfo_remote(data, conn, sockfd); - Curl_conninfo_local(data, sockfd, local_ip, &local_port); - - /* persist connection info in session handle */ - Curl_persistconninfo(data, conn, local_ip, local_port); -} - -/* - * post_connect() is called after a successful connect to the peer - */ -static void post_connect(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - Curl_updateconninfo(data, conn, conn->sock[sockindex]); - Curl_verboseconnect(data, conn); - data->info.numconnects++; /* to track the number of connections made */ + if(conn->connection_id == f->id_tofind) { + f->found = conn; + return 1; + } + return 0; } /* - * is_connected() checks if the socket has connected. + * Used to extract socket and connectdata struct for the most recent + * transfer on the given Curl_easy. + * + * The returned socket will be CURL_SOCKET_BAD in case of failure! */ -static CURLcode is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected) +curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, + struct connectdata **connp) { - CURLcode result = CURLE_OK; - timediff_t allow; - int error = 0; - struct curltime now; - int rc = 0; - int i; - - DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); - - *connected = FALSE; /* a very negative world view is best */ - - now = Curl_now(); - - /* Check if any of the conn->tempsock we use for establishing connections - * succeeded and, if so, close any ongoing other ones. - * Transfer the successful conn->tempsock to conn->sock[sockindex] - * and set conn->tempsock to CURL_SOCKET_BAD. - * If transport is QUIC, we need to shutdown the ongoing 'other' - * connect attempts in a QUIC appropriate way. */ - for(i = 0; i<2; i++) { - const int other = i ^ 1; - if(conn->tempsock[i] == CURL_SOCKET_BAD) - continue; - error = 0; -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - result = Curl_quic_is_connected(data, conn, i, connected); - if(!result && *connected) { - /* use this socket from now on */ - conn->sock[sockindex] = conn->tempsock[i]; - conn->ip_addr = conn->tempaddr[i]; - conn->tempsock[i] = CURL_SOCKET_BAD; - post_connect(data, conn, sockindex); - connkeep(conn, "HTTP/3 default"); - if(conn->tempsock[other] != CURL_SOCKET_BAD) - Curl_quic_disconnect(data, conn, other); - return CURLE_OK; - } - /* When a QUIC connect attempt fails, the better error explanation is in - 'result' and not in errno */ - if(result) { - conn->tempsock[i] = CURL_SOCKET_BAD; - error = SOCKERRNO; - } - } - else -#endif - { -#ifdef mpeix - /* Call this function once now, and ignore the results. We do this to - "clear" the error state on the socket so that we can later read it - reliably. This is reported necessary on the MPE/iX operating - system. */ - (void)verifyconnect(conn->tempsock[i], NULL); -#endif + DEBUGASSERT(data); - /* check socket for connect */ - rc = SOCKET_WRITABLE(conn->tempsock[i], 0); - } + /* this works for an easy handle: + * - that has been used for curl_easy_perform() + * - that is associated with a multi handle, and whose connection + * was detached with CURLOPT_CONNECT_ONLY + */ + if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { + struct connectdata *c; + struct connfind find; + find.id_tofind = data->state.lastconnect_id; + find.found = NULL; - if(rc == 0) { /* no connection yet */ - if(Curl_timediff(now, conn->connecttime) >= - conn->timeoutms_per_addr[i]) { - infof(data, "After %" CURL_FORMAT_TIMEDIFF_T - "ms connect time, move on!", conn->timeoutms_per_addr[i]); - error = ETIMEDOUT; - } + Curl_conncache_foreach(data, + data->share && (data->share->specifier + & (1<< CURL_LOCK_DATA_CONNECT))? + &data->share->conn_cache: + data->multi_easy? + &data->multi_easy->conn_cache: + &data->multi->conn_cache, &find, conn_is_conn); - /* should we try another protocol family? */ - if(i == 0 && !conn->bits.parallel_connect && - (Curl_timediff(now, conn->connecttime) >= - data->set.happy_eyeballs_timeout)) { - conn->bits.parallel_connect = TRUE; /* starting now */ - trynextip(data, conn, sockindex, 1); - } + if(!find.found) { + data->state.lastconnect_id = -1; + return CURL_SOCKET_BAD; } - else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) { - if(verifyconnect(conn->tempsock[i], &error)) { - /* we are connected with TCP, awesome! */ - - /* use this socket from now on */ - conn->sock[sockindex] = conn->tempsock[i]; - conn->ip_addr = conn->tempaddr[i]; - conn->tempsock[i] = CURL_SOCKET_BAD; -#ifdef ENABLE_IPV6 - conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE; -#endif - /* close the other socket, if open */ - if(conn->tempsock[other] != CURL_SOCKET_BAD) { - Curl_closesocket(data, conn, conn->tempsock[other]); - conn->tempsock[other] = CURL_SOCKET_BAD; - } - - *connected = TRUE; - return CURLE_OK; - } - } - else if(rc & CURL_CSELECT_ERR) { - (void)verifyconnect(conn->tempsock[i], &error); - } + c = find.found; + if(connp) + /* only store this if the caller cares for it */ + *connp = c; + return c->sock[FIRSTSOCKET]; + } + return CURL_SOCKET_BAD; +} - /* - * The connection failed here, we should attempt to connect to the "next - * address" for the given host. But first remember the latest error. - */ - if(error) { - data->state.os_errno = error; - SET_SOCKERRNO(error); - if(conn->tempaddr[i]) { - CURLcode status; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char ipaddress[MAX_IPADR_LEN]; - char buffer[STRERROR_LEN]; - Curl_printable_address(conn->tempaddr[i], ipaddress, - sizeof(ipaddress)); -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - infof(data, "connect to %s port %u failed: %s", - ipaddress, conn->port, curl_easy_strerror(result)); - } - else +/* + * Curl_conncontrol() marks streams or connection for closure. + */ +void Curl_conncontrol(struct connectdata *conn, + int ctrl /* see defines in header */ +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + , const char *reason #endif - infof(data, "connect to %s port %u failed: %s", - ipaddress, conn->port, - Curl_strerror(error, buffer, sizeof(buffer))); + ) +{ + /* close if a connection, or a stream that isn't multiplexed. */ + /* This function will be called both before and after this connection is + associated with a transfer. */ + bool closeit, is_multiplex; + DEBUGASSERT(conn); +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + (void)reason; /* useful for debugging */ #endif - - allow = Curl_timeleft(data, &now, TRUE); - conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ? - allow : allow / 2; - ainext(conn, i, TRUE); - status = trynextip(data, conn, sockindex, i); - if((status != CURLE_COULDNT_CONNECT) || - conn->tempsock[other] == CURL_SOCKET_BAD) { - /* the last attempt failed and no other sockets remain open */ - if(!result) - result = status; - } - } - } - } - - /* - * Now that we've checked whether we are connected, check whether we've - * already timed out. - * - * First figure out how long time we have left to connect */ - - allow = Curl_timeleft(data, &now, TRUE); - - if(allow < 0) { - /* time-out, bail out, go home */ - failf(data, "Connection timeout after %ld ms", - Curl_timediff(now, data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; + is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); + closeit = (ctrl == CONNCTRL_CONNECTION) || + ((ctrl == CONNCTRL_STREAM) && !is_multiplex); + if((ctrl == CONNCTRL_STREAM) && is_multiplex) + ; /* stream signal on multiplex conn never affects close state */ + else if((bit)closeit != conn->bits.close) { + conn->bits.close = closeit; /* the only place in the source code that + should assign this bit */ } +} - if(result && - (conn->tempsock[0] == CURL_SOCKET_BAD) && - (conn->tempsock[1] == CURL_SOCKET_BAD)) { - /* no more addresses to try */ - const char *hostname; - CURLcode failreason = result; - - /* if the first address family runs out of addresses to try before the - happy eyeball timeout, go ahead and try the next family now */ - result = trynextip(data, conn, sockindex, 1); - if(!result) - return result; +/** + * job walking the matching addr infos, creating a sub-cfilter with the + * provided method `cf_create` and running setup/connect on it. + */ +struct eyeballer { + const char *name; + const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ + int ai_family; /* matching address family only */ + cf_ip_connect_create *cf_create; /* for creating cf */ + struct Curl_cfilter *cf; /* current sub-cfilter connecting */ + struct eyeballer *primary; /* eyeballer this one is backup for */ + timediff_t delay_ms; /* delay until start */ + struct curltime started; /* start of current attempt */ + timediff_t timeoutms; /* timeout for current attempt */ + expire_id timeout_id; /* ID for Curl_expire() */ + CURLcode result; + int error; + BIT(has_started); /* attempts have started */ + BIT(is_done); /* out of addresses/time */ + BIT(connected); /* cf has connected */ +}; - result = failreason; -#ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) - hostname = conn->socks_proxy.host.name; - else if(conn->bits.httpproxy) - hostname = conn->http_proxy.host.name; - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; +typedef enum { + SCFST_INIT, + SCFST_WAITING, + SCFST_DONE +} cf_connect_state; - failf(data, "Failed to connect to %s port %u after " - "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", - hostname, conn->port, - Curl_timediff(now, data->progress.t_startsingle), - curl_easy_strerror(result)); +struct cf_he_ctx { + int transport; + cf_ip_connect_create *cf_create; + const struct Curl_dns_entry *remotehost; + cf_connect_state state; + struct eyeballer *baller[2]; + struct eyeballer *winner; + struct curltime started; +}; - Curl_quic_disconnect(data, conn, 0); - Curl_quic_disconnect(data, conn, 1); +static CURLcode eyeballer_new(struct eyeballer **pballer, + cf_ip_connect_create *cf_create, + const struct Curl_addrinfo *addr, + int ai_family, + struct eyeballer *primary, + timediff_t delay_ms, + timediff_t timeout_ms, + expire_id timeout_id) +{ + struct eyeballer *baller; -#ifdef WSAETIMEDOUT - if(WSAETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; -#elif defined(ETIMEDOUT) - if(ETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; -#endif - } - else - result = CURLE_OK; /* still trying */ + *pballer = NULL; + baller = calloc(1, sizeof(*baller) + 1000); + if(!baller) + return CURLE_OUT_OF_MEMORY; - return result; + baller->name = ((ai_family == AF_INET)? "ipv4" : ( +#ifdef ENABLE_IPV6 + (ai_family == AF_INET6)? "ipv6" : +#endif + "ip")); + baller->cf_create = cf_create; + baller->addr = addr; + baller->ai_family = ai_family; + baller->primary = primary; + baller->delay_ms = delay_ms; + baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)? + timeout_ms / 2 : timeout_ms; + baller->timeout_id = timeout_id; + baller->result = CURLE_COULDNT_CONNECT; + + *pballer = baller; + return CURLE_OK; } -static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) +static void baller_close(struct eyeballer *baller, + struct Curl_easy *data) { -#if defined(TCP_NODELAY) - curl_socklen_t onoff = (curl_socklen_t) 1; - int level = IPPROTO_TCP; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; -#else - (void) data; -#endif - - if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, - sizeof(onoff)) < 0) - infof(data, "Could not set TCP_NODELAY: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#else - (void)data; - (void)sockfd; -#endif + if(baller && baller->cf) { + Curl_conn_cf_discard_chain(&baller->cf, data); + } } -#ifdef SO_NOSIGPIPE -/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when - sending data to a dead peer (instead of relying on the 4th argument to send - being MSG_NOSIGNAL). Possibly also existing and in use on other BSD - systems? */ -static void nosigpipe(struct Curl_easy *data, - curl_socket_t sockfd) +static void baller_free(struct eyeballer *baller, + struct Curl_easy *data) { - int onoff = 1; - if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, - sizeof(onoff)) < 0) { -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; - infof(data, "Could not set SO_NOSIGPIPE: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#endif + if(baller) { + baller_close(baller, data); + free(baller); } } -#else -#define nosigpipe(x,y) Curl_nop_stmt -#endif - -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://support.microsoft.com/kb/823764 - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - - The problem described in this knowledge-base is applied only to pre-Vista - Windows. Following function trying to detect OS version and skips - SO_SNDBUF adjustment for Windows Vista and above. -*/ -#define DETECT_OS_NONE 0 -#define DETECT_OS_PREVISTA 1 -#define DETECT_OS_VISTA_OR_LATER 2 - -void Curl_sndbufset(curl_socket_t sockfd) +static void baller_next_addr(struct eyeballer *baller) { - int val = CURL_MAX_WRITE_SIZE + 32; - int curval = 0; - int curlen = sizeof(curval); - - static int detectOsState = DETECT_OS_NONE; - - if(detectOsState == DETECT_OS_NONE) { - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - detectOsState = DETECT_OS_VISTA_OR_LATER; - else - detectOsState = DETECT_OS_PREVISTA; - } - - if(detectOsState == DETECT_OS_VISTA_OR_LATER) - return; - - if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) - if(curval > val) - return; - - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); + baller->addr = addr_next_match(baller->addr, baller->ai_family); } -#endif /* - * singleipconnect() + * Initiate a connect attempt walk. * * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to * CURL_SOCKET_BAD. Other errors will however return proper errors. - * - * singleipconnect() connects to the given IP only, and it may return without - * having connected. */ -static CURLcode singleipconnect(struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int tempindex) +static void baller_initiate(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller) { - struct Curl_sockaddr_ex addr; - int rc = -1; - int error = 0; - bool isconnected = FALSE; - curl_socket_t sockfd; + struct cf_he_ctx *ctx = cf->ctx; + struct Curl_cfilter *cf_prev = baller->cf; + struct Curl_cfilter *wcf; CURLcode result; - char ipaddress[MAX_IPADR_LEN]; - int port; - bool is_tcp; -#ifdef TCP_FASTOPEN_CONNECT - int optval = 1; -#endif - const char *ipmsg; - char buffer[STRERROR_LEN]; - curl_socket_t *sockp = &conn->tempsock[tempindex]; - *sockp = CURL_SOCKET_BAD; - result = Curl_socket(data, ai, &addr, &sockfd); + + /* Don't close a previous cfilter yet to ensure that the next IP's + socket gets a different file descriptor, which can prevent bugs when + the curl_multi_socket_action interface is used with certain select() + replacements such as kqueue. */ + result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr, + ctx->transport); if(result) - return result; + goto out; - /* store remote address and port used in this connection attempt */ - if(!Curl_addr2string(&addr.sa_addr, addr.addrlen, - ipaddress, &port)) { - /* malformed address or bug in inet_ntop, try next address */ - failf(data, "sa_addr inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, sockfd); - return CURLE_OK; + /* the new filter might have sub-filters */ + for(wcf = baller->cf; wcf; wcf = wcf->next) { + wcf->conn = cf->conn; + wcf->sockindex = cf->sockindex; } -#ifdef ENABLE_IPV6 - if(addr.family == AF_INET6) - ipmsg = " Trying [%s]:%d..."; - else -#endif - ipmsg = " Trying %s:%d..."; - infof(data, ipmsg, ipaddress, port); -#ifdef ENABLE_IPV6 - is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) && - addr.socktype == SOCK_STREAM; -#else - is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM; -#endif - if(is_tcp && data->set.tcp_nodelay) - tcpnodelay(data, sockfd); - - nosigpipe(data, sockfd); + if(addr_next_match(baller->addr, baller->ai_family)) { + Curl_expire(data, baller->timeoutms, baller->timeout_id); + } - Curl_sndbufset(sockfd); +out: + if(result) { + DEBUGF(LOG_CF(data, cf, "%s failed", baller->name)); + baller_close(baller, data); + } + if(cf_prev) + Curl_conn_cf_discard_chain(&cf_prev, data); + baller->result = result; +} - if(is_tcp && data->set.tcp_keepalive) - tcpkeepalive(data, sockfd); +/** + * Start a connection attempt on the current baller address. + * Will return CURLE_OK on the first address where a socket + * could be created and the non-blocking connect started. + * Returns error when all remaining addresses have been tried. + */ +static CURLcode baller_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller, + timediff_t timeoutms) +{ + baller->error = 0; + baller->connected = FALSE; + baller->has_started = TRUE; + + while(baller->addr) { + baller->started = Curl_now(); + baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ? + timeoutms / 2 : timeoutms; + baller_initiate(cf, data, baller); + if(!baller->result) + break; + baller_next_addr(baller); + } + if(!baller->addr) { + baller->is_done = TRUE; + } + return baller->result; +} - if(data->set.fsockopt) { - /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); - error = data->set.fsockopt(data->set.sockopt_client, - sockfd, - CURLSOCKTYPE_IPCXN); - Curl_set_in_callback(data, false); - if(error == CURL_SOCKOPT_ALREADY_CONNECTED) - isconnected = TRUE; - else if(error) { - Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */ - return CURLE_ABORTED_BY_CALLBACK; - } +/* Used within the multi interface. Try next IP address, returns error if no + more address exists or error */ +static CURLcode baller_start_next(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller, + timediff_t timeoutms) +{ + if(cf->sockindex == FIRSTSOCKET) { + baller_next_addr(baller); + baller_start(cf, data, baller, timeoutms); + } + else { + baller->error = 0; + baller->connected = FALSE; + baller->has_started = TRUE; + baller->is_done = TRUE; + baller->result = CURLE_COULDNT_CONNECT; } + return baller->result; +} - /* possibly bind the local end to an IP, interface or port */ - if(addr.family == AF_INET -#ifdef ENABLE_IPV6 - || addr.family == AF_INET6 +static CURLcode baller_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct eyeballer *baller, + struct curltime *now, + bool *connected) +{ + (void)cf; + *connected = baller->connected; + if(!baller->result && !*connected) { + /* evaluate again */ + baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected); + + if(!baller->result) { + if (*connected) { + baller->connected = TRUE; + baller->is_done = TRUE; + } + else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) { + infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T + "ms, move on!", baller->name, baller->timeoutms); +#if defined(ETIMEDOUT) + baller->error = ETIMEDOUT; #endif - ) { - result = bindlocal(data, conn, sockfd, addr.family, - Curl_ipv6_scope(&addr.sa_addr)); - if(result) { - Curl_closesocket(data, conn, sockfd); /* close socket and bail out */ - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - /* The address family is not supported on this interface. - We can continue trying addresses */ - return CURLE_COULDNT_CONNECT; + baller->result = CURLE_OPERATION_TIMEDOUT; } - return result; } } + return baller->result; +} - /* set socket non-blocking */ - (void)curlx_nonblock(sockfd, TRUE); +/* + * is_connected() checks if the socket has connected. + */ +static CURLcode is_connected(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *connected) +{ + struct cf_he_ctx *ctx = cf->ctx; + struct connectdata *conn = cf->conn; + CURLcode result; + struct curltime now; + size_t i; + int ongoing, not_started; + const char *hostname; - conn->connecttime = Curl_now(); - if(conn->num_addr > 1) { - Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME); - Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2); - } + /* Check if any of the conn->tempsock we use for establishing connections + * succeeded and, if so, close any ongoing other ones. + * Transfer the successful conn->tempsock to conn->sock[sockindex] + * and set conn->tempsock to CURL_SOCKET_BAD. + * If transport is QUIC, we need to shutdown the ongoing 'other' + * cot ballers in a QUIC appropriate way. */ +evaluate: + *connected = FALSE; /* a very negative world view is best */ + now = Curl_now(); + ongoing = not_started = 0; + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; - /* Connect TCP and QUIC sockets */ - if(!isconnected && (conn->transport != TRNSPRT_UDP)) { - if(conn->bits.tcp_fastopen) { -#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ -# if defined(HAVE_BUILTIN_AVAILABLE) - /* while connectx function is available since macOS 10.11 / iOS 9, - it did not have the interface declared correctly until - Xcode 9 / macOS SDK 10.13 */ - if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { - sa_endpoints_t endpoints; - endpoints.sae_srcif = 0; - endpoints.sae_srcaddr = NULL; - endpoints.sae_srcaddrlen = 0; - endpoints.sae_dstaddr = &addr.sa_addr; - endpoints.sae_dstaddrlen = addr.addrlen; - - rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, - CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, - NULL, 0, NULL, NULL); + if(!baller || baller->is_done) + continue; + + if(!baller->has_started) { + ++not_started; + continue; + } + baller->result = baller_connect(cf, data, baller, &now, connected); + DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d", + baller->name, baller->result, *connected)); + + if(!baller->result) { + if(*connected) { + /* connected, declare the winner */ + ctx->winner = baller; + ctx->baller[i] = NULL; + break; } - else { - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); + else { /* still waiting */ + ++ongoing; } -# else - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); -# endif /* HAVE_BUILTIN_AVAILABLE */ -#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ - if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, - (void *)&optval, sizeof(optval)) < 0) - infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd); - - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); -#elif defined(MSG_FASTOPEN) /* old Linux */ - if(conn->given->flags & PROTOPT_SSL) - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); - else - rc = 0; /* Do nothing */ -#endif } - else { - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); + else if(!baller->is_done) { + /* The baller failed to connect, start its next attempt */ + if(baller->error) { + data->state.os_errno = baller->error; + SET_SOCKERRNO(baller->error); + } + baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE)); + if(baller->is_done) { + DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + } + else { + /* next attempt was started */ + DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name)); + ++ongoing; + } } + } - if(-1 == rc) - error = SOCKERRNO; -#ifdef ENABLE_QUIC - else if(conn->transport == TRNSPRT_QUIC) { - /* pass in 'sockfd' separately since it hasn't been put into the - tempsock array at this point */ - result = Curl_quic_connect(data, conn, sockfd, tempindex, - &addr.sa_addr, addr.addrlen); - if(result) - error = SOCKERRNO; + if(ctx->winner) { + *connected = TRUE; + return CURLE_OK; + } + + /* Nothing connected, check the time before we might + * start new ballers or return ok. */ + if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) { + failf(data, "Connection timeout after %ld ms", + Curl_timediff(now, data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + + /* Check if we have any waiting ballers to start now. */ + if(not_started > 0) { + int added = 0; + + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; + + if(!baller || baller->has_started) + continue; + /* We start its primary baller has failed to connect or if + * its start delay_ms have expired */ + if((baller->primary && baller->primary->is_done) || + Curl_timediff(now, ctx->started) >= baller->delay_ms) { + baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE)); + if(baller->is_done) { + DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + } + else { + DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%ldms)", + baller->name, baller->timeoutms)); + ++ongoing; + ++added; + } + } } -#endif + if(added > 0) + goto evaluate; } - else { - *sockp = sockfd; + + if(ongoing > 0) { + /* We are still trying, return for more waiting */ + *connected = FALSE; return CURLE_OK; } - if(-1 == rc) { - switch(error) { - case EINPROGRESS: - case EWOULDBLOCK: -#if defined(EAGAIN) -#if (EAGAIN) != (EWOULDBLOCK) - /* On some platforms EAGAIN and EWOULDBLOCK are the - * same value, and on others they are different, hence - * the odd #if - */ - case EAGAIN: -#endif -#endif - result = CURLE_OK; + /* all ballers have failed to connect. */ + DEBUGF(LOG_CF(data, cf, "all eyeballers failed")); + result = CURLE_COULDNT_CONNECT; + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; + DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d", + baller?baller->name:NULL, + baller?baller->has_started:0, + baller?baller->result:0)); + if(baller && baller->has_started && baller->result) { + result = baller->result; break; - - default: - /* unknown error, fallthrough and try another address! */ - infof(data, "Immediate connect fail for %s: %s", - ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); - data->state.os_errno = error; - - /* connect failed */ - Curl_closesocket(data, conn, sockfd); - result = CURLE_COULDNT_CONNECT; } } - if(!result) - *sockp = sockfd; +#ifndef CURL_DISABLE_PROXY + if(conn->bits.socksproxy) + hostname = conn->socks_proxy.host.name; + else if(conn->bits.httpproxy) + hostname = conn->http_proxy.host.name; + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + + failf(data, "Failed to connect to %s port %u after " + "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", + hostname, conn->port, + Curl_timediff(now, data->progress.t_startsingle), + curl_easy_strerror(result)); + +#ifdef WSAETIMEDOUT + if(WSAETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#elif defined(ETIMEDOUT) + if(ETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; +#endif return result; } /* - * TCP connect to the given host with timeout, proxy or remote doesn't matter. - * There might be more than one IP address to try out. Fill in the passed - * pointer with the connected socket. + * Connect to the given host with timeout, proxy or remote doesn't matter. + * There might be more than one IP address to try out. */ - -CURLcode Curl_connecthost(struct Curl_easy *data, - struct connectdata *conn, /* context */ - const struct Curl_dns_entry *remotehost) +static CURLcode start_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost) { + struct cf_he_ctx *ctx = cf->ctx; + struct connectdata *conn = cf->conn; CURLcode result = CURLE_COULDNT_CONNECT; - int i; + int ai_family0, ai_family1; timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + const struct Curl_addrinfo *addr0, *addr1; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ @@ -1310,58 +745,76 @@ CURLcode Curl_connecthost(struct Curl_easy *data, return CURLE_OPERATION_TIMEDOUT; } - conn->num_addr = Curl_num_addresses(remotehost->addr); - conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr; - conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD; - - /* Max time for the next connection attempt */ - conn->timeoutms_per_addr[0] = - conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2; - conn->timeoutms_per_addr[1] = - conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2; + ctx->started = Curl_now(); + /* remotehost->addr is the list of addresses from the resolver, each + * with an address family. The list has at least one entry, possibly + * many more. + * We try at most 2 at a time, until we either get a connection or + * run out of addresses to try. Since likelihood of success is tied + * to the address family (e.g. IPV6 might not work at all ), we want + * the 2 connect attempt ballers to try different families, if possible. + * + */ if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) { /* any IP version is allowed */ - conn->tempfamily[0] = conn->tempaddr[0]? - conn->tempaddr[0]->ai_family:0; + ai_family0 = remotehost->addr? + remotehost->addr->ai_family : 0; #ifdef ENABLE_IPV6 - conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ? + ai_family1 = ai_family0 == AF_INET6 ? AF_INET : AF_INET6; #else - conn->tempfamily[1] = AF_UNSPEC; + ai_family1 = AF_UNSPEC; #endif } else { /* only one IP version is allowed */ - conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ? + ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ? AF_INET : #ifdef ENABLE_IPV6 AF_INET6; #else AF_UNSPEC; #endif - conn->tempfamily[1] = AF_UNSPEC; - - ainext(conn, 0, FALSE); /* find first address of the right type */ + ai_family1 = AF_UNSPEC; } - ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */ - - DEBUGF(infof(data, "family0 == %s, family1 == %s", - conn->tempfamily[0] == AF_INET ? "v4" : "v6", - conn->tempfamily[1] == AF_INET ? "v4" : "v6")); + /* Get the first address in the list that matches the family, + * this might give NULL, if we do not have any matches. */ + addr0 = addr_first_match(remotehost->addr, ai_family0); + addr1 = addr_first_match(remotehost->addr, ai_family1); + if(!addr0 && addr1) { + /* switch around, so a single baller always uses addr0 */ + addr0 = addr1; + ai_family0 = ai_family1; + addr1 = NULL; + } - /* get through the list in family order in case of quick failures */ - for(i = 0; (i < 2) && result; i++) { - while(conn->tempaddr[i]) { - result = singleipconnect(data, conn, conn->tempaddr[i], i); - if(!result) - break; - ainext(conn, i, TRUE); - } + /* We found no address that matches our criteria, we cannot connect */ + if(!addr0) { + return CURLE_COULDNT_CONNECT; } + + memset(ctx->baller, 0, sizeof(ctx->baller)); + result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0, + NULL, 0, /* no primary/delay, start now */ + timeout_ms, EXPIRE_DNS_PER_NAME); if(result) return result; + DEBUGF(LOG_CF(data, cf, "created %s (timeout %ldms)", + ctx->baller[0]->name, ctx->baller[0]->timeoutms)); + if(addr1) { + /* second one gets a delayed start */ + result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1, + ctx->baller[0], /* wait on that to fail */ + /* or start this delayed */ + data->set.happy_eyeballs_timeout, + timeout_ms, EXPIRE_DNS_PER_NAME2); + if(result) + return result; + DEBUGF(LOG_CF(data, cf, "created %s (timeout %ldms)", + ctx->baller[1]->name, ctx->baller[1]->timeoutms)); + } Curl_expire(data, data->set.happy_eyeballs_timeout, EXPIRE_HAPPY_EYEBALLS); @@ -1369,310 +822,335 @@ CURLcode Curl_connecthost(struct Curl_easy *data, return CURLE_OK; } -struct connfind { - long id_tofind; - struct connectdata *found; -}; - -static int conn_is_conn(struct Curl_easy *data, - struct connectdata *conn, void *param) +static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct connfind *f = (struct connfind *)param; - (void)data; - if(conn->connection_id == f->id_tofind) { - f->found = conn; - return 1; + struct cf_he_ctx *ctx = cf->ctx; + size_t i; + + DEBUGASSERT(ctx); + DEBUGASSERT(data); + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + baller_free(ctx->baller[i], data); + ctx->baller[i] = NULL; } - return 0; + baller_free(ctx->winner, data); + ctx->winner = NULL; } -/* - * Used to extract socket and connectdata struct for the most recent - * transfer on the given Curl_easy. - * - * The returned socket will be CURL_SOCKET_BAD in case of failure! - */ -curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, - struct connectdata **connp) +static int cf_he_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) { - DEBUGASSERT(data); + struct cf_he_ctx *ctx = cf->ctx; + size_t i, s; + int wrc, rc = GETSOCK_BLANK; + curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE]; - /* this works for an easy handle: - * - that has been used for curl_easy_perform() - * - that is associated with a multi handle, and whose connection - * was detached with CURLOPT_CONNECT_ONLY - */ - if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { - struct connectdata *c; - struct connfind find; - find.id_tofind = data->state.lastconnect_id; - find.found = NULL; + if(cf->connected) + return cf->next->cft->get_select_socks(cf->next, data, socks); - Curl_conncache_foreach(data, - data->share && (data->share->specifier - & (1<< CURL_LOCK_DATA_CONNECT))? - &data->share->conn_cache: - data->multi_easy? - &data->multi_easy->conn_cache: - &data->multi->conn_cache, &find, conn_is_conn); + for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller || !baller->cf) + continue; - if(!find.found) { - data->state.lastconnect_id = -1; - return CURL_SOCKET_BAD; + wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks); + if(wrc) { + /* TODO: we assume we get at most one socket back */ + socks[s] = wsocks[0]; + if(wrc & GETSOCK_WRITESOCK(0)) + rc |= GETSOCK_WRITESOCK(s); + if(wrc & GETSOCK_READSOCK(0)) + rc |= GETSOCK_READSOCK(s); + s++; } - - c = find.found; - if(connp) - /* only store this if the caller cares for it */ - *connp = c; - return c->sock[FIRSTSOCKET]; } - return CURL_SOCKET_BAD; + return rc; } -/* - * Check if a connection seems to be alive. - */ -bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn) +static CURLcode cf_he_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) { - (void)data; - /* First determine if ssl */ - if(Curl_conn_is_ssl(data, FIRSTSOCKET)) { - /* use the SSL context */ - if(!Curl_ssl_check_cxn(data, conn)) - return false; /* FIN received */ + struct cf_he_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; } -/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ -#ifdef MSG_PEEK - else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) - return false; - else { - /* use the socket */ - char buf; - if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, - (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { - return false; /* FIN received */ - } + + (void)blocking; /* TODO: do we want to support this? */ + DEBUGASSERT(ctx); + *done = FALSE; + + switch(ctx->state) { + case SCFST_INIT: + DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); + DEBUGASSERT(!cf->connected); + result = start_connect(cf, data, ctx->remotehost); + if(result) + return result; + ctx->state = SCFST_WAITING; + /* FALLTHROUGH */ + case SCFST_WAITING: + result = is_connected(cf, data, done); + if(!result && *done) { + DEBUGASSERT(ctx->winner); + DEBUGASSERT(ctx->winner->cf); + DEBUGASSERT(ctx->winner->cf->connected); + /* we have a winner. Install and activate it. + * close/free all others. */ + ctx->state = SCFST_DONE; + cf->connected = TRUE; + cf->next = ctx->winner->cf; + ctx->winner->cf = NULL; + cf_he_ctx_clear(cf, data); + Curl_conn_cf_cntrl(cf->next, data, TRUE, + CF_CTRL_CONN_INFO_UPDATE, 0, NULL); + + if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ + Curl_verboseconnect(data, cf->conn); + data->info.numconnects++; /* to track the # of connections made */ + } + break; + case SCFST_DONE: + *done = TRUE; + break; } -#endif - return true; + return result; } -/* - * Close a socket. - * - * 'conn' can be NULL, beware! - */ -int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sock) +static void cf_he_close(struct Curl_cfilter *cf, + struct Curl_easy *data) { - if(conn && conn->fclosesocket) { - if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted) - /* if this socket matches the second socket, and that was created with - accept, then we MUST NOT call the callback but clear the accepted - status */ - conn->bits.sock_accepted = FALSE; - else { - int rc; - Curl_multi_closed(data, sock); - Curl_set_in_callback(data, true); - rc = conn->fclosesocket(conn->closesocket_client, sock); - Curl_set_in_callback(data, false); - return rc; - } - } + struct cf_he_ctx *ctx = cf->ctx; - if(conn) - /* tell the multi-socket code about this */ - Curl_multi_closed(data, sock); - - sclose(sock); + DEBUGF(LOG_CF(data, cf, "close")); + cf_he_ctx_clear(cf, data); + cf->connected = FALSE; + ctx->state = SCFST_INIT; - return 0; + if(cf->next) { + cf->next->cft->close(cf->next, data); + Curl_conn_cf_discard_chain(&cf->next, data); + } } -/* - * Create a socket based on info from 'conn' and 'ai'. - * - * 'addr' should be a pointer to the correct struct to get data back, or NULL. - * 'sockfd' must be a pointer to a socket descriptor. - * - * If the open socket callback is set, used that! - * - */ -CURLcode Curl_socket(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - curl_socket_t *sockfd) +static bool cf_he_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) { - struct connectdata *conn = data->conn; - struct Curl_sockaddr_ex dummy; - - if(!addr) - /* if the caller doesn't want info back, use a local temp copy */ - addr = &dummy; - - /* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold - * any protocol-specific address structures. The variable declared here - * will be used to pass / receive data to/from the fopensocket callback - * if this has been set, before that, it is initialized from parameters. - */ + struct cf_he_ctx *ctx = cf->ctx; + size_t i; - addr->family = ai->ai_family; - switch(conn->transport) { - case TRNSPRT_TCP: - addr->socktype = SOCK_STREAM; - addr->protocol = IPPROTO_TCP; - break; - case TRNSPRT_UNIX: - addr->socktype = SOCK_STREAM; - addr->protocol = IPPROTO_IP; - break; - default: /* UDP and QUIC */ - addr->socktype = SOCK_DGRAM; - addr->protocol = IPPROTO_UDP; - break; - } - addr->addrlen = ai->ai_addrlen; - - if(addr->addrlen > sizeof(struct Curl_sockaddr_storage)) - addr->addrlen = sizeof(struct Curl_sockaddr_storage); - memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen); - - if(data->set.fopensocket) { - /* - * If the opensocket callback is set, all the destination address - * information is passed to the callback. Depending on this information the - * callback may opt to abort the connection, this is indicated returning - * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When - * the callback returns a valid socket the destination address information - * might have been changed and this 'new' address will actually be used - * here to connect. - */ - Curl_set_in_callback(data, true); - *sockfd = data->set.fopensocket(data->set.opensocket_client, - CURLSOCKTYPE_IPCXN, - (struct curl_sockaddr *)addr); - Curl_set_in_callback(data, false); - } - else - /* opensocket callback not set, so simply create the socket now */ - *sockfd = socket(addr->family, addr->socktype, addr->protocol); + if(cf->connected) + return cf->next->cft->has_data_pending(cf->next, data); - if(*sockfd == CURL_SOCKET_BAD) - /* no socket, no connection */ - return CURLE_COULDNT_CONNECT; + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; + if(!baller || !baller->cf) + continue; + if(baller->cf->cft->has_data_pending(baller->cf, data)) + return TRUE; + } + return FALSE; +} - if(conn->transport == TRNSPRT_QUIC) { - /* QUIC sockets need to be nonblocking */ - (void)curlx_nonblock(*sockfd, TRUE); - switch(addr->family) { -#if defined(__linux__) && defined(IP_MTU_DISCOVER) - case AF_INET: { - int val = IP_PMTUDISC_DO; - (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val, - sizeof(val)); - break; +static CURLcode cf_he_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_he_ctx *ctx = cf->ctx; + + if(!cf->connected) { + switch(query) { + case CF_QUERY_CONNECT_REPLY_MS: { + int reply_ms = -1; + size_t i; + + for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { + struct eyeballer *baller = ctx->baller[i]; + int breply_ms; + + if(baller && baller->cf && + !baller->cf->cft->query(baller->cf, data, query, + &breply_ms, NULL)) { + if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) + reply_ms = breply_ms; + } + } + *pres1 = reply_ms; + DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1)); + return CURLE_OK; } -#endif -#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) - case AF_INET6: { - int val = IPV6_PMTUDISC_DO; - (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, - sizeof(val)); + + default: break; } -#endif - } } -#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(conn->scope_id && (addr->family == AF_INET6)) { - struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; - sa6->sin6_scope_id = conn->scope_id; - } -#endif - - return CURLE_OK; + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; } -/* - * Curl_conncontrol() marks streams or connection for closure. - */ -void Curl_conncontrol(struct connectdata *conn, - int ctrl /* see defines in header */ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - , const char *reason -#endif - ) +static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - /* close if a connection, or a stream that isn't multiplexed. */ - /* This function will be called both before and after this connection is - associated with a transfer. */ - bool closeit; - DEBUGASSERT(conn); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - (void)reason; /* useful for debugging */ -#endif - closeit = (ctrl == CONNCTRL_CONNECTION) || - ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM)); - if((ctrl == CONNCTRL_STREAM) && - (conn->handler->flags & PROTOPT_STREAM)) - ; - else if((bit)closeit != conn->bits.close) { - conn->bits.close = closeit; /* the only place in the source code that - should assign this bit */ + struct cf_he_ctx *ctx = cf->ctx; + + DEBUGF(LOG_CF(data, cf, "destroy")); + if(ctx) { + cf_he_ctx_clear(cf, data); } + /* release any resources held in state */ + Curl_safefree(ctx); } -typedef enum { - SCFST_INIT, - SCFST_WAITING, - SCFST_DONE -} cf_connect_state; - -struct socket_cf_ctx { - const struct Curl_dns_entry *remotehost; - cf_connect_state state; +struct Curl_cftype Curl_cft_happy_eyeballs = { + "HAPPY-EYEBALLS", + 0, + CURL_LOG_DEFAULT, + cf_he_destroy, + cf_he_connect, + cf_he_close, + Curl_cf_def_get_host, + cf_he_get_select_socks, + cf_he_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_he_query, }; -static int socket_cf_get_select_socks(struct Curl_cfilter *cf, - struct Curl_easy *data, - curl_socket_t *socks) +CURLcode Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + cf_ip_connect_create *cf_create, + const struct Curl_dns_entry *remotehost, + int transport) { - struct connectdata *conn = cf->conn; - int i, s, rc = GETSOCK_BLANK; + struct cf_he_ctx *ctx = NULL; + CURLcode result; (void)data; - if(cf->connected) { - return rc; + (void)conn; + *pcf = NULL; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->transport = transport; + ctx->cf_create = cf_create; + ctx->remotehost = remotehost; + + result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx); + +out: + if(result) { + Curl_safefree(*pcf); + Curl_safefree(ctx); } + return result; +} + +struct transport_provider { + int transport; + cf_ip_connect_create *cf_create; +}; - for(i = s = 0; i<2; i++) { - if(conn->tempsock[i] != CURL_SOCKET_BAD) { - socks[s] = conn->tempsock[i]; - rc |= GETSOCK_WRITESOCK(s); +static +#ifndef DEBUGBUILD +const +#endif +struct transport_provider transport_providers[] = { + { TRNSPRT_TCP, Curl_cf_tcp_create }, #ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) - /* when connecting QUIC, we want to read the socket too */ - rc |= GETSOCK_READSOCK(s); + { TRNSPRT_QUIC, Curl_cf_quic_create }, #endif - s++; + { TRNSPRT_UDP, Curl_cf_udp_create }, + { TRNSPRT_UNIX, Curl_cf_unix_create }, +}; + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +static cf_ip_connect_create *get_cf_create(int transport) +{ + size_t i; + for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { + if(transport == transport_providers[i].transport) + return transport_providers[i].cf_create; + } + return NULL; +} + +#ifdef DEBUGBUILD +void Curl_debug_set_transport_provider(int transport, + cf_ip_connect_create *cf_create) +{ + size_t i; + for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { + if(transport == transport_providers[i].transport) { + transport_providers[i].cf_create = cf_create; + return; } } +} +#endif /* DEBUGBUILD */ - return rc; +static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport) +{ + cf_ip_connect_create *cf_create; + struct Curl_cfilter *cf; + CURLcode result; + + /* Need to be first */ + DEBUGASSERT(cf_at); + cf_create = get_cf_create(transport); + if(!cf_create) { + DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport)); + return CURLE_UNSUPPORTED_PROTOCOL; + } + result = Curl_cf_happy_eyeballs_create(&cf, data, cf_at->conn, + cf_create, remotehost, + transport); + if(result) + return result; + + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; } -static CURLcode socket_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) +typedef enum { + CF_SETUP_INIT, + CF_SETUP_CNNCT_EYEBALLS, + CF_SETUP_CNNCT_SOCKS, + CF_SETUP_CNNCT_HTTP_PROXY, + CF_SETUP_CNNCT_HAPROXY, + CF_SETUP_CNNCT_SSL, + CF_SETUP_DONE +} cf_setup_state; + +struct cf_setup_ctx { + cf_setup_state state; + const struct Curl_dns_entry *remotehost; + int ssl_mode; + int transport; +}; + +static CURLcode cf_setup_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) { - struct connectdata *conn = cf->conn; - int sockindex = cf->sockindex; - struct socket_cf_ctx *ctx = cf->ctx; + struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; if(cf->connected) { @@ -1680,253 +1158,241 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - (void)blocking; - DEBUGASSERT(ctx); - *done = FALSE; - switch(ctx->state) { - case SCFST_INIT: - DEBUGASSERT(CURL_SOCKET_BAD == conn->sock[sockindex]); - DEBUGASSERT(!cf->connected); - result = Curl_connecthost(data, conn, ctx->remotehost); - if(!result) - ctx->state = SCFST_WAITING; - break; - case SCFST_WAITING: - result = is_connected(data, conn, sockindex, done); - if(!result && *done) { - Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ - if(Curl_conn_is_ssl(data, FIRSTSOCKET) || - (conn->handler->protocol & PROTO_FAMILY_SSH)) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ - post_connect(data, conn, sockindex); - ctx->state = SCFST_DONE; - cf->connected = TRUE; - } - break; - case SCFST_DONE: - *done = TRUE; - break; + /* connect current sub-chain */ +connect_sub_chain: + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; } - return result; -} -static CURLcode socket_cf_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost) -{ - struct socket_cf_ctx *ctx = cf->ctx; + if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { + result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport); + if(result) + return result; + ctx->state = CF_SETUP_CNNCT_EYEBALLS; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } - (void)data; - DEBUGASSERT(ctx); - if(ctx->remotehost != remotehost) { - if(ctx->remotehost) { - /* switching dns entry? TODO: reset? */ - } - ctx->remotehost = remotehost; + /* sub-chain connected, do we need to add more? */ +#ifndef CURL_DISABLE_PROXY + if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { + result = Curl_cf_socks_proxy_insert_after(cf, data); + if(result) + return result; + ctx->state = CF_SETUP_CNNCT_SOCKS; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; } - DEBUGF(infof(data, CFMSG(cf, "setup(remotehost=%s)"), - cf->conn->hostname_resolve)); - return CURLE_OK; -} -static void socket_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - int sockindex = cf->sockindex; - struct socket_cf_ctx *ctx = cf->ctx; - - DEBUGASSERT(ctx); - /* close possibly still open sockets */ - if(CURL_SOCKET_BAD != cf->conn->sock[sockindex]) { - Curl_closesocket(data, cf->conn, cf->conn->sock[sockindex]); - cf->conn->sock[sockindex] = CURL_SOCKET_BAD; + if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { +#ifdef USE_SSL + if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS + && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + result = Curl_cf_ssl_proxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* USE_SSL */ + +#if !defined(CURL_DISABLE_HTTP) + if(cf->conn->bits.tunnel_proxy) { + result = Curl_cf_http_proxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* !CURL_DISABLE_HTTP */ + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; } - if(CURL_SOCKET_BAD != cf->conn->tempsock[sockindex]) { - Curl_closesocket(data, cf->conn, cf->conn->tempsock[sockindex]); - cf->conn->tempsock[sockindex] = CURL_SOCKET_BAD; +#endif /* !CURL_DISABLE_PROXY */ + + if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { +#if !defined(CURL_DISABLE_PROXY) + if(data->set.haproxyprotocol) { + if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + failf(data, "haproxy protocol not support with SSL " + "encryption in place (QUIC?)"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + result = Curl_cf_haproxy_insert_after(cf, data); + if(result) + return result; + } +#endif /* !CURL_DISABLE_PROXY */ + ctx->state = CF_SETUP_CNNCT_HAPROXY; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; } - cf->connected = FALSE; - ctx->state = SCFST_INIT; -} - -static void socket_cf_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) -{ - (void)data; - *phost = cf->conn->host.name; - *pdisplay_host = cf->conn->host.dispname; - *pport = cf->conn->port; -} -static bool socket_cf_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - int readable; - (void)data; - DEBUGASSERT(cf); + if(ctx->state < CF_SETUP_CNNCT_SSL) { +#ifdef USE_SSL + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE + || (ctx->ssl_mode != CURL_CF_SSL_DISABLE + && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */ + && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ + result = Curl_cf_ssl_insert_after(cf, data); + if(result) + return result; + } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + } - readable = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0); - return (readable > 0 && (readable & CURL_CSELECT_IN)); + ctx->state = CF_SETUP_DONE; + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; } -static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) +static void cf_setup_close(struct Curl_cfilter *cf, + struct Curl_easy *data) { - ssize_t nwritten; - nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err); - return nwritten; -} + struct cf_setup_ctx *ctx = cf->ctx; -static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - ssize_t nread; - nread = Curl_recv_plain(data, cf->sockindex, buf, len, err); - return nread; + DEBUGF(LOG_CF(data, cf, "close")); + cf->connected = FALSE; + ctx->state = CF_SETUP_INIT; + + if(cf->next) { + cf->next->cft->close(cf->next, data); + Curl_conn_cf_discard_chain(&cf->next, data); + } } -static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct socket_cf_ctx *state = cf->ctx; + struct cf_setup_ctx *ctx = cf->ctx; (void)data; - if(cf->connected) { - socket_cf_close(cf, data); - } - /* release any resources held in state */ - Curl_safefree(state); + DEBUGF(LOG_CF(data, cf, "destroy")); + Curl_safefree(ctx); } -static const struct Curl_cftype cft_socket = { - "SOCKET", - CF_TYPE_IP_CONNECT, - socket_cf_destroy, - socket_cf_setup, - socket_cf_connect, - socket_cf_close, - socket_cf_get_host, - socket_cf_get_select_socks, - socket_cf_data_pending, - socket_cf_send, - socket_cf_recv, - Curl_cf_def_attach_data, - Curl_cf_def_detach_data, + +struct Curl_cftype Curl_cft_setup = { + "SETUP", + 0, + CURL_LOG_DEFAULT, + cf_setup_destroy, + cf_setup_connect, + cf_setup_close, + Curl_cf_def_get_host, + Curl_cf_def_get_select_socks, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, }; -CURLcode Curl_conn_socket_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +static CURLcode cf_setup_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode) { - CURLcode result; struct Curl_cfilter *cf = NULL; - struct socket_cf_ctx *scf_ctx = NULL; + struct cf_setup_ctx *ctx; + CURLcode result = CURLE_OK; - /* Need to be first */ - DEBUGASSERT(conn); - DEBUGASSERT(!conn->cfilter[sockindex]); - scf_ctx = calloc(sizeof(*scf_ctx), 1); - if(!scf_ctx) { + (void)data; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = Curl_cf_create(&cf, &cft_socket, scf_ctx); + ctx->state = CF_SETUP_INIT; + ctx->remotehost = remotehost; + ctx->ssl_mode = ssl_mode; + ctx->transport = transport; + + result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); if(result) goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); + ctx = NULL; out: - if(result) { - Curl_safefree(cf); - Curl_safefree(scf_ctx); - } + *pcf = result? NULL : cf; + free(ctx); return result; } -static CURLcode socket_accept_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking, bool *done) +CURLcode Curl_cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode) { - /* we start accepted, if we ever close, we cannot go on */ - (void)data; - (void)blocking; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - return CURLE_FAILED_INIT; + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; } -static CURLcode socket_accept_cf_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct Curl_dns_entry *remotehost) +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode) { - /* we start accepted, if we ever close, we cannot go on */ - (void)data; - (void)remotehost; - if(cf->connected) { - return CURLE_OK; - } - return CURLE_FAILED_INIT; -} + struct Curl_cfilter *cf; + CURLcode result; -static const struct Curl_cftype cft_socket_accept = { - "SOCKET-ACCEPT", - CF_TYPE_IP_CONNECT, - socket_cf_destroy, - socket_accept_cf_setup, - socket_accept_cf_connect, - socket_cf_close, - socket_cf_get_host, /* TODO: not accurate */ - Curl_cf_def_get_select_socks, - socket_cf_data_pending, - socket_cf_send, - socket_cf_recv, - Curl_cf_def_attach_data, - Curl_cf_def_detach_data, -}; + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + return result; +} -CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, curl_socket_t *s) +CURLcode Curl_conn_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int ssl_mode) { - CURLcode result; - struct Curl_cfilter *cf = NULL; - struct socket_cf_ctx *scf_ctx = NULL; + CURLcode result = CURLE_OK; - cf = conn->cfilter[sockindex]; - if(cf && cf->cft == &cft_socket_accept) { - /* already an accept filter installed, just replace the socket */ - scf_ctx = cf->ctx; - result = CURLE_OK; - } - else { - /* replace any existing */ - Curl_conn_cf_discard_all(data, conn, sockindex); - scf_ctx = calloc(sizeof(*scf_ctx), 1); - if(!scf_ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - result = Curl_cf_create(&cf, &cft_socket_accept, scf_ctx); + DEBUGASSERT(data); + DEBUGASSERT(conn->handler); + +#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) + if(!conn->cfilter[sockindex] && + conn->handler->protocol == CURLPROTO_HTTPS && + (ssl_mode == CURL_CF_SSL_ENABLE || ssl_mode != CURL_CF_SSL_DISABLE)) { + + result = Curl_cf_https_setup(data, conn, sockindex, remotehost); if(result) goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); } +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ - /* close any existing socket and replace */ - Curl_closesocket(data, conn, conn->sock[sockindex]); - conn->sock[sockindex] = *s; - conn->bits.sock_accepted = TRUE; - cf->connected = TRUE; - scf_ctx->state = SCFST_DONE; + /* Still no cfilter set, apply default. */ + if(!conn->cfilter[sockindex]) { + result = Curl_cf_setup_add(data, conn, sockindex, remotehost, + conn->transport, ssl_mode); + if(result) + goto out; + } + DEBUGASSERT(conn->cfilter[sockindex]); out: - if(result) { - Curl_safefree(cf); - Curl_safefree(scf_ctx); - } return result; } + diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 1e90a85..e4fa10c 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -29,9 +29,7 @@ #include "sockaddr.h" #include "timeval.h" -CURLcode Curl_connecthost(struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_dns_entry *host); +struct Curl_dns_entry; /* generic function that returns how much time there's left to run, according to the timeouts set */ @@ -53,67 +51,8 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, char *addr, int *port); -/* - * Check if a connection seems to be alive. - */ -bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn); - -#ifdef USE_WINSOCK -/* When you run a program that uses the Windows Sockets API, you may - experience slow performance when you copy data to a TCP server. - - https://support.microsoft.com/kb/823764 - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - -*/ -void Curl_sndbufset(curl_socket_t sockfd); -#else -#define Curl_sndbufset(y) Curl_nop_stmt -#endif - -void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd); -void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd); -void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd, - char *local_ip, int *local_port); void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, char *local_ip, int local_port); -int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sock); - -/* - * The Curl_sockaddr_ex structure is basically libcurl's external API - * curl_sockaddr structure with enough space available to directly hold any - * protocol-specific address structures. The variable declared here will be - * used to pass / receive data to/from the fopensocket callback if this has - * been set, before that, it is initialized from parameters. - */ -struct Curl_sockaddr_ex { - int family; - int socktype; - int protocol; - unsigned int addrlen; - union { - struct sockaddr addr; - struct Curl_sockaddr_storage buff; - } _sa_ex_u; -}; -#define sa_addr _sa_ex_u.addr - -/* - * Create a socket based on info from 'conn' and 'ai'. - * - * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open - * socket callback is set, used that! - * - */ -CURLcode Curl_socket(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - curl_socket_t *sockfd); /* * Curl_conncontrol() marks the end of a connection/stream. The 'closeit' @@ -148,13 +87,71 @@ void Curl_conncontrol(struct connectdata *conn, #define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP) #endif -CURLcode Curl_conn_socket_set(struct Curl_easy *data, +/** + * Create a cfilter for making an "ip" connection to the + * given address, using parameters from `conn`. The "ip" connection + * can be a TCP socket, a UDP socket or even a QUIC connection. + * + * It MUST use only the supplied `ai` for its connection attempt. + * + * Such a filter may be used in "happy eyeball" scenarios, and its + * `connect` implementation needs to support non-blocking. Once connected, + * it MAY be installed in the connection filter chain to serve transfers. + */ +typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +/** + * Create a happy eyeball connection filter that uses the, once resolved, + * address information to connect on ip families based on connection + * configuration. + * @param pcf output, the created cfilter + * @param data easy handle used in creation + * @param conn connection the filter is created for + * @param cf_create method to create the sub-filters performing the + * actual connects. + */ +CURLcode +Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, struct connectdata *conn, - int sockindex); - -CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - curl_socket_t *s); + cf_ip_connect_create *cf_create, + const struct Curl_dns_entry *remotehost, + int transport); + +CURLcode Curl_cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode); + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + const struct Curl_dns_entry *remotehost, + int transport, + int ssl_mode); + +/** + * Setup the cfilters at `sockindex` in connection `conn`. + * If no filter chain is installed yet, inspects the configuration + * in `data` and `conn? to install a suitable filter chain. + */ +CURLcode Curl_conn_setup(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const struct Curl_dns_entry *remotehost, + int ssl_mode); + +extern struct Curl_cftype Curl_cft_happy_eyeballs; +extern struct Curl_cftype Curl_cft_setup; + +#ifdef DEBUGBUILD +void Curl_debug_set_transport_provider(int transport, + cf_ip_connect_create *cf_create); +#endif #endif /* HEADER_CURL_CONNECT_H */ diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index 9e345b1..a29d204 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -977,7 +977,8 @@ static const struct content_encoding error_encoding = { static struct contenc_writer * new_unencoding_writer(struct Curl_easy *data, const struct content_encoding *handler, - struct contenc_writer *downstream) + struct contenc_writer *downstream, + int order) { struct contenc_writer *writer; @@ -987,6 +988,7 @@ new_unencoding_writer(struct Curl_easy *data, if(writer) { writer->handler = handler; writer->downstream = downstream; + writer->order = order; if(handler->init_writer(data, writer)) { free(writer); writer = NULL; @@ -1042,10 +1044,10 @@ static const struct content_encoding *find_encoding(const char *name, /* Set-up the unencoding stack from the Content-Encoding header value. * See RFC 7231 section 3.1.2.2. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked) + const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; - int counter = 0; + unsigned int order = is_transfer? 2: 1; do { const char *name; @@ -1062,7 +1064,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, namelen = enclist - name + 1; /* Special case: chunked encoding is handled at the reader level. */ - if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) { + if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) { k->chunk = TRUE; /* chunks coming our way. */ Curl_httpchunk_init(data); /* init our chunky engine. */ } @@ -1071,7 +1073,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, struct contenc_writer *writer; if(!k->writer_stack) { - k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL); + k->writer_stack = new_unencoding_writer(data, &client_encoding, + NULL, 0); if(!k->writer_stack) return CURLE_OUT_OF_MEMORY; @@ -1080,16 +1083,29 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, if(!encoding) encoding = &error_encoding; /* Defer error at stack use. */ - if(++counter >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to %u content encodings", - counter); + if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); return CURLE_BAD_CONTENT_ENCODING; } /* Stack the unencoding stage. */ - writer = new_unencoding_writer(data, encoding, k->writer_stack); - if(!writer) - return CURLE_OUT_OF_MEMORY; - k->writer_stack = writer; + if(order >= k->writer_stack->order) { + writer = new_unencoding_writer(data, encoding, + k->writer_stack, order); + if(!writer) + return CURLE_OUT_OF_MEMORY; + k->writer_stack = writer; + } + else { + struct contenc_writer *w = k->writer_stack; + while(w->downstream && order < w->downstream->order) + w = w->downstream; + writer = new_unencoding_writer(data, encoding, + w->downstream, order); + if(!writer) + return CURLE_OUT_OF_MEMORY; + w->downstream = writer; + } } } while(*enclist); @@ -1099,11 +1115,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, #else /* Stubs for builds without HTTP. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked) + const char *enclist, int is_transfer) { (void) data; (void) enclist; - (void) maybechunked; + (void) is_transfer; return CURLE_NOT_BUILT_IN; } diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h index 3c278cf..56e7f97 100644 --- a/Utilities/cmcurl/lib/content_encoding.h +++ b/Utilities/cmcurl/lib/content_encoding.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -28,6 +28,7 @@ struct contenc_writer { const struct content_encoding *handler; /* Encoding handler. */ struct contenc_writer *downstream; /* Downstream writer. */ + unsigned int order; /* Ordering within writer stack. */ }; /* Content encoding writer. */ @@ -46,7 +47,7 @@ struct content_encoding { CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked); + const char *enclist, int is_transfer); CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes); diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index bccf2e8..c457b2d 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -329,7 +329,7 @@ static char *sanitize_cookie_path(const char *cookie_path) */ void Curl_cookie_loadfiles(struct Curl_easy *data) { - struct curl_slist *list = data->state.cookielist; + struct curl_slist *list = data->set.cookielist; if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { @@ -347,8 +347,6 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) data->cookies = newcookies; list = list->next; } - curl_slist_free_all(data->state.cookielist); /* clean up list */ - data->state.cookielist = NULL; /* don't do this again! */ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } } @@ -1301,7 +1299,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, */ remove_expired(c); - if(fromfile && fp) + if(fromfile) fclose(fp); } @@ -1800,12 +1798,10 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) CURLcode res; if(data->set.str[STRING_COOKIEJAR]) { - if(data->state.cookielist) { - /* If there is a list of cookie files to read, do it first so that - we have all the told files read before we write the new jar. - Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ - Curl_cookie_loadfiles(data); - } + /* If there is a list of cookie files to read, do it first so that + we have all the told files read before we write the new jar. + Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ + Curl_cookie_loadfiles(data); Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -1816,12 +1812,6 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); } else { - if(cleanup && data->state.cookielist) { - /* since nothing is written, we can just free the list of cookie file - names */ - curl_slist_free_all(data->state.cookielist); /* clean up list */ - data->state.cookielist = NULL; - } Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); } diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h index abc0a2e..39bb08b 100644 --- a/Utilities/cmcurl/lib/cookie.h +++ b/Utilities/cmcurl/lib/cookie.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index bcea883..35a0635 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h index b778121..c757c49 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.h +++ b/Utilities/cmcurl/lib/curl_addrinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_base64.h b/Utilities/cmcurl/lib/curl_base64.h index 85368a1..806d443 100644 --- a/Utilities/cmcurl/lib/curl_base64.h +++ b/Utilities/cmcurl/lib/curl_base64.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index 9cde948..6e2458d 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h index dc6b8ca..1d1d60c 100644 --- a/Utilities/cmcurl/lib/curl_ctype.h +++ b/Utilities/cmcurl/lib/curl_ctype.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -27,7 +27,7 @@ #define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f')) #define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F')) -#define ISLOWCNTRL(x) ((x) >= 0 && ((x) <= 0x1f)) +#define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) #define IS7F(x) ((x) == 0x7f) #define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d)) diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c index a2bf648..5c623b3 100644 --- a/Utilities/cmcurl/lib/curl_des.c +++ b/Utilities/cmcurl/lib/curl_des.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h index c1c1674..6ec450a 100644 --- a/Utilities/cmcurl/lib/curl_des.h +++ b/Utilities/cmcurl/lib/curl_des.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c index 3cc7734..11c662a 100644 --- a/Utilities/cmcurl/lib/curl_endian.c +++ b/Utilities/cmcurl/lib/curl_endian.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_endian.h b/Utilities/cmcurl/lib/curl_endian.h index 08d52e7..fa28321 100644 --- a/Utilities/cmcurl/lib/curl_endian.h +++ b/Utilities/cmcurl/lib/curl_endian.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c index b8a85a9..5f9ca4f 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.c +++ b/Utilities/cmcurl/lib/curl_fnmatch.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_fnmatch.h b/Utilities/cmcurl/lib/curl_fnmatch.h index 8324be5..595646f 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.h +++ b/Utilities/cmcurl/lib/curl_fnmatch.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c index 0d8c285..686abe7 100644 --- a/Utilities/cmcurl/lib/curl_get_line.c +++ b/Utilities/cmcurl/lib/curl_get_line.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_get_line.h b/Utilities/cmcurl/lib/curl_get_line.h index b2a534d..0ff32c5 100644 --- a/Utilities/cmcurl/lib/curl_get_line.h +++ b/Utilities/cmcurl/lib/curl_get_line.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_gethostname.c b/Utilities/cmcurl/lib/curl_gethostname.c index 4747e93..706b2e6 100644 --- a/Utilities/cmcurl/lib/curl_gethostname.c +++ b/Utilities/cmcurl/lib/curl_gethostname.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_gethostname.h b/Utilities/cmcurl/lib/curl_gethostname.h index b736096..9281d9c 100644 --- a/Utilities/cmcurl/lib/curl_gethostname.h +++ b/Utilities/cmcurl/lib/curl_gethostname.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c index 01ab48e..c4c4f88 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.c +++ b/Utilities/cmcurl/lib/curl_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h index b4ed482..7b9a534 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.h +++ b/Utilities/cmcurl/lib/curl_gssapi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 36c0bd6..11625c0 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_krb5.h b/Utilities/cmcurl/lib/curl_krb5.h index ccd6f10..ccf6b96 100644 --- a/Utilities/cmcurl/lib/curl_krb5.h +++ b/Utilities/cmcurl/lib/curl_krb5.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h index ba3ede4..8a1d807 100644 --- a/Utilities/cmcurl/lib/curl_ldap.h +++ b/Utilities/cmcurl/lib/curl_ldap.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_log.c b/Utilities/cmcurl/lib/curl_log.c new file mode 100644 index 0000000..b5ce1c7 --- /dev/null +++ b/Utilities/cmcurl/lib/curl_log.c @@ -0,0 +1,223 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "curl_log.h" +#include "urldata.h" +#include "easyif.h" +#include "cfilters.h" +#include "timeval.h" +#include "multiif.h" +#include "strcase.h" + +#include "cf-socket.h" +#include "connect.h" +#include "http2.h" +#include "http_proxy.h" +#include "cf-http.h" +#include "socks.h" +#include "strtok.h" +#include "vtls/vtls.h" +#include "vquic/vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size) +{ + if(data->set.verbose) { + static const char s_infotype[CURLINFO_END][3] = { + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; + if(data->set.fdebug) { + bool inCallback = Curl_is_in_callback(data); + Curl_set_in_callback(data, true); + (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); + Curl_set_in_callback(data, inCallback); + } + else { + switch(type) { + case CURLINFO_TEXT: + case CURLINFO_HEADER_OUT: + case CURLINFO_HEADER_IN: + fwrite(s_infotype[type], 2, 1, data->set.err); + fwrite(ptr, size, 1, data->set.err); + break; + default: /* nada */ + break; + } + } + } +} + + +/* Curl_failf() is for messages stating why we failed. + * The message SHALL NOT include any LF or CR. + */ +void Curl_failf(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(data->set.verbose || data->set.errorbuffer) { + va_list ap; + int len; + char error[CURL_ERROR_SIZE + 2]; + va_start(ap, fmt); + len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); + + if(data->set.errorbuffer && !data->state.errorbuf) { + strcpy(data->set.errorbuffer, error); + data->state.errorbuf = TRUE; /* wrote error string */ + } + error[len++] = '\n'; + error[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, error, len); + va_end(ap); + } +} + +/* Curl_infof() is for info message along the way */ +#define MAXINFO 2048 + +void Curl_infof(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(data && data->set.verbose) { + va_list ap; + int len; + char buffer[MAXINFO + 2]; + va_start(ap, fmt); + len = mvsnprintf(buffer, MAXINFO, fmt, ap); + va_end(ap); + buffer[len++] = '\n'; + buffer[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, buffer, len); + } +} + +#ifdef DEBUGBUILD + +void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, + const char *fmt, ...) +{ + DEBUGASSERT(cf); + if(data && Curl_log_cf_is_debug(cf)) { + va_list ap; + int len; + char buffer[MAXINFO + 2]; + len = msnprintf(buffer, MAXINFO, "[CONN-%ld%s-%s] ", + cf->conn->connection_id, cf->sockindex? "/2" : "", + cf->cft->name); + va_start(ap, fmt); + len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); + va_end(ap); + buffer[len++] = '\n'; + buffer[len] = '\0'; + Curl_debug(data, CURLINFO_TEXT, buffer, len); + } +} + + +static struct Curl_cftype *cf_types[] = { + &Curl_cft_tcp, + &Curl_cft_udp, + &Curl_cft_unix, + &Curl_cft_tcp_accept, + &Curl_cft_happy_eyeballs, + &Curl_cft_setup, +#ifdef USE_NGHTTP2 + &Curl_cft_nghttp2, +#endif +#ifdef USE_SSL + &Curl_cft_ssl, + &Curl_cft_ssl_proxy, +#endif +#if !defined(CURL_DISABLE_PROXY) +#if !defined(CURL_DISABLE_HTTP) + &Curl_cft_http_proxy, +#endif /* !CURL_DISABLE_HTTP */ + &Curl_cft_haproxy, + &Curl_cft_socks_proxy, +#endif /* !CURL_DISABLE_PROXY */ +#ifdef ENABLE_QUIC + &Curl_cft_http3, +#endif +#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) + &Curl_cft_http_connect, +#endif + NULL, +}; + +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +CURLcode Curl_log_init(void) +{ + const char *setting = getenv("CURL_DEBUG"); + if(setting) { + char *token, *tok_buf, *tmp; + size_t i; + + tmp = strdup(setting); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ", ", &tok_buf); + while(token) { + for(i = 0; cf_types[i]; ++i) { + if(strcasecompare(token, cf_types[i]->name)) { + cf_types[i]->log_level = CURL_LOG_DEBUG; + break; + } + } + token = strtok_r(NULL, ", ", &tok_buf); + } + free(tmp); + } + return CURLE_OK; +} +#else /* DEBUGBUILD */ + +CURLcode Curl_log_init(void) +{ + return CURLE_OK; +} + +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, + const char *fmt, ...) +{ + (void)data; + (void)cf; + (void)fmt; +} +#endif + +#endif /* !DEBUGBUILD */ diff --git a/Utilities/cmcurl/lib/curl_log.h b/Utilities/cmcurl/lib/curl_log.h new file mode 100644 index 0000000..ad6143f --- /dev/null +++ b/Utilities/cmcurl/lib/curl_log.h @@ -0,0 +1,138 @@ +#ifndef HEADER_CURL_LOG_H +#define HEADER_CURL_LOG_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_easy; +struct Curl_cfilter; + +/** + * Init logging, return != 0 on failure. + */ +CURLcode Curl_log_init(void); + + +void Curl_infof(struct Curl_easy *, const char *fmt, ...); +void Curl_failf(struct Curl_easy *, const char *fmt, ...); + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + +#if defined(HAVE_VARIADIC_MACROS_C99) +#define infof(...) Curl_nop_stmt +#elif defined(HAVE_VARIADIC_MACROS_GCC) +#define infof(x...) Curl_nop_stmt +#else +#error "missing VARIADIC macro define, fix and rebuild!" +#endif + +#else /* CURL_DISABLE_VERBOSE_STRINGS */ + +#define infof Curl_infof + +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + +#define failf Curl_failf + + +#define CURL_LOG_DEFAULT 0 +#define CURL_LOG_DEBUG 1 +#define CURL_LOG_TRACE 2 + + +/* the function used to output verbose information */ +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size); + +#ifdef DEBUGBUILD + +/* explainer: we have some mix configuration and werror settings + * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced + * on gnuc and some other compiler. Need to treat carefully. + */ +#if defined(HAVE_VARIADIC_MACROS_C99) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + +#define LOG_CF(data, cf, ...) \ + do { if(Curl_log_cf_is_debug(cf)) \ + Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0) +#else +#define LOG_CF Curl_log_cf_debug +#endif + +void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +#else + const char *fmt, ...); +#endif + +#define Curl_log_cf_is_debug(cf) \ + ((cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG) + +#else /* !DEBUGBUILD */ + +#if defined(HAVE_VARIADIC_MACROS_C99) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define LOG_CF(...) Curl_nop_stmt +#define Curl_log_cf_debug(...) Curl_nop_stmt +#elif defined(HAVE_VARIADIC_MACROS_GCC) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define LOG_CF(x...) Curl_nop_stmt +#define Curl_log_cf_debug(x...) Curl_nop_stmt +#else +#define LOG_CF Curl_log_cf_debug +/* without c99, we seem unable to completely define away this function. */ +void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, + const char *fmt, ...); +#endif + +#define Curl_log_cf_is_debug(x) ((void)(x), FALSE) + +#endif /* !DEBUGBUILD */ + +#define LOG_CF_IS_DEBUG(x) Curl_log_cf_is_debug(x) + +/* Macros intended for DEBUGF logging, use like: + * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much")); + * and it will output: + * [CONN-1-0][CF-SSL] this filter very much rocks + * on connection #1 with sockindex 0 for filter of type "SSL". */ +#define DMSG(d,msg) \ + "[CONN-%ld] "msg, (d)->conn->connection_id +#define DMSGI(d,i,msg) \ + "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i) +#define CMSG(c,msg) \ + "[CONN-%ld] "msg, (c)->connection_id +#define CMSGI(c,i,msg) \ + "[CONN-%ld-%d] "msg, (c)->connection_id, (i) +#define CFMSG(cf,msg) \ + "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \ + (cf)->sockindex, (cf)->cft->name + + + +#endif /* HEADER_CURL_LOG_H */ diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h index 8049355..03567b9 100644 --- a/Utilities/cmcurl/lib/curl_md4.h +++ b/Utilities/cmcurl/lib/curl_md4.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h index 7893296..ec2512f 100644 --- a/Utilities/cmcurl/lib/curl_md5.h +++ b/Utilities/cmcurl/lib/curl_md5.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h index 092fc9f..7af1391 100644 --- a/Utilities/cmcurl/lib/curl_memory.h +++ b/Utilities/cmcurl/lib/curl_memory.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_memrchr.c b/Utilities/cmcurl/lib/curl_memrchr.c index c329a61..3f3dc6d 100644 --- a/Utilities/cmcurl/lib/curl_memrchr.c +++ b/Utilities/cmcurl/lib/curl_memrchr.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_memrchr.h b/Utilities/cmcurl/lib/curl_memrchr.h index e7654e1..a1a4ba0 100644 --- a/Utilities/cmcurl/lib/curl_memrchr.h +++ b/Utilities/cmcurl/lib/curl_memrchr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c index 309dccb..522ea34 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.c +++ b/Utilities/cmcurl/lib/curl_multibyte.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h index 9297148..ddac1f6 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.h +++ b/Utilities/cmcurl/lib/curl_multibyte.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index 690f8f7..25d2526 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -36,12 +36,13 @@ /* Please keep the SSL backend-specific #if branches in this order: 1. USE_OPENSSL - 2. USE_GNUTLS - 3. USE_NSS - 4. USE_MBEDTLS - 5. USE_SECTRANSP - 6. USE_OS400CRYPTO - 7. USE_WIN32_CRYPTO + 2. USE_WOLFSSL + 3. USE_GNUTLS + 4. USE_NSS + 5. USE_MBEDTLS + 6. USE_SECTRANSP + 7. USE_OS400CRYPTO + 8. USE_WIN32_CRYPTO This ensures that: - the same SSL branch gets activated throughout this source diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h index 60444c9..33b651f 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.h +++ b/Utilities/cmcurl/lib/curl_ntlm_core.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -37,11 +37,11 @@ #define NTLM_NEEDS_NSS_INIT #endif -#ifdef USE_WOLFSSL +#if defined(USE_OPENSSL) +# include <openssl/ssl.h> +#elif defined(USE_WOLFSSL) # include <wolfssl/options.h> # include <wolfssl/openssl/ssl.h> -#elif defined(USE_OPENSSL) -# include <openssl/ssl.h> #endif /* Helpers to generate function byte arguments in little endian order */ diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c index fcf5075..a10e2a1 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.c +++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_ntlm_wb.h b/Utilities/cmcurl/lib/curl_ntlm_wb.h index 1f04db8..37704c0 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_wb.h +++ b/Utilities/cmcurl/lib/curl_ntlm_wb.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_path.c b/Utilities/cmcurl/lib/curl_path.c index f00e3ee..2df8687 100644 --- a/Utilities/cmcurl/lib/curl_path.c +++ b/Utilities/cmcurl/lib/curl_path.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_path.h b/Utilities/cmcurl/lib/curl_path.h index 98e56ea..9ed09de 100644 --- a/Utilities/cmcurl/lib/curl_path.h +++ b/Utilities/cmcurl/lib/curl_path.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h index 3823828..6d3d492 100644 --- a/Utilities/cmcurl/lib/curl_printf.h +++ b/Utilities/cmcurl/lib/curl_printf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_range.c b/Utilities/cmcurl/lib/curl_range.c index 4999936..d499953 100644 --- a/Utilities/cmcurl/lib/curl_range.c +++ b/Utilities/cmcurl/lib/curl_range.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_range.h b/Utilities/cmcurl/lib/curl_range.h index 33570ab..77679e2 100644 --- a/Utilities/cmcurl/lib/curl_range.h +++ b/Utilities/cmcurl/lib/curl_range.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 1932cb4..2679a2c 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012, Howard Chu, <hyc@highlandsun.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Howard Chu, <hyc@highlandsun.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h index f856085..9b93ee0 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.h +++ b/Utilities/cmcurl/lib/curl_rtmp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010 - 2022, Howard Chu, <hyc@highlandsun.com> + * Copyright (C) Howard Chu, <hyc@highlandsun.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 46ee800..119fb9b 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -36,7 +36,8 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) + !defined(CURL_DISABLE_POP3) || \ + (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) #include <curl/curl.h> #include "urldata.h" diff --git a/Utilities/cmcurl/lib/curl_sasl.h b/Utilities/cmcurl/lib/curl_sasl.h index c709d56..e94e643 100644 --- a/Utilities/cmcurl/lib/curl_sasl.h +++ b/Utilities/cmcurl/lib/curl_sasl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -125,9 +125,9 @@ struct SASL { unsigned short authmechs; /* Accepted authentication mechanisms */ unsigned short prefmech; /* Preferred authentication mechanism */ unsigned short authused; /* Auth mechanism used for the connection */ - bool resetprefs; /* For URL auth option parsing. */ - bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ - bool force_ir; /* Protocol always supports initial response */ + BIT(resetprefs); /* For URL auth option parsing. */ + BIT(mutual_auth); /* Mutual authentication enabled (GSSAPI only) */ + BIT(force_ir); /* Protocol always supports initial response */ }; /* This is used to test whether the line starts with the given mechanism */ diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index 084d19a..b58f813 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -792,14 +792,16 @@ endings either CRLF or LF so 't' is appropriate. #define FOPEN_APPENDTEXT "a" #endif -/* WinSock destroys recv() buffer when send() failed. - * Enabled automatically for Windows and for Cygwin as Cygwin sockets are - * wrappers for WinSock sockets. https://github.com/curl/curl/issues/657 - * Define DONT_USE_RECV_BEFORE_SEND_WORKAROUND to force disable workaround. +/* Windows workaround to recv before every send, because apparently Winsock + * destroys destroys recv() buffer when send() failed. + * This workaround is now disabled by default since it caused hard to fix bugs. + * Define USE_RECV_BEFORE_SEND_WORKAROUND to enable it. + * https://github.com/curl/curl/issues/657 + * https://github.com/curl/curl/pull/10409 */ #if !defined(DONT_USE_RECV_BEFORE_SEND_WORKAROUND) # if defined(WIN32) || defined(__CYGWIN__) -# define USE_RECV_BEFORE_SEND_WORKAROUND +/* # define USE_RECV_BEFORE_SEND_WORKAROUND */ # endif #else /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */ # ifdef USE_RECV_BEFORE_SEND_WORKAROUND @@ -851,6 +853,13 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define USE_HTTP3 #endif +/* Certain Windows implementations are not aligned with what curl expects, + so always use the local one on this platform. E.g. the mingw-w64 + implementation can return wrong results for non-ASCII inputs. */ +#if defined(HAVE_BASENAME) && defined(WIN32) +#undef HAVE_BASENAME +#endif + #if defined(USE_UNIX_SOCKETS) && defined(WIN32) # if defined(__MINGW32__) && !defined(LUP_SECURE) typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */ @@ -868,4 +877,10 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, # endif #endif +/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no + replacements (yet) so tell the compiler to not warn for them. */ +#ifdef USE_OPENSSL +#define OPENSSL_SUPPRESS_DEPRECATED +#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 ac4a7f1..e68eb50 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index 39523af..c5e157b 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com> + * 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/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index 33108c4..eb21e7e 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h index ad11130..9816d59 100644 --- a/Utilities/cmcurl/lib/curl_sspi.h +++ b/Utilities/cmcurl/lib/curl_sspi.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index dff6167..e13e294 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h index 63392f6..facbc73 100644 --- a/Utilities/cmcurl/lib/curl_threads.h +++ b/Utilities/cmcurl/lib/curl_threads.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/curlx.h b/Utilities/cmcurl/lib/curlx.h index 1796afa..7a753d6 100644 --- a/Utilities/cmcurl/lib/curlx.h +++ b/Utilities/cmcurl/lib/curlx.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/dict.c b/Utilities/cmcurl/lib/dict.c index 993373e..0ce62a0 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -98,37 +98,27 @@ const struct Curl_handler Curl_handler_dict = { PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ }; -static char *unescape_word(const char *inputbuff) +#define DYN_DICT_WORD 10000 +static char *unescape_word(const char *input) { - char *newp = NULL; - char *dictp; - size_t len; - - CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len, - REJECT_NADA); - if(!newp || result) - return NULL; - - dictp = malloc(len*2 + 1); /* add one for terminating zero */ - if(dictp) { - char *ptr; - char ch; - int olen = 0; - /* According to RFC2229 section 2.2, these letters need to be escaped with - \[letter] */ - for(ptr = newp; - (ch = *ptr) != 0; - ptr++) { - if((ch <= 32) || (ch == 127) || - (ch == '\'') || (ch == '\"') || (ch == '\\')) { - dictp[olen++] = '\\'; - } - dictp[olen++] = ch; - } - dictp[olen] = 0; + struct dynbuf out; + const char *ptr; + CURLcode result = CURLE_OK; + Curl_dyn_init(&out, DYN_DICT_WORD); + + /* According to RFC2229 section 2.2, these letters need to be escaped with + \[letter] */ + for(ptr = input; *ptr; ptr++) { + char ch = *ptr; + if((ch <= 32) || (ch == 127) || + (ch == '\'') || (ch == '\"') || (ch == '\\')) + result = Curl_dyn_addn(&out, "\\", 1); + if(!result) + result = Curl_dyn_addn(&out, ptr, 1); + if(result) + return NULL; } - free(newp); - return dictp; + return Curl_dyn_ptr(&out); } /* sendf() sends formatted data to the server */ @@ -178,20 +168,25 @@ static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data, static CURLcode dict_do(struct Curl_easy *data, bool *done) { char *word; - char *eword; + char *eword = NULL; char *ppath; char *database = NULL; char *strategy = NULL; char *nthdef = NULL; /* This is not part of the protocol, but required by RFC 2229 */ - CURLcode result = CURLE_OK; + CURLcode result; struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - char *path = data->state.up.path; + char *path; *done = TRUE; /* unconditionally */ + /* url-decode path before further evaluation */ + result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); + if(result) + return result; + if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { @@ -225,8 +220,10 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } eword = unescape_word(word); - if(!eword) - return CURLE_OUT_OF_MEMORY; + if(!eword) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } result = sendf(sockfd, data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" @@ -239,11 +236,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) strategy, eword); - free(eword); - if(result) { failf(data, "Failed sending DICT request"); - return result; + goto error; } Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */ } @@ -273,8 +268,10 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } eword = unescape_word(word); - if(!eword) - return CURLE_OUT_OF_MEMORY; + if(!eword) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } result = sendf(sockfd, data, "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" @@ -285,11 +282,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) database, eword); - free(eword); - if(result) { failf(data, "Failed sending DICT request"); - return result; + goto error; } Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); } @@ -310,13 +305,16 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) "QUIT\r\n", ppath); if(result) { failf(data, "Failed sending DICT request"); - return result; + goto error; } Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); } } - return CURLE_OK; + error: + free(eword); + free(path); + return result; } #endif /* CURL_DISABLE_DICT */ diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h index b283a0d..ba9a927 100644 --- a/Utilities/cmcurl/lib/dict.h +++ b/Utilities/cmcurl/lib/dict.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/doh.c b/Utilities/cmcurl/lib/doh.c index 3b1d5d6..2e6a377 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -396,6 +396,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, goto error; dohp->pending++; +#ifdef ENABLE_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], @@ -405,6 +406,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, goto error; dohp->pending++; } +#endif return NULL; error: diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h index 678e807..7d7b694 100644 --- a/Utilities/cmcurl/lib/doh.h +++ b/Utilities/cmcurl/lib/doh.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.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/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c index 0b1cf9a..847f6f6 100644 --- a/Utilities/cmcurl/lib/dynbuf.c +++ b/Utilities/cmcurl/lib/dynbuf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.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/dynbuf.h b/Utilities/cmcurl/lib/dynbuf.h index 04a728c..57ad62b 100644 --- a/Utilities/cmcurl/lib/dynbuf.h +++ b/Utilities/cmcurl/lib/dynbuf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.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/easy.c b/Utilities/cmcurl/lib/easy.c index d7f93be..db03026 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -65,6 +65,7 @@ #include "easyif.h" #include "multiif.h" #include "select.h" +#include "cfilters.h" #include "sendf.h" /* for failf function prototype */ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" @@ -113,7 +114,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #if defined(_WIN32_WCE) #define system_strdup _strdup #elif !defined(HAVE_STRDUP) -#define system_strdup curlx_strdup +#define system_strdup Curl_strdup #else #define system_strdup strdup #endif @@ -164,6 +165,11 @@ static CURLcode global_init(long flags, bool memoryfuncs) #endif } + if(Curl_log_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n")); + goto fail; + } + if(!Curl_ssl_init()) { DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); goto fail; @@ -913,11 +919,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) goto fail; } - /* duplicate all values in 'change' */ - if(data->state.cookielist) { - outcurl->state.cookielist = - Curl_slist_duplicate(data->state.cookielist); - if(!outcurl->state.cookielist) + if(data->set.cookielist) { + outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist); + if(!outcurl->set.cookielist) goto fail; } #endif @@ -1003,8 +1007,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) if(outcurl) { #ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(outcurl->state.cookielist); - outcurl->state.cookielist = NULL; + curl_slist_free_all(outcurl->set.cookielist); + outcurl->set.cookielist = NULL; #endif Curl_safefree(outcurl->state.buffer); Curl_dyn_free(&outcurl->state.headerb); @@ -1101,7 +1105,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) k->keepon = newstate; if(!(newstate & KEEP_RECV_PAUSE)) { - Curl_http2_stream_pause(data, FALSE); + Curl_conn_ev_data_pause(data, FALSE); if(data->state.tempcount) { /* there are buffers for sending that can be delivered as the receive @@ -1224,7 +1228,7 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, return result; *n = (size_t)n1; - + infof(data, "reached %s:%d", __FILE__, __LINE__); return CURLE_OK; } @@ -1294,29 +1298,34 @@ static int conn_upkeep(struct Curl_easy *data, struct connectdata *conn, void *param) { - /* Param is unused. */ - (void)param; + struct curltime *now = param; - if(conn->handler->connection_check) { - /* briefly attach the connection to this transfer for the purpose of - checking it */ - Curl_attach_connection(data, conn); + if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms) + return 0; + /* briefly attach for action */ + Curl_attach_connection(data, conn); + if(conn->handler->connection_check) { /* Do a protocol-specific keepalive check on the connection. */ conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); - /* detach the connection again */ - Curl_detach_connection(data); } + else { + /* Do the generic action on the FIRSTSOCKE filter chain */ + Curl_conn_keep_alive(data, conn, FIRSTSOCKET); + } + Curl_detach_connection(data); + conn->keepalive = *now; return 0; /* continue iteration */ } static CURLcode upkeep(struct conncache *conn_cache, void *data) { + struct curltime now = Curl_now(); /* Loop over every connection and make connection alive. */ Curl_conncache_foreach(data, conn_cache, - data, + &now, conn_upkeep); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h index d96e56b..5fa9477 100644 --- a/Utilities/cmcurl/lib/easy_lock.h +++ b/Utilities/cmcurl/lib/easy_lock.h @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c index a639bb3..2b8a521 100644 --- a/Utilities/cmcurl/lib/easygetopt.c +++ b/Utilities/cmcurl/lib/easygetopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * ___|___/|_| ______| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al. + * Copyright (C) Daniel Stenberg, <daniel.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h index 205382c..570ebef 100644 --- a/Utilities/cmcurl/lib/easyif.h +++ b/Utilities/cmcurl/lib/easyif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c index 03b814e..a9c1efd 100644 --- a/Utilities/cmcurl/lib/easyoptions.c +++ b/Utilities/cmcurl/lib/easyoptions.c @@ -1,11 +1,11 @@ /*************************************************************************** * _ _ ____ _ - * Project ___| | | | _ | | + * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ - * ___|___/|_| ______| + * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al. + * Copyright (C) Daniel Stenberg, <daniel.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/easyoptions.h b/Utilities/cmcurl/lib/easyoptions.h index 33f816d..24b4cd9 100644 --- a/Utilities/cmcurl/lib/easyoptions.h +++ b/Utilities/cmcurl/lib/easyoptions.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/escape.c b/Utilities/cmcurl/lib/escape.c index ed59838..56aa2b3 100644 --- a/Utilities/cmcurl/lib/escape.c +++ b/Utilities/cmcurl/lib/escape.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -97,7 +97,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, return strdup(""); while(length--) { - unsigned char in = *string; /* we need to treat the characters unsigned */ + unsigned char in = *string++; /* treat the characters unsigned */ if(Curl_isunreserved(in)) { /* append this */ @@ -106,15 +106,28 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, } else { /* encode it */ - if(Curl_dyn_addf(&d, "%%%02X", in)) + const char hex[] = "0123456789ABCDEF"; + char out[3]={'%'}; + out[1] = hex[in>>4]; + out[2] = hex[in & 0xf]; + if(Curl_dyn_addn(&d, out, 3)) return NULL; } - string++; } return Curl_dyn_ptr(&d); } +static const unsigned char hextable[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */ +}; + +/* the input is a single hex digit */ +#define onehex2dec(x) hextable[x - '0'] + /* * Curl_urldecode() URL decodes the given string. * @@ -137,54 +150,47 @@ CURLcode Curl_urldecode(const char *string, size_t length, { size_t alloc; char *ns; - size_t strindex = 0; - unsigned long hex; DEBUGASSERT(string); DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ - alloc = (length?length:strlen(string)) + 1; - ns = malloc(alloc); + alloc = (length?length:strlen(string)); + ns = malloc(alloc + 1); if(!ns) return CURLE_OUT_OF_MEMORY; - while(--alloc > 0) { + /* store output string */ + *ostring = ns; + + while(alloc) { unsigned char in = *string; if(('%' == in) && (alloc > 2) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ - char hexstr[3]; - char *ptr; - hexstr[0] = string[1]; - hexstr[1] = string[2]; - hexstr[2] = 0; - - hex = strtoul(hexstr, &ptr, 16); + in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]); - in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ - - string += 2; - alloc -= 2; + string += 3; + alloc -= 3; + } + else { + string++; + alloc--; } if(((ctrl == REJECT_CTRL) && (in < 0x20)) || ((ctrl == REJECT_ZERO) && (in == 0))) { - free(ns); + Curl_safefree(*ostring); return CURLE_URL_MALFORMAT; } - ns[strindex++] = in; - string++; + *ns++ = in; } - ns[strindex] = 0; /* terminate it */ + *ns = 0; /* terminate it */ if(olen) /* store output size */ - *olen = strindex; - - /* store output string */ - *ostring = ns; + *olen = ns - *ostring; return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h index 61d4611..cdbb712 100644 --- a/Utilities/cmcurl/lib/escape.h +++ b/Utilities/cmcurl/lib/escape.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/file.c b/Utilities/cmcurl/lib/file.c index 3f642be..51c5d07 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/file.h b/Utilities/cmcurl/lib/file.h index 826d453..4565525 100644 --- a/Utilities/cmcurl/lib/file.h +++ b/Utilities/cmcurl/lib/file.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/fileinfo.c b/Utilities/cmcurl/lib/fileinfo.c index 7bbf24b..409b38f 100644 --- a/Utilities/cmcurl/lib/fileinfo.c +++ b/Utilities/cmcurl/lib/fileinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.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/fileinfo.h b/Utilities/cmcurl/lib/fileinfo.h index 5bad718..af44212 100644 --- a/Utilities/cmcurl/lib/fileinfo.h +++ b/Utilities/cmcurl/lib/fileinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.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/fopen.c b/Utilities/cmcurl/lib/fopen.c index ad3691b..f710dbf 100644 --- a/Utilities/cmcurl/lib/fopen.c +++ b/Utilities/cmcurl/lib/fopen.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -106,7 +106,6 @@ fail: free(tempstore); - *tempname = NULL; return result; } diff --git a/Utilities/cmcurl/lib/fopen.h b/Utilities/cmcurl/lib/fopen.h index 289e55f..e3a919d 100644 --- a/Utilities/cmcurl/lib/fopen.h +++ b/Utilities/cmcurl/lib/fopen.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/formdata.c b/Utilities/cmcurl/lib/formdata.c index b30e8de..2bdb9f2 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/formdata.h b/Utilities/cmcurl/lib/formdata.h index c6c6397..caabb63 100644 --- a/Utilities/cmcurl/lib/formdata.h +++ b/Utilities/cmcurl/lib/formdata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/ftp.c b/Utilities/cmcurl/lib/ftp.c index 874725b..8c72874 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -61,6 +61,7 @@ #include "strcase.h" #include "vtls/vtls.h" #include "cfilters.h" +#include "cf-socket.h" #include "connect.h" #include "strerror.h" #include "inet_ntop.h" @@ -285,8 +286,8 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) conn->bits.do_more = FALSE; (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - /* Replace any filter on SECONDARY with one listeing on this socket */ - result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, &s); + /* Replace any filter on SECONDARY with one listening on this socket */ + result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s); if(result) return result; @@ -819,26 +820,11 @@ static int ftp_domore_getsock(struct Curl_easy *data, if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); - bool any = FALSE; /* if stopped and still in this state, then we're also waiting for a connect on the secondary connection */ socks[0] = conn->sock[FIRSTSOCKET]; - - if(!data->set.ftp_use_port) { - int s; - int i; - /* PORT is used to tell the server to connect to us, and during that we - don't do happy eyeballs, but we do if we connect to the server */ - for(s = 1, i = 0; i<2; i++) { - if(conn->tempsock[i] != CURL_SOCKET_BAD) { - socks[s] = conn->tempsock[i]; - bits |= GETSOCK_WRITESOCK(s++); - any = TRUE; - } - } - } - if(!any) { + if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { socks[1] = conn->sock[SECONDARYSOCKET]; bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1); } @@ -1024,9 +1010,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(*addr != '\0') { /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(conn->ip_addr->ai_family, + switch(Curl_if2ip(conn->remote_addr->family, #ifdef ENABLE_IPV6 - Curl_ipv6_scope(conn->ip_addr->ai_addr), + Curl_ipv6_scope(&conn->remote_addr->sa_addr), conn->scope_id, #endif addr, hbuf, sizeof(hbuf))) { @@ -1097,7 +1083,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, portsock = CURL_SOCKET_BAD; error = 0; for(ai = res; ai; ai = ai->ai_next) { - if(Curl_socket(data, ai, NULL, &portsock)) { + if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) { error = SOCKERRNO; continue; } @@ -1266,9 +1252,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* store which command was sent */ ftpc->count1 = fcmd; - /* Replace any filter on SECONDARY with one listeing on this socket */ - result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, - &portsock); + /* 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 */ @@ -1279,7 +1264,7 @@ out: state(data, FTP_STOP); } if(portsock != CURL_SOCKET_BAD) - Curl_closesocket(data, conn, portsock); + Curl_socket_close(data, conn, portsock); free(addr); return result; } @@ -1954,7 +1939,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* postponed address resolution in case of tcp fastopen */ if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) { - Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]); + Curl_conn_ev_update_info(data, conn); Curl_safefree(ftpc->newhost); ftpc->newhost = strdup(control_address(conn)); if(!ftpc->newhost) @@ -2569,13 +2554,11 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data) /* for USER and PASS responses */ static CURLcode ftp_state_user_resp(struct Curl_easy *data, - int ftpcode, - ftpstate instate) + int ftpcode) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; - (void)instate; /* no use for this yet */ /* some need password anyway, and others just return 2xx ignored */ if((ftpcode == 331) && (ftpc->state == FTP_USER)) { @@ -2670,7 +2653,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, /* 230 User logged in - already! Take as 220 if TLS required. */ if(data->set.use_ssl <= CURLUSESSL_TRY || conn->bits.ftp_use_control_ssl) - return ftp_state_user_resp(data, ftpcode, ftpc->state); + return ftp_state_user_resp(data, ftpcode); } else if(ftpcode != 220) { failf(data, "Got a %03d ftp-server response when 220 was expected", @@ -2742,7 +2725,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, if((ftpcode == 234) || (ftpcode == 334)) { /* this was BLOCKING, keep it so for now */ bool done; - if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) { /* we failed and bail out */ @@ -2775,7 +2758,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, case FTP_USER: case FTP_PASS: - result = ftp_state_user_resp(data, ftpcode, ftpc->state); + result = ftp_state_user_resp(data, ftpcode); break; case FTP_ACCT: diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 7f6f432..65efa6f 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -42,7 +42,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread, /**************************************************************************** * FTP unique setup ***************************************************************************/ -typedef enum { +enum { FTP_STOP, /* do nothing state, stops the state machine */ FTP_WAIT220, /* waiting for the initial 220 response immediately after a connect */ @@ -80,7 +80,8 @@ typedef enum { FTP_STOR, /* generic state for STOR and APPE */ FTP_QUIT, FTP_LAST /* never used */ -} ftpstate; +}; +typedef unsigned char ftpstate; /* use the enum values */ struct ftp_parselist_data; /* defined later in ftplistparser.c */ @@ -122,38 +123,38 @@ struct ftp_conn { char *entrypath; /* the PWD reply when we logged on */ char *file; /* url-decoded file name (or path) */ char **dirs; /* realloc()ed array for path components */ - int dirdepth; /* number of entries used in the 'dirs' array */ - bool dont_check; /* Set to TRUE to prevent the final (post-transfer) - file size and 226/250 status check. It should still - read the line, just ignore the result. */ - bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If - the connection has timed out or been closed, this - should be FALSE when it gets to Curl_ftp_quit() */ - bool cwddone; /* if it has been determined that the proper CWD combo - already has been done */ - int cwdcount; /* number of CWD commands issued */ - bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent - caching the current directory */ - bool wait_data_conn; /* this is set TRUE if data connection is waited */ - /* newhost is the (allocated) IP addr or host name to connect the data - connection to */ - unsigned short newport; char *newhost; char *prevpath; /* url-decoded conn->path from the previous transfer */ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a and others (A/I or zero) */ - int count1; /* general purpose counter for the state machine */ - int count2; /* general purpose counter for the state machine */ - int count3; /* general purpose counter for the state machine */ - ftpstate state; /* always use ftp.c:state() to change state! */ - ftpstate state_saved; /* transfer type saved to be reloaded after - data connection is established */ curl_off_t retr_size_saved; /* Size of retrieved file saved */ char *server_os; /* The target server operating system. */ curl_off_t known_filesize; /* file size is different from -1, if wildcard LIST parsing was done and wc_statemach set it */ + int dirdepth; /* number of entries used in the 'dirs' array */ + int cwdcount; /* number of CWD commands issued */ + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + /* newhost is the (allocated) IP addr or host name to connect the data + connection to */ + unsigned short newport; + ftpstate state; /* always use ftp.c:state() to change state! */ + ftpstate state_saved; /* transfer type saved to be reloaded after data + connection is established */ BIT(ftp_trying_alternative); + BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) + file size and 226/250 status check. It should still + read the line, just ignore the result. */ + BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If + the connection has timed out or been closed, this + should be FALSE when it gets to Curl_ftp_quit() */ + BIT(cwddone); /* if it has been determined that the proper CWD combo + already has been done */ + BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ }; #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index 3d529ef..f7c6434 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/ftplistparser.h b/Utilities/cmcurl/lib/ftplistparser.h index 0a80543..3d9a43b 100644 --- a/Utilities/cmcurl/lib/ftplistparser.h +++ b/Utilities/cmcurl/lib/ftplistparser.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/functypes.h b/Utilities/cmcurl/lib/functypes.h index 8891b1d..075c02e 100644 --- a/Utilities/cmcurl/lib/functypes.h +++ b/Utilities/cmcurl/lib/functypes.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/getenv.c b/Utilities/cmcurl/lib/getenv.c index 5f00fd1..8069784 100644 --- a/Utilities/cmcurl/lib/getenv.c +++ b/Utilities/cmcurl/lib/getenv.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index 3a24c65..826ffd0 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/getinfo.h b/Utilities/cmcurl/lib/getinfo.h index 1b5e8c2..56bb440 100644 --- a/Utilities/cmcurl/lib/getinfo.h +++ b/Utilities/cmcurl/lib/getinfo.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/gopher.c b/Utilities/cmcurl/lib/gopher.c index 6fbb7de..4a11d93 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/gopher.h b/Utilities/cmcurl/lib/gopher.h index 4ea269d..9e3365b 100644 --- a/Utilities/cmcurl/lib/gopher.h +++ b/Utilities/cmcurl/lib/gopher.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/h2h3.c b/Utilities/cmcurl/lib/h2h3.c index 3a9288d..3b21699 100644 --- a/Utilities/cmcurl/lib/h2h3.c +++ b/Utilities/cmcurl/lib/h2h3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -118,6 +118,7 @@ static header_instruction inspect_header(const char *name, size_t namelen, CURLcode Curl_pseudo_headers(struct Curl_easy *data, const char *mem, /* the request */ const size_t len /* size of request */, + size_t* hdrlen /* opt size of headers read */, struct h2h3req **hp) { struct connectdata *conn = data->conn; @@ -291,6 +292,12 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data, } } + if(hdrlen) { + /* Skip trailing CRLF */ + end += 4; + *hdrlen = end - mem; + } + hreq->entries = nheader; *hp = hreq; diff --git a/Utilities/cmcurl/lib/h2h3.h b/Utilities/cmcurl/lib/h2h3.h index c35b706..396c12c 100644 --- a/Utilities/cmcurl/lib/h2h3.h +++ b/Utilities/cmcurl/lib/h2h3.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -51,6 +51,7 @@ struct h2h3req { CURLcode Curl_pseudo_headers(struct Curl_easy *data, const char *request, const size_t len, + size_t* hdrlen /* optional */, struct h2h3req **hp); /* diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c index b6a2a33..06ce92c 100644 --- a/Utilities/cmcurl/lib/hash.c +++ b/Utilities/cmcurl/lib/hash.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hash.h b/Utilities/cmcurl/lib/hash.h index 5b59bf1..9cfffc2 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/headers.c b/Utilities/cmcurl/lib/headers.c index 978c918..22e0e01 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2022, Daniel Stenberg, <daniel@haxx.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/headers.h b/Utilities/cmcurl/lib/headers.h index 96332db..a5229ea 100644 --- a/Utilities/cmcurl/lib/headers.h +++ b/Utilities/cmcurl/lib/headers.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hmac.c b/Utilities/cmcurl/lib/hmac.c index dfb0db5..8d8de17 100644 --- a/Utilities/cmcurl/lib/hmac.c +++ b/Utilities/cmcurl/lib/hmac.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index df50d13..536eb73 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hostip.c b/Utilities/cmcurl/lib/hostip.c index dd427a2..9738806 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hostip.h b/Utilities/cmcurl/lib/hostip.h index 3b1d814..e0d13cd 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hostip4.c b/Utilities/cmcurl/lib/hostip4.c index 109bd1e..9140180 100644 --- a/Utilities/cmcurl/lib/hostip4.c +++ b/Utilities/cmcurl/lib/hostip4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index af8bc23..6b0ba55 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hostsyn.c b/Utilities/cmcurl/lib/hostsyn.c index 73d1e50..ca8b075 100644 --- a/Utilities/cmcurl/lib/hostsyn.c +++ b/Utilities/cmcurl/lib/hostsyn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/hsts.c b/Utilities/cmcurl/lib/hsts.c index c449120..64cbae1 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -39,6 +39,7 @@ #include "parsedate.h" #include "fopen.h" #include "rename.h" +#include "share.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -425,14 +426,23 @@ static CURLcode hsts_add(struct hsts *h, char *line) if(2 == rc) { time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) : TIME_T_MAX; - CURLcode result; + CURLcode result = CURLE_OK; char *p = host; bool subdomain = FALSE; + struct stsentry *e; if(p[0] == '.') { p++; subdomain = TRUE; } - result = hsts_create(h, p, subdomain, expires); + /* only add it if not already present */ + e = Curl_hsts(h, p, subdomain); + if(!e) + result = hsts_create(h, p, subdomain, expires); + else { + /* the same host name, use the largest expire time */ + if(expires > e->expires) + e->expires = expires; + } if(result) return result; } @@ -551,4 +561,18 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) return CURLE_OK; } +void Curl_hsts_loadfiles(struct Curl_easy *data) +{ + struct curl_slist *l = data->set.hstslist; + if(l) { + Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); + + while(l) { + (void)Curl_hsts_loadfile(data, data->hsts, l->data); + l = l->next; + } + Curl_share_unlock(data, CURL_LOCK_DATA_HSTS); + } +} + #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h index 0e36a77..d3431a5 100644 --- a/Utilities/cmcurl/lib/hsts.h +++ b/Utilities/cmcurl/lib/hsts.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -59,9 +59,11 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); +void Curl_hsts_loadfiles(struct Curl_easy *data); #else #define Curl_hsts_cleanup(x) #define Curl_hsts_loadcb(x,y) CURLE_OK #define Curl_hsts_save(x,y,z) +#define Curl_hsts_loadfiles(x) #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ #endif /* HEADER_CURL_HSTS_H */ diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 1b75022..cb585e7 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -62,6 +62,7 @@ #include "cookie.h" #include "vauth/vauth.h" #include "vtls/vtls.h" +#include "vquic/vquic.h" #include "http_digest.h" #include "http_ntlm.h" #include "curl_ntlm_wb.h" @@ -150,7 +151,7 @@ const struct Curl_handler Curl_handler_ws = { http_getsock_do, /* doing_getsock */ ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ + Curl_ws_disconnect, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ @@ -204,7 +205,7 @@ const struct Curl_handler Curl_handler_wss = { http_getsock_do, /* doing_getsock */ ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ + Curl_ws_disconnect, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ @@ -218,41 +219,6 @@ const struct Curl_handler Curl_handler_wss = { #endif -static CURLcode h3_setup_conn(struct Curl_easy *data, - struct connectdata *conn) -{ -#ifdef ENABLE_QUIC - /* We want HTTP/3 directly, setup the filter chain ourself, - * overriding the default behaviour. */ - DEBUGASSERT(conn->transport == TRNSPRT_QUIC); - - if(!(conn->handler->flags & PROTOPT_SSL)) { - failf(data, "HTTP/3 requested for non-HTTPS URL"); - return CURLE_URL_MALFORMAT; - } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) { - failf(data, "HTTP/3 is not supported over a SOCKS proxy"); - return CURLE_URL_MALFORMAT; - } - if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { - failf(data, "HTTP/3 is not supported over a HTTP proxy"); - return CURLE_URL_MALFORMAT; - } -#endif - - DEBUGF(infof(data, "HTTP/3 direct conn setup(conn #%ld, index=%d)", - conn->connection_id, FIRSTSOCKET)); - return Curl_conn_socket_set(data, conn, FIRSTSOCKET); - -#else /* ENABLE_QUIC */ - (void)conn; - (void)data; - DEBUGF(infof(data, "QUIC is not supported in this build")); - return CURLE_NOT_BUILT_IN; -#endif /* !ENABLE_QUIC */ -} - static CURLcode http_setup_conn(struct Curl_easy *data, struct connectdata *conn) { @@ -268,19 +234,16 @@ static CURLcode http_setup_conn(struct Curl_easy *data, Curl_mime_initpart(&http->form); data->req.p.http = http; - if(data->state.httpwant == CURL_HTTP_VERSION_3) { + if((data->state.httpwant == CURL_HTTP_VERSION_3) + || (data->state.httpwant == CURL_HTTP_VERSION_3ONLY)) { + CURLcode result = Curl_conn_may_http3(data, conn); + if(result) + return result; + + /* TODO: HTTP lower version eyeballing */ conn->transport = TRNSPRT_QUIC; } - if(conn->transport == TRNSPRT_QUIC) { - return h3_setup_conn(data, conn); - } - else { - if(!CONN_INUSE(conn)) - /* if not already multi-using, setup connection details */ - Curl_http2_setup_conn(conn); - Curl_http2_setup_req(data); - } return CURLE_OK; } @@ -1256,8 +1219,8 @@ static size_t readmoredata(char *buffer, size_t nitems, void *userp) { - struct Curl_easy *data = (struct Curl_easy *)userp; - struct HTTP *http = data->req.p.http; + struct HTTP *http = (struct HTTP *)userp; + struct Curl_easy *data = http->backup.data; size_t fullsize = size * nitems; if(!http->postsize) @@ -1309,6 +1272,7 @@ static size_t readmoredata(char *buffer, */ 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, @@ -1321,14 +1285,13 @@ CURLcode Curl_buffer_send(struct dynbuf *in, char *ptr; size_t size; struct connectdata *conn = data->conn; - struct HTTP *http = data->req.p.http; size_t sendsize; curl_socket_t sockfd; size_t headersize; DEBUGASSERT(socketindex <= SECONDARYSOCKET); - sockfd = conn->sock[socketindex]; + sockfd = Curl_conn_get_socket(data, socketindex); /* 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 */ @@ -1456,10 +1419,11 @@ CURLcode Curl_buffer_send(struct dynbuf *in, 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 *)data; + data->state.in = (void *)http; http->postdata = ptr; http->postsize = (curl_off_t)size; @@ -1468,7 +1432,6 @@ CURLcode Curl_buffer_send(struct dynbuf *in, http->send_buffer = *in; /* copy the whole struct */ http->sending = HTTPSEND_REQUEST; - return CURLE_OK; } http->sending = HTTPSEND_BODY; @@ -1579,8 +1542,8 @@ static int http_getsock_do(struct Curl_easy *data, curl_socket_t *socks) { /* write mode */ - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; + (void)conn; + socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET); return GETSOCK_WRITESOCK(0); } @@ -1610,8 +1573,6 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK; Curl_dyn_free(&http->send_buffer); - Curl_http2_done(data, premature); - Curl_quic_done(data, premature); Curl_mime_cleanpart(&http->form); Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); @@ -1664,17 +1625,10 @@ bool Curl_use_http_1_1plus(const struct Curl_easy *data, static const char *get_http_string(const struct Curl_easy *data, const struct connectdata *conn) { -#ifdef ENABLE_QUIC - if((data->state.httpwant == CURL_HTTP_VERSION_3) || - (conn->httpversion == 30)) + if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)) return "3"; -#endif - -#ifdef USE_NGHTTP2 - if(conn->proto.httpc.h2) + if(Curl_conn_is_http2(data, conn, FIRSTSOCKET)) return "2"; -#endif - if(Curl_use_http_1_1plus(data, conn)) return "1.1"; @@ -2359,7 +2313,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, 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) CURLE_OK +#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK #endif CURLcode result = CURLE_OK; struct HTTP *http = data->req.p.http; @@ -2403,7 +2357,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, Curl_pgrsSetUploadSize(data, http->postsize); /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, &data->info.request_size, 0, + result = Curl_buffer_send(r, data, data->req.p.http, + &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending PUT request"); @@ -2424,7 +2379,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, if(result) return result; - result = Curl_buffer_send(r, data, &data->info.request_size, 0, + result = Curl_buffer_send(r, data, data->req.p.http, + &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); @@ -2440,8 +2396,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, 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")))) { + (!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, @@ -2495,7 +2450,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, http->sending = HTTPSEND_BODY; /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, &data->info.request_size, 0, + result = Curl_buffer_send(r, data, data->req.p.http, + &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending POST request"); @@ -2561,7 +2517,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, /* In HTTP2, we send request body in DATA frame regardless of its size. */ - if(conn->httpversion != 20 && + if(conn->httpversion < 20 && !data->state.expect100header && (http->postsize < MAX_INITIAL_POST_SIZE)) { /* if we don't use expect: 100 AND @@ -2612,11 +2568,10 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, 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 *)data; + data->state.in = (void *)http; /* set the upload size to the progress meter */ Curl_pgrsSetUploadSize(data, http->postsize); @@ -2655,7 +2610,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, } } /* issue the request */ - result = Curl_buffer_send(r, data, &data->info.request_size, included_body, + result = Curl_buffer_send(r, data, data->req.p.http, + &data->info.request_size, included_body, FIRSTSOCKET); if(result) @@ -2671,7 +2627,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, return result; /* issue the request */ - result = Curl_buffer_send(r, data, &data->info.request_size, 0, + result = Curl_buffer_send(r, data, data->req.p.http, + &data->info.request_size, 0, FIRSTSOCKET); if(result) failf(data, "Failed sending HTTP request"); @@ -3021,50 +2978,27 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) the rest of the request in the PERFORM phase. */ *done = TRUE; - if(conn->transport != TRNSPRT_QUIC) { - if(conn->httpversion < 20) { /* unless the connection is re-used and - already http2 */ - switch(conn->alpn) { - case CURL_HTTP_VERSION_2: - conn->httpversion = 20; /* we know we're on HTTP/2 now */ - - result = Curl_http2_switched(data, NULL, 0); - if(result) - return result; - break; - case CURL_HTTP_VERSION_1_1: - /* continue with HTTP/1.1 when explicitly requested */ - break; - default: - /* Check if user wants to use HTTP/2 with clear TCP */ -#ifdef USE_NGHTTP2 - if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - /* We don't support HTTP/2 proxies yet. Also it's debatable - whether or not this setting should apply to HTTP/2 proxies. */ - infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); - break; - } -#endif - DEBUGF(infof(data, "HTTP/2 over clean TCP")); - conn->httpversion = 20; - - result = Curl_http2_switched(data, NULL, 0); - if(result) - return result; - } -#endif - break; - } - } - else { - /* prepare for an http2 request */ - result = Curl_http2_setup(data, conn); + switch(conn->alpn) { + case CURL_HTTP_VERSION_3: + DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET)); + break; + case CURL_HTTP_VERSION_2: + DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET)); + break; + case CURL_HTTP_VERSION_1_1: + /* continue with HTTP/1.1 when explicitly requested */ + break; + default: + /* Check if user wants to use HTTP/2 with clear TCP */ + if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) { + DEBUGF(infof(data, "HTTP/2 over clean TCP")); + result = Curl_http2_switch(data, conn, FIRSTSOCKET); if(result) return result; } + break; } + http = data->req.p.http; DEBUGASSERT(http); @@ -3224,7 +3158,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } if(!(conn->handler->flags&PROTOPT_SSL) && - conn->httpversion != 20 && + conn->httpversion < 20 && (data->state.httpwant == CURL_HTTP_VERSION_2)) { /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done over SSL */ @@ -3236,8 +3170,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } result = Curl_http_cookies(data, conn, &req); +#ifdef USE_WEBSOCKETS if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) result = Curl_ws_request(data, &req); +#endif if(!result) result = Curl_add_timecondition(data, &req); if(!result) @@ -3282,7 +3218,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } } - if((conn->httpversion == 20) && data->req.upload_chunky) + if((conn->httpversion >= 20) && data->req.upload_chunky) /* upload_chunky was set above to set up the request in a chunky fashion, but is disabled here again to avoid that the chunked encoded version is actually used when sending the request body over h2 */ @@ -3669,7 +3605,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, #endif )) { /* the ALPN of the current request */ - enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + 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, @@ -3963,7 +3900,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* switch to http2 now. The bytes after response headers are also processed here, otherwise they are lost. */ - result = Curl_http2_switched(data, k->str, *nread); + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, + k->str, *nread); if(result) return result; *nread = 0; @@ -3971,7 +3909,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, #ifdef USE_WEBSOCKETS else if(k->upgr101 == UPGR101_WS) { /* verify the response */ - result = Curl_ws_accept(data); + result = Curl_ws_accept(data, k->str, *nread); if(result) return result; k->header = FALSE; /* no more header to parse! */ @@ -4191,11 +4129,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, stream. In order to do this, we keep reading until we close the stream. */ if(0 == k->maxdownload -#if defined(USE_NGHTTP2) - && !((conn->handler->protocol & PROTO_FAMILY_HTTP) && - conn->httpversion == 20) -#endif - ) + && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) + && !Curl_conn_is_http3(data, conn, FIRSTSOCKET)) *stop_reading = TRUE; if(*stop_reading) { @@ -4290,7 +4225,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } if(conn->httpversion < 20) { conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; - infof(data, "Mark bundle as not supporting multiuse"); } } else if(!nc) { diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index ecfe4ee..444abc0 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -24,6 +24,11 @@ * ***************************************************************************/ #include "curl_setup.h" + +#if defined(USE_MSH3) && !defined(_WIN32) +#include <pthread.h> +#endif + #include "ws.h" typedef enum { @@ -37,11 +42,7 @@ typedef enum { #ifndef CURL_DISABLE_HTTP -#ifdef USE_NGHTTP2 -#include <nghttp2/nghttp2.h> -#endif - -#if defined(_WIN32) && defined(ENABLE_QUIC) +#if defined(ENABLE_QUIC) || defined(USE_NGHTTP2) #include <stdint.h> #endif @@ -73,8 +74,10 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, const struct connectdata *conn, 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); @@ -179,29 +182,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); struct h3out; /* see ngtcp2 */ #endif -#ifdef USE_MSH3 -#ifdef _WIN32 -#define msh3_lock CRITICAL_SECTION -#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) -#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) -#define msh3_lock_acquire(lock) EnterCriticalSection(lock) -#define msh3_lock_release(lock) LeaveCriticalSection(lock) -#else /* !_WIN32 */ -#include <pthread.h> -#define msh3_lock pthread_mutex_t -#define msh3_lock_initialize(lock) { \ - pthread_mutexattr_t attr; \ - pthread_mutexattr_init(&attr); \ - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ - pthread_mutex_init(lock, &attr); \ - pthread_mutexattr_destroy(&attr); \ -} -#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) -#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) -#define msh3_lock_release(lock) pthread_mutex_unlock(lock) -#endif /* _WIN32 */ -#endif /* USE_MSH3 */ - /**************************************************************************** * HTTP unique setup ***************************************************************************/ @@ -220,6 +200,7 @@ struct HTTP { void *fread_in; /* backup storage for fread_in pointer */ const char *postdata; curl_off_t postsize; + struct Curl_easy *data; } backup; enum { @@ -258,7 +239,6 @@ struct HTTP { #if defined(USE_NGHTTP2) || defined(USE_NGHTTP3) bool bodystarted; int status_code; /* HTTP status code */ - bool closed; /* TRUE on HTTP2 stream close */ char *mem; /* points to a buffer in memory to store received data */ size_t len; /* size of the buffer 'mem' points to */ size_t memlen; /* size of data copied to mem */ @@ -268,6 +248,8 @@ struct HTTP { const uint8_t *upload_mem; /* points to a buffer to read from */ size_t upload_len; /* size of the buffer 'upload_mem' points to */ curl_off_t upload_left; /* number of bytes left to upload */ + bool closed; /* TRUE on stream close */ + bool reset; /* TRUE on stream reset */ #endif #ifdef ENABLE_QUIC @@ -278,20 +260,25 @@ struct HTTP { bool firstheader; /* FALSE until headers arrive */ bool firstbody; /* FALSE until body arrives */ bool h3req; /* FALSE until request is issued */ -#endif +#endif /* !USE_MSH3 */ bool upload_done; -#endif +#endif /* ENABLE_QUIC */ #ifdef USE_NGHTTP3 - size_t unacked_window; + size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ struct h3out *h3out; /* per-stream buffers for upload */ struct dynbuf overflow; /* excess data received during a single Curl_read */ -#endif +#endif /* USE_NGHTTP3 */ #ifdef USE_MSH3 struct MSH3_REQUEST *req; - msh3_lock recv_lock; +#ifdef _WIN32 + CRITICAL_SECTION recv_lock; +#else /* !_WIN32 */ + pthread_mutex_t recv_lock; +#endif /* _WIN32 */ /* Receive Buffer (Headers and Data) */ uint8_t* recv_buf; size_t recv_buf_alloc; + size_t recv_buf_max; /* Receive Headers */ size_t recv_header_len; bool recv_header_complete; @@ -300,53 +287,13 @@ struct HTTP { bool recv_data_complete; /* General Receive Error */ CURLcode recv_error; -#endif -}; - -#ifdef USE_NGHTTP2 -/* h2 settings for this connection */ -struct h2settings { - uint32_t max_concurrent_streams; - bool enable_push; -}; -#endif - -struct http_conn { -#ifdef USE_NGHTTP2 -#define H2_BINSETTINGS_LEN 80 - uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ - - /* We associate the connectdata struct with the connection, but we need to - make sure we can identify the current "driving" transfer. This is a - work-around for the lack of nghttp2_session_set_user_data() in older - nghttp2 versions that we want to support. (Added in 1.31.0) */ - struct Curl_easy *trnsfr; - - nghttp2_session *h2; - Curl_send *send_underlying; /* underlying send Curl_send callback */ - Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */ - char *inbuf; /* buffer to receive data from underlying socket */ - size_t inbuflen; /* number of bytes filled in inbuf */ - size_t nread_inbuf; /* number of bytes read from in inbuf */ - /* We need separate buffer for transmission and reception because we - may call nghttp2_session_send() after the - nghttp2_session_mem_recv() but mem buffer is still not full. In - this case, we wrongly sends the content of mem buffer if we share - them for both cases. */ - int32_t pause_stream_id; /* stream ID which paused - nghttp2_session_mem_recv */ - size_t drain_total; /* sum of all stream's UrlState.drain */ - - /* this is a hash of all individual streams (Curl_easy structs) */ - struct h2settings settings; - - /* list of settings that will be sent */ - nghttp2_settings_entry local_settings[3]; - size_t local_settings_num; -#else - int unused; /* prevent a compiler warning */ -#endif +#endif /* USE_MSH3 */ +#ifdef USE_QUICHE + bool h3_got_header; /* TRUE when h3 stream has recvd some HEADER */ + bool h3_recving_data; /* TRUE when h3 stream is reading DATA */ + bool h3_body_pending; /* TRUE when h3 stream may have more body DATA */ + struct h3_event_node *pending; +#endif /* USE_QUICHE */ }; CURLcode Curl_http_size(struct Curl_easy *data); diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index b9d3245..bdb5e73 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -35,6 +35,7 @@ #include "strcase.h" #include "multiif.h" #include "url.h" +#include "cfilters.h" #include "connect.h" #include "strtoofft.h" #include "strdup.h" @@ -63,124 +64,322 @@ #define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ -#ifdef DEBUG_HTTP2 -#define H2BUGF(x) x -#else -#define H2BUGF(x) do { } while(0) -#endif -static ssize_t http2_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err); -static bool http2_connisdead(struct Curl_easy *data, - struct connectdata *conn); -static int h2_session_send(struct Curl_easy *data, - nghttp2_session *h2); -static int h2_process_pending_input(struct Curl_easy *data, - struct http_conn *httpc, - CURLcode *err); +#define H2_SETTINGS_IV_LEN 3 +#define H2_BINSETTINGS_LEN 80 -/* - * Curl_http2_init_state() is called when the easy handle is created and - * allows for HTTP/2 specific init of state. - */ -void Curl_http2_init_state(struct UrlState *state) +static int populate_settings(nghttp2_settings_entry *iv, + struct Curl_easy *data) { - state->stream_weight = NGHTTP2_DEFAULT_WEIGHT; + iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = Curl_multi_max_concurrent_streams(data->multi); + + iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = HTTP2_HUGE_WINDOW_SIZE; + + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = data->multi->push_cb != NULL; + + return 3; +} + +static size_t populate_binsettings(uint8_t *binsettings, + struct Curl_easy *data) +{ + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + int ivlen; + + ivlen = populate_settings(iv, data); + /* this returns number of bytes it wrote */ + return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, + iv, ivlen); +} + +struct cf_h2_ctx { + nghttp2_session *h2; + uint32_t max_concurrent_streams; + bool enable_push; + /* The easy handle used in the current filter call, cleared at return */ + struct cf_call_data call_data; + + char *inbuf; /* buffer to receive data from underlying socket */ + size_t inbuflen; /* number of bytes filled in inbuf */ + size_t nread_inbuf; /* number of bytes read from in inbuf */ + + struct dynbuf outbuf; + + /* We need separate buffer for transmission and reception because we + may call nghttp2_session_send() after the + nghttp2_session_mem_recv() but mem buffer is still not full. In + this case, we wrongly sends the content of mem buffer if we share + them for both cases. */ + int32_t pause_stream_id; /* stream ID which paused + nghttp2_session_mem_recv */ + size_t drain_total; /* sum of all stream's UrlState.drain */ +}; + +/* How to access `call_data` from a cf_h2 filter */ +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_h2_ctx *)(cf)->ctx)->call_data + + +static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(ctx->h2) { + nghttp2_session_del(ctx->h2); + } + free(ctx->inbuf); + Curl_dyn_free(&ctx->outbuf); + memset(ctx, 0, sizeof(*ctx)); + ctx->call_data = save; +} + +static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) +{ + if(ctx) { + cf_h2_ctx_clear(ctx); + free(ctx); + } +} + +static int h2_client_new(struct Curl_cfilter *cf, + nghttp2_session_callbacks *cbs) +{ + struct cf_h2_ctx *ctx = cf->ctx; + +#if NGHTTP2_VERSION_NUM < 0x013200 + /* before 1.50.0 */ + return nghttp2_session_client_new(&ctx->h2, cbs, cf); +#else + nghttp2_option *o; + int rc = nghttp2_option_new(&o); + if(rc) + return rc; + /* turn off RFC 9113 leading and trailing white spaces validation against + HTTP field value. */ + nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); + rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); + nghttp2_option_del(o); + return rc; +#endif } +static ssize_t send_callback(nghttp2_session *h2, + const uint8_t *mem, size_t length, int flags, + void *userp); +static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, + void *userp); +static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const uint8_t *mem, size_t len, void *userp); +static int on_stream_close(nghttp2_session *session, int32_t stream_id, + uint32_t error_code, void *userp); +static int on_begin_headers(nghttp2_session *session, + const nghttp2_frame *frame, void *userp); +static int on_header(nghttp2_session *session, const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, + void *userp); +static int error_callback(nghttp2_session *session, const char *msg, + size_t len, void *userp); + /* - * Curl_http2_init_userset() is called when the easy handle is created and - * allows for HTTP/2 specific user-set fields. + * multi_connchanged() is called to tell that there is a connection in + * this multi handle that has changed state (multiplexing become possible, the + * number of allowed streams changed or similar), and a subsequent use of this + * multi handle should move CONNECT_PEND handles back to CONNECT to have them + * retry. */ -void Curl_http2_init_userset(struct UserDefined *set) +static void multi_connchanged(struct Curl_multi *multi) { - set->stream_weight = NGHTTP2_DEFAULT_WEIGHT; + multi->recheckstate = TRUE; } -static int http2_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) +static CURLcode http2_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) { - const struct http_conn *c = &conn->proto.httpc; - struct SingleRequest *k = &data->req; - int bitmap = GETSOCK_BLANK; struct HTTP *stream = data->req.p.http; - sock[0] = conn->sock[FIRSTSOCKET]; + (void)cf; + DEBUGASSERT(stream); + DEBUGASSERT(data->state.buffer); - if(!(k->keepon & KEEP_RECV_PAUSE)) - /* Unless paused - in an HTTP/2 connection we can basically always get a - frame so we should always be ready for one */ - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); + stream->stream_id = -1; - /* we're (still uploading OR the HTTP/2 layer wants to send data) AND - there's a window to send data in */ - if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || - nghttp2_session_want_write(c->h2)) && - (nghttp2_session_get_remote_window_size(c->h2) && - nghttp2_session_get_stream_remote_window_size(c->h2, - stream->stream_id))) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); + Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS); + Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS); - return bitmap; + stream->bodystarted = FALSE; + stream->status_code = -1; + stream->pausedata = NULL; + stream->pauselen = 0; + stream->closed = FALSE; + stream->close_handled = FALSE; + stream->memlen = 0; + stream->error = NGHTTP2_NO_ERROR; + stream->upload_left = 0; + stream->upload_mem = NULL; + stream->upload_len = 0; + stream->mem = data->state.buffer; + stream->len = data->set.buffer_size; + + return CURLE_OK; } /* - * http2_stream_free() free HTTP2 stream related data + * Initialize the cfilter context */ -static void http2_stream_free(struct HTTP *http) +static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool via_h1_upgrade) { - if(http) { - Curl_dyn_free(&http->header_recvbuf); - for(; http->push_headers_used > 0; --http->push_headers_used) { - free(http->push_headers[http->push_headers_used - 1]); + struct cf_h2_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OUT_OF_MEMORY; + int rc; + nghttp2_session_callbacks *cbs = NULL; + + DEBUGASSERT(!ctx->h2); + ctx->inbuf = malloc(H2_BUFSIZE); + if(!ctx->inbuf) + goto out; + /* we want to aggregate small frames, SETTINGS, PRIO, UPDATES */ + Curl_dyn_init(&ctx->outbuf, 4*1024); + + rc = nghttp2_session_callbacks_new(&cbs); + if(rc) { + failf(data, "Couldn't initialize nghttp2 callbacks"); + goto out; + } + + nghttp2_session_callbacks_set_send_callback(cbs, send_callback); + nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); + nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + cbs, on_data_chunk_recv); + nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); + nghttp2_session_callbacks_set_on_begin_headers_callback( + cbs, on_begin_headers); + nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); + nghttp2_session_callbacks_set_error_callback(cbs, error_callback); + + /* The nghttp2 session is not yet setup, do it */ + rc = h2_client_new(cf, cbs); + if(rc) { + failf(data, "Couldn't initialize nghttp2"); + goto out; + } + ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; + + result = http2_data_setup(cf, data); + if(result) + goto out; + + if(via_h1_upgrade) { + /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted + * in the H1 request and we upgrade from there. This stream + * is opened implicitly as #1. */ + uint8_t binsettings[H2_BINSETTINGS_LEN]; + size_t binlen; /* length of the binsettings data */ + + binlen = populate_binsettings(binsettings, data); + + stream->stream_id = 1; + /* queue SETTINGS frame (again) */ + rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, + data->state.httpreq == HTTPREQ_HEAD, + NULL); + if(rc) { + failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + + rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->stream_id, + data); + if(rc) { + infof(data, "http/2: failed to set user_data for stream %u", + stream->stream_id); + DEBUGASSERT(0); } - free(http->push_headers); - http->push_headers = NULL; } -} + else { + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + int ivlen; -/* - * Disconnects *a* connection used for HTTP/2. It might be an old one from the - * connection cache and not the "main" one. Don't touch the easy handle! - */ + /* H2 Settings need to be submitted. Stream is not open yet. */ + DEBUGASSERT(stream->stream_id == -1); -static CURLcode http2_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct http_conn *c = &conn->proto.httpc; - (void)dead_connection; -#ifndef DEBUG_HTTP2 - (void)data; -#endif + ivlen = populate_settings(iv, data); + rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, + iv, ivlen); + if(rc) { + failf(data, "nghttp2_submit_settings() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } + } - H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now")); + rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, + HTTP2_HUGE_WINDOW_SIZE); + if(rc) { + failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", + nghttp2_strerror(rc), rc); + result = CURLE_HTTP2; + goto out; + } - nghttp2_session_del(c->h2); - Curl_safefree(c->inbuf); + /* all set, traffic will be send on connect */ + result = CURLE_OK; - H2BUGF(infof(data, "HTTP/2 DISCONNECT done")); +out: + if(cbs) + nghttp2_session_callbacks_del(cbs); + return result; +} - return CURLE_OK; +static CURLcode h2_session_send(struct Curl_cfilter *cf, + struct Curl_easy *data); +static int h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err); + +/* + * http2_stream_free() free HTTP2 stream related data + */ +static void http2_stream_free(struct HTTP *stream) +{ + if(stream) { + Curl_dyn_free(&stream->header_recvbuf); + 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; + } } /* * The server may send us data at any point (e.g. PING frames). Therefore, * we cannot assume that an HTTP/2 socket is dead just because it is readable. * - * Instead, if it is readable, run Curl_connalive() to peek at the socket + * Check the lower filters first and, if successful, peek at the socket * and distinguish between closed and data. */ -static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn) +static bool http2_connisdead(struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_h2_ctx *ctx = cf->ctx; int sval; bool dead = TRUE; - if(conn->bits.close) + if(!cf->next || !cf->next->cft->is_alive(cf->next, data)) return TRUE; - sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0); + sval = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); if(sval == 0) { /* timeout */ dead = FALSE; @@ -190,177 +389,57 @@ static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn) dead = TRUE; } else if(sval & CURL_CSELECT_IN) { - /* readable with no error. could still be closed */ - dead = !Curl_connalive(data, conn); - if(!dead) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, - only "protocol frames" */ - CURLcode result; - struct http_conn *httpc = &conn->proto.httpc; - ssize_t nread = -1; - if(httpc->recv_underlying) - /* if called "too early", this pointer isn't setup yet! */ - nread = ((Curl_recv *)httpc->recv_underlying)( - data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); - if(nread != -1) { - H2BUGF(infof(data, - "%d bytes stray data read before trying h2 connection", - (int)nread)); - httpc->nread_inbuf = 0; - httpc->inbuflen = nread; - if(h2_process_pending_input(data, httpc, &result) < 0) - /* immediate error, considered dead */ - dead = TRUE; - } - else - /* the read failed so let's say this is dead anyway */ + /* This happens before we've sent off a request and the connection is + not in use by any other transfer, there shouldn't be any data here, + only "protocol frames" */ + CURLcode result; + ssize_t nread = -1; + + Curl_attach_connection(data, cf->conn); + nread = Curl_conn_cf_recv(cf->next, data, + ctx->inbuf, H2_BUFSIZE, &result); + dead = FALSE; + if(nread != -1) { + DEBUGF(LOG_CF(data, cf, "%d bytes stray data read before trying " + "h2 connection", (int)nread)); + ctx->nread_inbuf = 0; + ctx->inbuflen = nread; + if(h2_process_pending_input(cf, data, &result) < 0) + /* immediate error, considered dead */ dead = TRUE; } + else + /* the read failed so let's say this is dead anyway */ + dead = TRUE; + Curl_detach_connection(data); } return dead; } -/* - * Set the transfer that is currently using this HTTP/2 connection. - */ -static void set_transfer(struct http_conn *c, - struct Curl_easy *data) -{ - c->trnsfr = data; -} - -/* - * Get the transfer that is currently using this HTTP/2 connection. - */ -static struct Curl_easy *get_transfer(struct http_conn *c) -{ - DEBUGASSERT(c && c->trnsfr); - return c->trnsfr; -} - -static unsigned int http2_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) +static CURLcode http2_send_ping(struct Curl_cfilter *cf, + struct Curl_easy *data) { - unsigned int ret_val = CONNRESULT_NONE; - struct http_conn *c = &conn->proto.httpc; + struct cf_h2_ctx *ctx = cf->ctx; int rc; - bool send_frames = false; - if(checks_to_perform & CONNCHECK_ISDEAD) { - if(http2_connisdead(data, conn)) - ret_val |= CONNRESULT_DEAD; + rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL); + if(rc) { + failf(data, "nghttp2_submit_ping() failed: %s(%d)", + nghttp2_strerror(rc), rc); + return CURLE_HTTP2; } - if(checks_to_perform & CONNCHECK_KEEPALIVE) { - struct curltime now = Curl_now(); - timediff_t elapsed = Curl_timediff(now, conn->keepalive); - - if(elapsed > data->set.upkeep_interval_ms) { - /* Perform an HTTP/2 PING */ - rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL); - if(!rc) { - /* Successfully added a PING frame to the session. Need to flag this - so the frame is sent. */ - send_frames = true; - } - else { - failf(data, "nghttp2_submit_ping() failed: %s(%d)", - nghttp2_strerror(rc), rc); - } - - conn->keepalive = now; - } + rc = nghttp2_session_send(ctx->h2); + if(rc) { + failf(data, "nghttp2_session_send() failed: %s(%d)", + nghttp2_strerror(rc), rc); + return CURLE_SEND_ERROR; } - - if(send_frames) { - set_transfer(c, data); /* set the transfer */ - rc = nghttp2_session_send(c->h2); - if(rc) - failf(data, "nghttp2_session_send() failed: %s(%d)", - nghttp2_strerror(rc), rc); - } - - return ret_val; -} - -/* called from http_setup_conn */ -void Curl_http2_setup_req(struct Curl_easy *data) -{ - struct HTTP *http = data->req.p.http; - http->bodystarted = FALSE; - http->status_code = -1; - http->pausedata = NULL; - http->pauselen = 0; - http->closed = FALSE; - http->close_handled = FALSE; - http->mem = NULL; - http->len = 0; - http->memlen = 0; - http->error = NGHTTP2_NO_ERROR; -} - -/* called from http_setup_conn */ -void Curl_http2_setup_conn(struct connectdata *conn) -{ - conn->proto.httpc.settings.max_concurrent_streams = - DEFAULT_MAX_CONCURRENT_STREAMS; + return CURLE_OK; } /* - * HTTP2 handler interface. This isn't added to the general list of protocols - * but will be used at run-time when the protocol is dynamically switched from - * HTTP to HTTP2. - */ -static const struct Curl_handler Curl_handler_http2 = { - "HTTP", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - http2_getsock, /* proto_getsock */ - http2_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - http2_getsock, /* perform_getsock */ - http2_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - http2_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTP, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_STREAM /* flags */ -}; - -static const struct Curl_handler Curl_handler_http2_ssl = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - http2_getsock, /* proto_getsock */ - http2_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - http2_getsock, /* perform_getsock */ - http2_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - http2_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -/* * Store nghttp2 version info in this buffer. */ void Curl_http2_ver(char *p, size_t len) @@ -369,31 +448,75 @@ void Curl_http2_ver(char *p, size_t len) (void)msnprintf(p, len, "nghttp2/%s", h2->version_str); } +static CURLcode flush_output(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + size_t buflen = Curl_dyn_len(&ctx->outbuf); + ssize_t written; + CURLcode result; + + if(!buflen) + return CURLE_OK; + + DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen)); + written = Curl_conn_cf_send(cf->next, data, Curl_dyn_ptr(&ctx->outbuf), + buflen, &result); + if(written < 0) { + return result; + } + if((size_t)written < buflen) { + Curl_dyn_tail(&ctx->outbuf, buflen - (size_t)written); + return CURLE_AGAIN; + } + else { + Curl_dyn_reset(&ctx->outbuf); + } + return CURLE_OK; +} + /* * The implementation of nghttp2_send_callback type. Here we write |data| with * size |length| to the network and return the number of bytes actually * written. See the documentation of nghttp2_send_callback for the details. */ static ssize_t send_callback(nghttp2_session *h2, - const uint8_t *mem, size_t length, int flags, + const uint8_t *buf, size_t blen, int flags, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; - struct Curl_easy *data = get_transfer(c); + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t written; CURLcode result = CURLE_OK; + size_t buflen = Curl_dyn_len(&ctx->outbuf); (void)h2; (void)flags; + DEBUGASSERT(data); - if(!c->send_underlying) - /* called before setup properly! */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - - written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET, - mem, length, &result); + if(blen < 1024 && (buflen + blen + 1 < ctx->outbuf.toobig)) { + result = Curl_dyn_addn(&ctx->outbuf, buf, blen); + if(result) { + failf(data, "Failed to add data to output buffer"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return blen; + } + if(buflen) { + /* not adding, flush buffer */ + result = flush_output(cf, data); + if(result) { + if(result == CURLE_AGAIN) { + return NGHTTP2_ERR_WOULDBLOCK; + } + failf(data, "Failed sending HTTP2 data"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + DEBUGF(LOG_CF(data, cf, "h2 conn send %zu bytes", blen)); + written = Curl_conn_cf_send(cf->next, data, buf, blen, &result); if(result == CURLE_AGAIN) { return NGHTTP2_ERR_WOULDBLOCK; } @@ -467,26 +590,33 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) /* * This specific transfer on this connection has been "drained". */ -static void drained_transfer(struct Curl_easy *data, - struct http_conn *httpc) +static void drained_transfer(struct Curl_cfilter *cf, + struct Curl_easy *data) { - DEBUGASSERT(httpc->drain_total >= data->state.drain); - httpc->drain_total -= data->state.drain; - data->state.drain = 0; + if(data->state.drain) { + struct cf_h2_ctx *ctx = cf->ctx; + DEBUGASSERT(ctx->drain_total > 0); + ctx->drain_total--; + data->state.drain = 0; + } } /* * Mark this transfer to get "drained". */ -static void drain_this(struct Curl_easy *data, - struct http_conn *httpc) +static void drain_this(struct Curl_cfilter *cf, + struct Curl_easy *data) { - data->state.drain++; - httpc->drain_total++; - DEBUGASSERT(httpc->drain_total >= data->state.drain); + if(!data->state.drain) { + struct cf_h2_ctx *ctx = cf->ctx; + data->state.drain = 1; + ctx->drain_total++; + DEBUGASSERT(ctx->drain_total > 0); + } } -static struct Curl_easy *duphandle(struct Curl_easy *data) +static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct Curl_easy *second = curl_easy_duphandle(data); if(second) { @@ -497,9 +627,8 @@ static struct Curl_easy *duphandle(struct Curl_easy *data) } else { second->req.p.http = http; - Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS); - Curl_http2_setup_req(second); - second->state.stream_weight = data->state.stream_weight; + http2_data_setup(cf, second); + second->state.priority.weight = data->state.priority.weight; } } return second; @@ -559,22 +688,23 @@ static int set_transfer_url(struct Curl_easy *data, return 0; } -static int push_promise(struct Curl_easy *data, - struct connectdata *conn, +static int push_promise(struct Curl_cfilter *cf, + struct Curl_easy *data, const nghttp2_push_promise *frame) { + struct cf_h2_ctx *ctx = cf->ctx; int rv; /* one of the CURL_PUSH_* defines */ - H2BUGF(infof(data, "PUSH_PROMISE received, stream %u", - frame->promised_stream_id)); + + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] PUSH_PROMISE received", + frame->promised_stream_id)); if(data->multi->push_cb) { struct HTTP *stream; struct HTTP *newstream; struct curl_pushheaders heads; CURLMcode rc; - struct http_conn *httpc; size_t i; /* clone the parent */ - struct Curl_easy *newhandle = duphandle(data); + struct Curl_easy *newhandle = h2_duphandle(cf, data); if(!newhandle) { infof(data, "failed to duplicate handle"); rv = CURL_PUSH_DENY; /* FAIL HARD */ @@ -584,7 +714,7 @@ static int push_promise(struct Curl_easy *data, heads.data = data; heads.frame = frame; /* ask the application */ - H2BUGF(infof(data, "Got PUSH_PROMISE, ask application")); + DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application")); stream = data->req.p.http; if(!stream) { @@ -630,7 +760,7 @@ static int push_promise(struct Curl_easy *data, /* approved, add to the multi handle and immediately switch to PERFORM state with the given connection !*/ - rc = Curl_multi_add_perform(data->multi, newhandle, conn); + rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn); if(rc) { infof(data, "failed to add handle to multi"); http2_stream_free(newhandle->req.p.http); @@ -640,8 +770,7 @@ static int push_promise(struct Curl_easy *data, goto fail; } - httpc = &conn->proto.httpc; - rv = nghttp2_session_set_stream_user_data(httpc->h2, + rv = nghttp2_session_set_stream_user_data(ctx->h2, frame->promised_stream_id, newhandle); if(rv) { @@ -655,84 +784,82 @@ static int push_promise(struct Curl_easy *data, Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS); } else { - H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it")); + DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it")); rv = CURL_PUSH_DENY; } fail: return rv; } -/* - * multi_connchanged() is called to tell that there is a connection in - * this multi handle that has changed state (multiplexing become possible, the - * number of allowed streams changed or similar), and a subsequent use of this - * multi handle should move CONNECT_PEND handles back to CONNECT to have them - * retry. - */ -static void multi_connchanged(struct Curl_multi *multi) -{ - multi->recheckstate = TRUE; -} - static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; struct Curl_easy *data_s = NULL; struct HTTP *stream = NULL; - struct Curl_easy *data = get_transfer(httpc); + struct Curl_easy *data = CF_DATA_CURRENT(cf); int rv; size_t left, ncopy; int32_t stream_id = frame->hd.stream_id; CURLcode result; + DEBUGASSERT(data); if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ - if(frame->hd.type == NGHTTP2_SETTINGS) { - uint32_t max_conn = httpc->settings.max_concurrent_streams; - H2BUGF(infof(data, "Got SETTINGS")); - httpc->settings.max_concurrent_streams = - nghttp2_session_get_remote_settings( + DEBUGASSERT(data); + switch(frame->hd.type) { + case NGHTTP2_SETTINGS: { + uint32_t max_conn = ctx->max_concurrent_streams; + DEBUGF(LOG_CF(data, cf, "recv frame SETTINGS")); + ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - httpc->settings.enable_push = - nghttp2_session_get_remote_settings( + ctx->enable_push = nghttp2_session_get_remote_settings( session, NGHTTP2_SETTINGS_ENABLE_PUSH); - H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d", - httpc->settings.max_concurrent_streams)); - H2BUGF(infof(data, "ENABLE_PUSH == %s", - httpc->settings.enable_push?"TRUE":"false")); - if(max_conn != httpc->settings.max_concurrent_streams) { + DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d", + ctx->max_concurrent_streams)); + DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s", + ctx->enable_push ? "TRUE" : "false")); + if(data && max_conn != ctx->max_concurrent_streams) { /* only signal change if the value actually changed */ - infof(data, - "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!", - httpc->settings.max_concurrent_streams); + DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u", + ctx->max_concurrent_streams)); multi_connchanged(data->multi); } + break; + } + case NGHTTP2_GOAWAY: + if(data) { + infof(data, "recveived GOAWAY, error=%d, last_stream=%u", + frame->goaway.error_code, frame->goaway.last_stream_id); + multi_connchanged(data->multi); + } + break; + case NGHTTP2_WINDOW_UPDATE: + DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE")); + break; + default: + DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); } return 0; } data_s = nghttp2_session_get_stream_user_data(session, stream_id); if(!data_s) { - H2BUGF(infof(data, - "No Curl_easy associated with stream: %u", - stream_id)); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] No Curl_easy associated", + stream_id)); return 0; } stream = data_s->req.p.http; if(!stream) { - H2BUGF(infof(data_s, "No proto pointer for stream: %u", - stream_id)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] No proto pointer", stream_id)); return NGHTTP2_ERR_CALLBACK_FAILURE; } - H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u", - frame->hd.type, stream_id)); - switch(frame->hd.type) { case NGHTTP2_DATA: /* If body started on this stream, then receiving DATA is illegal. */ + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame DATA", stream_id)); if(!stream->bodystarted) { rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_PROTOCOL_ERROR); @@ -741,8 +868,17 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, return NGHTTP2_ERR_CALLBACK_FAILURE; } } + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + /* Stream has ended. If there is pending data, ensure that read + will occur to consume it. */ + if(!data->state.drain && stream->memlen) { + drain_this(cf, data_s); + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + } break; case NGHTTP2_HEADERS: + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame HEADERS", stream_id)); if(stream->bodystarted) { /* Only valid HEADERS after body started is trailer HEADERS. We buffer them in on_header callback. */ @@ -776,19 +912,18 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, stream->nread_header_recvbuf += ncopy; DEBUGASSERT(stream->mem); - H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p", - ncopy, stream_id, stream->mem)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu header bytes, at %p", + stream_id, ncopy, (void *)stream->mem)); stream->len -= ncopy; stream->memlen += ncopy; - drain_this(data_s, httpc); - /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + drain_this(cf, data_s); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); break; case NGHTTP2_PUSH_PROMISE: - rv = push_promise(data_s, conn, &frame->push_promise); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id)); + rv = push_promise(cf, data_s, &frame->push_promise); if(rv) { /* deny! */ int h2; DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); @@ -798,14 +933,18 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, if(nghttp2_is_fatal(h2)) return NGHTTP2_ERR_CALLBACK_FAILURE; else if(rv == CURL_PUSH_ERROROUT) { - DEBUGF(infof(data_s, "Fail the parent stream (too)")); + DEBUGF(LOG_CF(data_s, cf, "Fail the parent stream (too)")); return NGHTTP2_ERR_CALLBACK_FAILURE; } } break; + case NGHTTP2_RST_STREAM: + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv RST", stream_id)); + stream->reset = TRUE; + break; default: - H2BUGF(infof(data_s, "Got frame type %x for stream %u", - frame->hd.type, stream_id)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame %x", + stream_id, frame->hd.type)); break; } return 0; @@ -815,15 +954,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *mem, size_t len, void *userp) { + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; struct HTTP *stream; struct Curl_easy *data_s; size_t nread; - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; - (void)session; (void)flags; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ + DEBUGASSERT(CF_DATA_CURRENT(cf)); /* get the stream from the hash based on Stream ID */ data_s = nghttp2_session_get_stream_user_data(session, stream_id); @@ -831,8 +970,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* Receiving a Stream ID not in the hash should not happen - unless we have aborted a transfer artificially and there were more data in the pipeline. Silently ignore. */ - H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n", - stream_id)); + DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%u] Data for unknown", + stream_id)); return 0; } @@ -846,36 +985,38 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, stream->len -= nread; stream->memlen += nread; - drain_this(data_s, &conn->proto.httpc); - /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) + if(CF_DATA_CURRENT(cf) != data_s) { + drain_this(cf, data_s); Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + } - H2BUGF(infof(data_s, "%zu data received for stream %u " - "(%zu left in buffer %p, total %zu)", - nread, stream_id, - stream->len, stream->mem, - stream->memlen)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu DATA recvd, " + "(buffer now holds %zu, %zu still free in %p)", + stream_id, nread, + stream->memlen, stream->len, (void *)stream->mem)); if(nread < len) { stream->pausedata = mem + nread; stream->pauselen = len - nread; - H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" - ", stream %u", - len - nread, stream_id)); - data_s->conn->proto.httpc.pause_stream_id = stream_id; - + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu not recvd -> NGHTTP2_ERR_PAUSE", + stream_id, len - nread)); + ctx->pause_stream_id = stream_id; + drain_this(cf, data_s); return NGHTTP2_ERR_PAUSE; } +#if 0 /* pause execution of nghttp2 if we received data for another handle in order to process them first. */ - if(get_transfer(httpc) != data_s) { - data_s->conn->proto.httpc.pause_stream_id = stream_id; - + if(CF_DATA_CURRENT(cf) != data_s) { + ctx->pause_stream_id = stream_id; + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] not call_data -> NGHTTP2_ERR_PAUSE", + stream_id)); + drain_this(cf, data_s); return NGHTTP2_ERR_PAUSE; } +#endif return 0; } @@ -883,15 +1024,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, static int on_stream_close(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *userp) { + struct Curl_cfilter *cf = userp; + struct cf_h2_ctx *ctx = cf->ctx; struct Curl_easy *data_s; struct HTTP *stream; - struct connectdata *conn = (struct connectdata *)userp; int rv; (void)session; (void)stream_id; if(stream_id) { - struct http_conn *httpc; /* get the stream from the hash based on Stream ID, stream ID zero is for connection-oriented stuff */ data_s = nghttp2_session_get_stream_user_data(session, stream_id); @@ -900,16 +1041,17 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, decided to reject stream (e.g., PUSH_PROMISE). */ return 0; } - H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u", - nghttp2_http2_strerror(error_code), error_code, stream_id)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] on_stream_close(), %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code)); stream = data_s->req.p.http; if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; stream->closed = TRUE; - httpc = &conn->proto.httpc; - drain_this(data_s, httpc); - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + if(CF_DATA_CURRENT(cf) != data_s) { + drain_this(cf, data_s); + Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + } stream->error = error_code; /* remove the entry from the hash as the stream is now gone */ @@ -919,12 +1061,12 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream_id); DEBUGASSERT(0); } - if(stream_id == httpc->pause_stream_id) { - H2BUGF(infof(data_s, "Stopped the pause stream")); - httpc->pause_stream_id = 0; + if(stream_id == ctx->pause_stream_id) { + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed the pause stream", + stream_id)); + ctx->pause_stream_id = 0; } - H2BUGF(infof(data_s, "Removed stream %u hash", stream_id)); - stream->stream_id = 0; /* cleared */ + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed, cleared", stream_id)); } return 0; } @@ -932,16 +1074,17 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, static int on_begin_headers(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { + struct Curl_cfilter *cf = userp; struct HTTP *stream; struct Curl_easy *data_s = NULL; - (void)userp; + (void)cf; data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if(!data_s) { return 0; } - H2BUGF(infof(data_s, "on_begin_headers() was called")); + DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called")); if(frame->hd.type != NGHTTP2_HEADERS) { return 0; @@ -989,11 +1132,10 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, uint8_t flags, void *userp) { + struct Curl_cfilter *cf = userp; struct HTTP *stream; struct Curl_easy *data_s; int32_t stream_id = frame->hd.stream_id; - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; CURLcode result; (void)flags; @@ -1020,13 +1162,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) { /* pseudo headers are lower case */ int rc = 0; - char *check = aprintf("%s:%d", conn->host.name, conn->remote_port); + char *check = aprintf("%s:%d", cf->conn->host.name, + cf->conn->remote_port); if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; if(!strcasecompare(check, (const char *)value) && - ((conn->remote_port != conn->given->defport) || - !strcasecompare(conn->host.name, (const char *)value))) { + ((cf->conn->remote_port != cf->conn->given->defport) || + !strcasecompare(cf->conn->host.name, (const char *)value))) { /* This is push is not for the same authority that was asked for in * the URL. RFC 7540 section 8.2 says: "A client MUST treat a * PUSH_PROMISE for which the server is not authoritative as a stream @@ -1075,11 +1218,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen, - value)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] trailer: %.*s: %.*s", + stream->stream_id, + (int)namelen, name, + (int)valuelen, value)); result = Curl_dyn_addf(&stream->trailer_recvbuf, - "%.*s: %.*s\r\n", namelen, name, - valuelen, value); + "%.*s: %.*s\r\n", (int)namelen, name, + (int)valuelen, value); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1110,11 +1255,11 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) + if(CF_DATA_CURRENT(cf) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)", - stream->status_code, data_s)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] status: HTTP/2 %03d", + stream->stream_id, stream->status_code)); return 0; } @@ -1134,11 +1279,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) + if(CF_DATA_CURRENT(cf) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen, - value)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] header: %.*s: %.*s", + stream->stream_id, + (int)namelen, name, + (int)valuelen, value)); return 0; /* 0 is successful */ } @@ -1150,12 +1297,13 @@ static ssize_t data_source_read_callback(nghttp2_session *session, nghttp2_data_source *source, void *userp) { + struct Curl_cfilter *cf = userp; struct Curl_easy *data_s; struct HTTP *stream = NULL; size_t nread; (void)source; - (void)userp; + (void)cf; if(stream_id) { /* get the stream from the hash based on Stream ID, stream ID zero is for connection-oriented stuff */ @@ -1186,9 +1334,8 @@ static ssize_t data_source_read_callback(nghttp2_session *session, else if(nread == 0) return NGHTTP2_ERR_DEFERRED; - H2BUGF(infof(data_s, "data_source_read_callback: " - "returns %zu bytes stream %u", - nread, stream_id)); + DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] data_source_read_callback: " + "returns %zu bytes", stream_id, nread)); return nread; } @@ -1207,149 +1354,58 @@ static int error_callback(nghttp2_session *session, } #endif -static void populate_settings(struct Curl_easy *data, - struct http_conn *httpc) +static void http2_data_done(struct Curl_cfilter *cf, + struct Curl_easy *data, bool premature) { - nghttp2_settings_entry *iv = httpc->local_settings; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = Curl_multi_max_concurrent_streams(data->multi); - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = HTTP2_HUGE_WINDOW_SIZE; - - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = data->multi->push_cb != NULL; - - httpc->local_settings_num = 3; -} - -void Curl_http2_done(struct Curl_easy *data, bool premature) -{ - struct HTTP *http = data->req.p.http; - struct http_conn *httpc = &data->conn->proto.httpc; + struct cf_h2_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; /* there might be allocated resources done before this got the 'h2' pointer setup */ - Curl_dyn_free(&http->header_recvbuf); - Curl_dyn_free(&http->trailer_recvbuf); - if(http->push_headers) { + Curl_dyn_free(&stream->header_recvbuf); + Curl_dyn_free(&stream->trailer_recvbuf); + if(stream->push_headers) { /* if they weren't used and then freed before */ - for(; http->push_headers_used > 0; --http->push_headers_used) { - free(http->push_headers[http->push_headers_used - 1]); + for(; stream->push_headers_used > 0; --stream->push_headers_used) { + free(stream->push_headers[stream->push_headers_used - 1]); } - free(http->push_headers); - http->push_headers = NULL; + free(stream->push_headers); + stream->push_headers = NULL; } - if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) || - !httpc->h2) /* not HTTP/2 ? */ + if(!ctx || !ctx->h2) return; /* do this before the reset handling, as that might clear ->stream_id */ - if(http->stream_id == httpc->pause_stream_id) { - H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id)); - httpc->pause_stream_id = 0; + if(stream->stream_id && stream->stream_id == ctx->pause_stream_id) { + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] DONE, the pause stream", + stream->stream_id)); + ctx->pause_stream_id = 0; } - if(premature || (!http->closed && http->stream_id)) { + + if(premature || (!stream->closed && stream->stream_id)) { /* RST_STREAM */ - set_transfer(httpc, data); /* set the transfer */ - H2BUGF(infof(data, "RST stream %u", http->stream_id)); - if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, - http->stream_id, NGHTTP2_STREAM_CLOSED)) - (void)nghttp2_session_send(httpc->h2); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] RST", stream->stream_id)); + if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + stream->stream_id, NGHTTP2_STREAM_CLOSED)) + (void)nghttp2_session_send(ctx->h2); } if(data->state.drain) - drained_transfer(data, httpc); + drained_transfer(cf, data); /* -1 means unassigned and 0 means cleared */ - if(http->stream_id > 0) { - int rv = nghttp2_session_set_stream_user_data(httpc->h2, - http->stream_id, 0); + if(nghttp2_session_get_stream_user_data(ctx->h2, stream->stream_id)) { + int rv = nghttp2_session_set_stream_user_data(ctx->h2, + stream->stream_id, 0); if(rv) { infof(data, "http/2: failed to clear user_data for stream %u", - http->stream_id); + stream->stream_id); DEBUGASSERT(0); } - set_transfer(httpc, NULL); - http->stream_id = 0; } } -static int client_new(struct connectdata *conn, - nghttp2_session_callbacks *callbacks) -{ -#if NGHTTP2_VERSION_NUM < 0x013200 - /* before 1.50.0 */ - return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); -#else - nghttp2_option *o; - int rc = nghttp2_option_new(&o); - if(rc) - return rc; - /* turn off RFC 9113 leading and trailing white spaces validation against - HTTP field value. */ - nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); - rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn, - o); - nghttp2_option_del(o); - return rc; -#endif -} - -/* - * Initialize nghttp2 for a Curl connection - */ -static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn) -{ - if(!conn->proto.httpc.h2) { - int rc; - nghttp2_session_callbacks *callbacks; - - conn->proto.httpc.inbuf = malloc(H2_BUFSIZE); - if(!conn->proto.httpc.inbuf) - return CURLE_OUT_OF_MEMORY; - - rc = nghttp2_session_callbacks_new(&callbacks); - - if(rc) { - failf(data, "Couldn't initialize nghttp2 callbacks"); - return CURLE_OUT_OF_MEMORY; /* most likely at least */ - } - - /* nghttp2_send_callback */ - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - /* nghttp2_on_frame_recv_callback */ - nghttp2_session_callbacks_set_on_frame_recv_callback - (callbacks, on_frame_recv); - /* nghttp2_on_data_chunk_recv_callback */ - nghttp2_session_callbacks_set_on_data_chunk_recv_callback - (callbacks, on_data_chunk_recv); - /* nghttp2_on_stream_close_callback */ - nghttp2_session_callbacks_set_on_stream_close_callback - (callbacks, on_stream_close); - /* nghttp2_on_begin_headers_callback */ - nghttp2_session_callbacks_set_on_begin_headers_callback - (callbacks, on_begin_headers); - /* nghttp2_on_header_callback */ - nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); - - nghttp2_session_callbacks_set_error_callback(callbacks, error_callback); - - /* The nghttp2 session is not yet setup, do it */ - rc = client_new(conn, callbacks); - - nghttp2_session_callbacks_del(callbacks); - - if(rc) { - failf(data, "Couldn't initialize nghttp2"); - return CURLE_OUT_OF_MEMORY; /* most likely at least */ - } - } - return CURLE_OK; -} - /* * Append headers to ask for an HTTP1.1 to HTTP2 upgrade. */ @@ -1357,26 +1413,18 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct Curl_easy *data) { CURLcode result; - ssize_t binlen; char *base64; size_t blen; - struct connectdata *conn = data->conn; struct SingleRequest *k = &data->req; - uint8_t *binsettings = conn->proto.httpc.binsettings; - struct http_conn *httpc = &conn->proto.httpc; - - populate_settings(data, httpc); + uint8_t binsettings[H2_BINSETTINGS_LEN]; + size_t binlen; /* length of the binsettings data */ - /* this returns number of bytes it wrote */ - binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, - httpc->local_settings, - httpc->local_settings_num); + binlen = populate_binsettings(binsettings, data); if(binlen <= 0) { failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); Curl_dyn_free(req); return CURLE_FAILED_INIT; } - conn->proto.httpc.binlen = binlen; result = Curl_base64url_encode((const char *)binsettings, binlen, &base64, &blen); @@ -1400,10 +1448,10 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, /* * Returns nonzero if current HTTP/2 session should be closed. */ -static int should_close_session(struct http_conn *httpc) +static int should_close_session(struct cf_h2_ctx *ctx) { - return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2); + return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) && + !nghttp2_session_want_write(ctx->h2); } /* @@ -1412,65 +1460,65 @@ static int should_close_session(struct http_conn *httpc) * This function returns 0 if it succeeds, or -1 and error code will * be assigned to *err. */ -static int h2_process_pending_input(struct Curl_easy *data, - struct http_conn *httpc, +static int h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data, CURLcode *err) { + struct cf_h2_ctx *ctx = cf->ctx; ssize_t nread; - char *inbuf; ssize_t rv; - nread = httpc->inbuflen - httpc->nread_inbuf; - inbuf = httpc->inbuf + httpc->nread_inbuf; + nread = ctx->inbuflen - ctx->nread_inbuf; + if(nread) { + char *inbuf = ctx->inbuf + ctx->nread_inbuf; - set_transfer(httpc, data); /* set the transfer */ - rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); - if(rv < 0) { - failf(data, - "h2_process_pending_input: nghttp2_session_mem_recv() returned " - "%zd:%s", rv, nghttp2_strerror((int)rv)); - *err = CURLE_RECV_ERROR; - return -1; - } + rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)inbuf, nread); + if(rv < 0) { + failf(data, + "h2_process_pending_input: nghttp2_session_mem_recv() returned " + "%zd:%s", rv, nghttp2_strerror((int)rv)); + *err = CURLE_RECV_ERROR; + return -1; + } - if(nread == rv) { - H2BUGF(infof(data, - "h2_process_pending_input: All data in connection buffer " - "processed")); - httpc->inbuflen = 0; - httpc->nread_inbuf = 0; - } - else { - httpc->nread_inbuf += rv; - H2BUGF(infof(data, - "h2_process_pending_input: %zu bytes left in connection " - "buffer", - httpc->inbuflen - httpc->nread_inbuf)); + if(nread == rv) { + DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed")); + ctx->inbuflen = 0; + ctx->nread_inbuf = 0; + } + else { + ctx->nread_inbuf += rv; + DEBUGF(LOG_CF(data, cf, "h2_process_pending_input: %zu bytes left " + "in connection buffer", + ctx->inbuflen - ctx->nread_inbuf)); + } } - rv = h2_session_send(data, httpc->h2); + rv = h2_session_send(cf, data); if(rv) { *err = CURLE_SEND_ERROR; return -1; } - if(nghttp2_session_check_request_allowed(httpc->h2) == 0) { + if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { /* No more requests are allowed in the current session, so the connection may not be reused. This is set when a GOAWAY frame has been received or when the limit of stream identifiers has been reached. */ - connclose(data->conn, "http/2: No new requests allowed"); + connclose(cf->conn, "http/2: No new requests allowed"); } - if(should_close_session(httpc)) { + if(should_close_session(ctx)) { struct HTTP *stream = data->req.p.http; - H2BUGF(infof(data, + DEBUGF(LOG_CF(data, cf, "h2_process_pending_input: nothing to do in this session")); - if(stream->error) + if(stream->reset) + *err = CURLE_PARTIAL_FILE; + else if(stream->error) *err = CURLE_HTTP2; else { /* not an error per se, but should still close the connection */ - connclose(data->conn, "GOAWAY received"); + connclose(cf->conn, "GOAWAY received"); *err = CURLE_OK; } return -1; @@ -1478,79 +1526,72 @@ static int h2_process_pending_input(struct Curl_easy *data, return 0; } -/* - * Called from transfer.c:done_sending when we stop uploading. - */ -CURLcode Curl_http2_done_sending(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode http2_data_done_send(struct Curl_cfilter *cf, + struct Curl_easy *data) { + struct cf_h2_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + struct HTTP *stream = data->req.p.http; - if((conn->handler == &Curl_handler_http2_ssl) || - (conn->handler == &Curl_handler_http2)) { - /* make sure this is only attempted for HTTP/2 transfers */ - struct HTTP *stream = data->req.p.http; - struct http_conn *httpc = &conn->proto.httpc; - nghttp2_session *h2 = httpc->h2; - - if(stream->upload_left) { - /* If the stream still thinks there's data left to upload. */ + if(!ctx || !ctx->h2) + goto out; - stream->upload_left = 0; /* DONE! */ + if(stream->upload_left) { + /* If the stream still thinks there's data left to upload. */ + stream->upload_left = 0; /* DONE! */ - /* resume sending here to trigger the callback to get called again so - that it can signal EOF to nghttp2 */ - (void)nghttp2_session_resume_data(h2, stream->stream_id); - (void)h2_process_pending_input(data, httpc, &result); - } + /* resume sending here to trigger the callback to get called again so + that it can signal EOF to nghttp2 */ + (void)nghttp2_session_resume_data(ctx->h2, stream->stream_id); + (void)h2_process_pending_input(cf, data, &result); + } - /* If nghttp2 still has pending frames unsent */ - if(nghttp2_session_want_write(h2)) { - struct SingleRequest *k = &data->req; - int rv; + /* If nghttp2 still has pending frames unsent */ + if(nghttp2_session_want_write(ctx->h2)) { + struct SingleRequest *k = &data->req; + int rv; - H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data)); + DEBUGF(LOG_CF(data, cf, "HTTP/2 still wants to send data")); - /* and attempt to send the pending frames */ - rv = h2_session_send(data, h2); - if(rv) - result = CURLE_SEND_ERROR; + /* and attempt to send the pending frames */ + rv = h2_session_send(cf, data); + if(rv) + result = CURLE_SEND_ERROR; - if(nghttp2_session_want_write(h2)) { - /* re-set KEEP_SEND to make sure we are called again */ - k->keepon |= KEEP_SEND; - } + if(nghttp2_session_want_write(ctx->h2)) { + /* re-set KEEP_SEND to make sure we are called again */ + k->keepon |= KEEP_SEND; } } + +out: return result; } -static ssize_t http2_handle_stream_close(struct connectdata *conn, +static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, struct Curl_easy *data, struct HTTP *stream, CURLcode *err) { - struct http_conn *httpc = &conn->proto.httpc; + struct cf_h2_ctx *ctx = cf->ctx; - if(httpc->pause_stream_id == stream->stream_id) { - httpc->pause_stream_id = 0; + if(ctx->pause_stream_id == stream->stream_id) { + ctx->pause_stream_id = 0; } - drained_transfer(data, httpc); + drained_transfer(cf, data); - if(httpc->pause_stream_id == 0) { - if(h2_process_pending_input(data, httpc, err) != 0) { + if(ctx->pause_stream_id == 0) { + if(h2_process_pending_input(cf, data, err) != 0) { return -1; } } - DEBUGASSERT(data->state.drain == 0); - /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ stream->closed = FALSE; if(stream->error == NGHTTP2_REFUSED_STREAM) { - H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection", - stream->stream_id)); - connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */ + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new " + "connection", stream->stream_id)); + connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ data->state.refused_stream = TRUE; *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ return -1; @@ -1597,10 +1638,24 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, stream->close_handled = TRUE; - H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close")); + DEBUGF(LOG_CF(data, cf, "http2_recv returns 0, http2_handle_stream_close")); return 0; } +static int sweight_wanted(const struct Curl_easy *data) +{ + /* 0 weight is not set by user and we take the nghttp2 default one */ + return data->set.priority.weight? + data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT; +} + +static int sweight_in_effect(const struct Curl_easy *data) +{ + /* 0 weight is not set by user and we take the nghttp2 default one */ + return data->state.priority.weight? + data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT; +} + /* * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight * and dependency to the peer. It also stores the updated values in the state @@ -1610,14 +1665,14 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { - struct HTTP *depstream = (data->set.stream_depends_on? - data->set.stream_depends_on->req.p.http:NULL); + struct Curl_data_priority *prio = &data->set.priority; + struct HTTP *depstream = (prio->parent? + prio->parent->req.p.http:NULL); int32_t depstream_id = depstream? depstream->stream_id:0; - nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight, - data->set.stream_depends_e); - data->state.stream_weight = data->set.stream_weight; - data->state.stream_depends_e = data->set.stream_depends_e; - data->state.stream_depends_on = data->set.stream_depends_on; + nghttp2_priority_spec_init(pri_spec, depstream_id, + sweight_wanted(data), + data->set.priority.exclusive); + data->state.priority = *prio; } /* @@ -1625,53 +1680,61 @@ static void h2_pri_spec(struct Curl_easy *data, * dependency settings and if so it submits a PRIORITY frame with the updated * info. */ -static int h2_session_send(struct Curl_easy *data, - nghttp2_session *h2) +static CURLcode h2_session_send(struct Curl_cfilter *cf, + struct Curl_easy *data) { + struct cf_h2_ctx *ctx = cf->ctx; struct HTTP *stream = data->req.p.http; - struct http_conn *httpc = &data->conn->proto.httpc; - set_transfer(httpc, data); - if((data->set.stream_weight != data->state.stream_weight) || - (data->set.stream_depends_e != data->state.stream_depends_e) || - (data->set.stream_depends_on != data->state.stream_depends_on) ) { + int rv = 0; + + if((sweight_wanted(data) != sweight_in_effect(data)) || + (data->set.priority.exclusive != data->state.priority.exclusive) || + (data->set.priority.parent != data->state.priority.parent) ) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; - int rv; h2_pri_spec(data, &pri_spec); - - H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)", - stream->stream_id, data)); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Queuing PRIORITY", + stream->stream_id)); DEBUGASSERT(stream->stream_id != -1); - rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id, - &pri_spec); + rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, + stream->stream_id, &pri_spec); if(rv) - return rv; + goto out; } - return nghttp2_session_send(h2); + rv = nghttp2_session_send(ctx->h2); +out: + if(nghttp2_is_fatal(rv)) { + DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv)); + return CURLE_SEND_ERROR; + } + return flush_output(cf, data); } -static ssize_t http2_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) { - ssize_t nread; - struct connectdata *conn = data->conn; - struct http_conn *httpc = &conn->proto.httpc; + struct cf_h2_ctx *ctx = cf->ctx; struct HTTP *stream = data->req.p.http; + ssize_t nread = -1; + struct cf_call_data save; - (void)sockindex; /* we always do HTTP2 on sockindex 0 */ + CF_DATA_SAVE(save, cf, data); - if(should_close_session(httpc)) { - H2BUGF(infof(data, - "http2_recv: nothing to do in this session")); - if(conn->bits.close) { + if(should_close_session(ctx)) { + DEBUGF(LOG_CF(data, cf, "http2_recv: nothing to do in this session")); + if(cf->conn->bits.close) { /* already marked for closure, return OK and we're done */ + drained_transfer(cf, data); *err = CURLE_OK; - return 0; + nread = 0; + goto out; } *err = CURLE_HTTP2; - return -1; + nread = -1; + goto out; } /* Nullify here because we call nghttp2_session_send() and they @@ -1690,74 +1753,67 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex, size_t left = Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf; size_t ncopy = CURLMIN(len, left); - memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) + + memcpy(buf, Curl_dyn_ptr(&stream->header_recvbuf) + stream->nread_header_recvbuf, ncopy); stream->nread_header_recvbuf += ncopy; - H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf", - (int)ncopy)); - return ncopy; + DEBUGF(LOG_CF(data, cf, "recv: Got %d bytes from header_recvbuf", + (int)ncopy)); + nread = ncopy; + goto out; } - H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u", - data, stream->stream_id, - nghttp2_session_get_local_window_size(httpc->h2), - nghttp2_session_get_stream_local_window_size(httpc->h2, - stream->stream_id) + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: win %u/%u", + stream->stream_id, + nghttp2_session_get_local_window_size(ctx->h2), + nghttp2_session_get_stream_local_window_size(ctx->h2, + stream->stream_id) )); - if((data->state.drain) && stream->memlen) { - H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)", - stream->memlen, stream->stream_id, - stream->mem, mem)); - if(mem != stream->mem) { + if(stream->memlen) { + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: DRAIN %zu bytes (%p => %p)", + stream->stream_id, stream->memlen, + (void *)stream->mem, (void *)buf)); + if(buf != stream->mem) { /* if we didn't get the same buffer this time, we must move the data to the beginning */ - memmove(mem, stream->mem, stream->memlen); + memmove(buf, stream->mem, stream->memlen); stream->len = len - stream->memlen; - stream->mem = mem; + stream->mem = buf; } - if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) { + + if(ctx->pause_stream_id == stream->stream_id && !stream->pausedata) { /* We have paused nghttp2, but we have no pause data (see on_data_chunk_recv). */ - httpc->pause_stream_id = 0; - if(h2_process_pending_input(data, httpc, err) != 0) { - return -1; + ctx->pause_stream_id = 0; + if(h2_process_pending_input(cf, data, err) != 0) { + nread = -1; + goto out; } } } else if(stream->pausedata) { - DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); + DEBUGASSERT(ctx->pause_stream_id == stream->stream_id); nread = CURLMIN(len, stream->pauselen); - memcpy(mem, stream->pausedata, nread); + memcpy(buf, stream->pausedata, nread); stream->pausedata += nread; stream->pauselen -= nread; + drain_this(cf, data); if(stream->pauselen == 0) { - H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id)); - DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); - httpc->pause_stream_id = 0; + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Unpaused", stream->stream_id)); + DEBUGASSERT(ctx->pause_stream_id == stream->stream_id); + ctx->pause_stream_id = 0; stream->pausedata = NULL; stream->pauselen = 0; - - /* When NGHTTP2_ERR_PAUSE is returned from - data_source_read_callback, we might not process DATA frame - fully. Calling nghttp2_session_mem_recv() again will - continue to process DATA frame, but if there is no incoming - frames, then we have to call it again with 0-length data. - Without this, on_stream_close callback will not be called, - and stream could be hanged. */ - if(h2_process_pending_input(data, httpc, err) != 0) { - return -1; - } } - H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u", - nread, stream->stream_id)); - return nread; + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: returns unpaused %zd bytes", + stream->stream_id, nread)); + goto out; } - else if(httpc->pause_stream_id) { + else if(ctx->pause_stream_id) { /* If a stream paused nghttp2_session_mem_recv previously, and has not processed all data, it still refers to the buffer in nghttp2_session. If we call nghttp2_session_mem_recv(), we may @@ -1766,36 +1822,56 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex, socket is not read. But it seems that usually streams are notified with its drain property, and socket is read again quickly. */ - if(stream->closed) + if(stream->closed) { /* closed overrides paused */ - return 0; - H2BUGF(infof(data, "stream %u is paused, pause id: %u", - stream->stream_id, httpc->pause_stream_id)); + drained_transfer(cf, data); + nread = 0; + goto out; + } + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is paused, pause h2sid: %u", + stream->stream_id, ctx->pause_stream_id)); *err = CURLE_AGAIN; - return -1; + nread = -1; + goto out; } else { - /* remember where to store incoming data for this stream and how big the - buffer is */ - stream->mem = mem; + /* We have nothing buffered for `data` and no other stream paused + * the processing of incoming data, we can therefore read new data + * from the network. + * If DATA is coming for this stream, we want to store it ad the + * `buf` passed in right away - saving us a copy. + */ + stream->mem = buf; stream->len = len; stream->memlen = 0; - if(httpc->inbuflen == 0) { - nread = ((Curl_recv *)httpc->recv_underlying)( - data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err); + if(ctx->inbuflen > 0) { + DEBUGF(LOG_CF(data, cf, "Use data left in connection buffer, nread=%zd", + ctx->inbuflen - ctx->nread_inbuf)); + if(h2_process_pending_input(cf, data, err)) + return -1; + } - if(nread == -1) { + while(stream->memlen == 0 /* have no data for this stream */ + && !ctx->pause_stream_id /* we are not paused either */ + && ctx->inbuflen == 0) { /* and out input buffer is empty */ + /* Receive data from the "lower" filters */ + nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, err); + if(nread < 0) { if(*err != CURLE_AGAIN) failf(data, "Failed receiving HTTP2 data"); - else if(stream->closed) + else if(stream->closed) { /* received when the stream was already closed! */ - return http2_handle_stream_close(conn, data, stream, err); + nread = http2_handle_stream_close(cf, data, stream, err); + goto out; + } - return -1; + /* nothing to read from the lower layers, clear drain */ + drained_transfer(cf, data); + nread = -1; + goto out; } - - if(nread == 0) { + else if(nread == 0) { if(!stream->closed) { /* This will happen when the server or proxy server is SIGKILLed during data transfer. We should emit an error since our data @@ -1803,107 +1879,133 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex, failf(data, "HTTP/2 stream %u was not closed cleanly before" " end of the underlying stream", stream->stream_id); - *err = CURLE_HTTP2_STREAM; - return -1; + drained_transfer(cf, data); + *err = CURLE_PARTIAL_FILE; + nread = -1; + goto out; } - H2BUGF(infof(data, "end of stream")); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] end of stream", + stream->stream_id)); *err = CURLE_OK; - return 0; + nread = 0; + goto out; } - H2BUGF(infof(data, "nread=%zd", nread)); - - httpc->inbuflen = nread; - - DEBUGASSERT(httpc->nread_inbuf == 0); - } - else { - nread = httpc->inbuflen - httpc->nread_inbuf; - (void)nread; /* silence warning, used in debug */ - H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd", - nread)); + DEBUGF(LOG_CF(data, cf, "read %zd from connection", nread)); + ctx->inbuflen = nread; + DEBUGASSERT(ctx->nread_inbuf == 0); + if(h2_process_pending_input(cf, data, err)) + return -1; } - if(h2_process_pending_input(data, httpc, err)) - return -1; } + if(stream->memlen) { ssize_t retlen = stream->memlen; - H2BUGF(infof(data, "http2_recv: returns %zd for stream %u", - retlen, stream->stream_id)); + + /* TODO: all this buffer handling is very brittle */ + stream->len += stream->memlen; stream->memlen = 0; - if(httpc->pause_stream_id == stream->stream_id) { + if(ctx->pause_stream_id == stream->stream_id) { /* data for this stream is returned now, but this stream caused a pause already so we need it called again asap */ - H2BUGF(infof(data, "Data returned for PAUSED stream %u", - stream->stream_id)); - } - else if(!stream->closed) { - drained_transfer(data, httpc); + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Data returned for PAUSED stream", + stream->stream_id)); + drain_this(cf, data); + Curl_expire(data, 0, EXPIRE_RUN_NOW); } - else + else if(stream->closed) { + if(stream->reset || stream->error) { + nread = http2_handle_stream_close(cf, data, stream, err); + goto out; + } /* this stream is closed, trigger a another read ASAP to detect that */ + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is closed now, run again", + stream->stream_id)); + drain_this(cf, data); Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + else { + drained_transfer(cf, data); + } - return retlen; + *err = CURLE_OK; + nread = retlen; + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_h2_recv -> %zd", + stream->stream_id, nread)); + goto out; + } + + if(stream->closed) { + nread = http2_handle_stream_close(cf, data, stream, err); + goto out; + } + + if(!data->state.drain && Curl_conn_cf_data_pending(cf->next, data)) { + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] pending data, set drain", + stream->stream_id)); + drain_this(cf, data); } - if(stream->closed) - return http2_handle_stream_close(conn, data, stream, err); *err = CURLE_AGAIN; - H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u", - stream->stream_id)); - return -1; + nread = -1; + DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv -> AGAIN", + stream->stream_id)); +out: + CF_DATA_RESTORE(cf, save); + return nread; } -static ssize_t http2_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) +static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) { /* * Currently, we send request in this function, but this function is also * used to send request body. It would be nice to add dedicated function for * request. */ + struct cf_h2_ctx *ctx = cf->ctx; int rv; - struct connectdata *conn = data->conn; - struct http_conn *httpc = &conn->proto.httpc; struct HTTP *stream = data->req.p.http; nghttp2_nv *nva = NULL; size_t nheader; nghttp2_data_provider data_prd; int32_t stream_id; - nghttp2_session *h2 = httpc->h2; nghttp2_priority_spec pri_spec; CURLcode result; struct h2h3req *hreq; + struct cf_call_data save; - (void)sockindex; - - H2BUGF(infof(data, "http2_send len=%zu", len)); + CF_DATA_SAVE(save, cf, data); + DEBUGF(LOG_CF(data, cf, "send len=%zu", len)); if(stream->stream_id != -1) { if(stream->close_handled) { infof(data, "stream %u closed", stream->stream_id); *err = CURLE_HTTP2_STREAM; - return -1; + len = -1; + goto out; } else if(stream->closed) { - return http2_handle_stream_close(conn, data, stream, err); + len = http2_handle_stream_close(cf, data, stream, err); + goto out; } /* If stream_id != -1, we have dispatched request HEADERS, and now are going to send or sending request body in DATA frame */ - stream->upload_mem = mem; + stream->upload_mem = buf; stream->upload_len = len; - rv = nghttp2_session_resume_data(h2, stream->stream_id); + rv = nghttp2_session_resume_data(ctx->h2, stream->stream_id); if(nghttp2_is_fatal(rv)) { *err = CURLE_SEND_ERROR; - return -1; + len = -1; + goto out; } - rv = h2_session_send(data, h2); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - return -1; + result = h2_session_send(cf, data); + if(result) { + *err = result; + len = -1; + goto out; } len -= stream->upload_len; @@ -1912,10 +2014,11 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, stream->upload_mem = NULL; stream->upload_len = 0; - if(should_close_session(httpc)) { - H2BUGF(infof(data, "http2_send: nothing to do in this session")); + if(should_close_session(ctx)) { + DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); *err = CURLE_HTTP2; - return -1; + len = -1; + goto out; } if(stream->upload_left) { @@ -1923,15 +2026,15 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, following API will make nghttp2_session_want_write() return nonzero if remote window allows it, which then libcurl checks socket is writable or not. See http2_perform_getsock(). */ - nghttp2_session_resume_data(h2, stream->stream_id); + nghttp2_session_resume_data(ctx->h2, stream->stream_id); } #ifdef DEBUG_HTTP2 if(!len) { infof(data, "http2_send: easy %p (stream %u) win %u/%u", data, stream->stream_id, - nghttp2_session_get_remote_window_size(httpc->h2), - nghttp2_session_get_stream_remote_window_size(httpc->h2, + nghttp2_session_get_remote_window_size(ctx->h2), + nghttp2_session_get_stream_remote_window_size(ctx->h2, stream->stream_id) ); @@ -1939,13 +2042,14 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, infof(data, "http2_send returns %zu for stream %u", len, stream->stream_id); #endif - return len; + goto out; } - result = Curl_pseudo_headers(data, mem, len, &hreq); + result = Curl_pseudo_headers(data, buf, len, NULL, &hreq); if(result) { *err = result; - return -1; + len = -1; + goto out; } nheader = hreq->entries; @@ -1953,7 +2057,8 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, if(!nva) { Curl_pseudo_free(hreq); *err = CURLE_OUT_OF_MEMORY; - return -1; + len = -1; + goto out; } else { unsigned int i; @@ -1969,8 +2074,8 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, h2_pri_spec(data, &pri_spec); - H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)", - nghttp2_session_check_request_allowed(h2), (void *)data)); + DEBUGF(LOG_CF(data, cf, "send request allowed %d (easy handle %p)", + nghttp2_session_check_request_allowed(ctx->h2), (void *)data)); switch(data->state.httpreq) { case HTTPREQ_POST: @@ -1985,42 +2090,40 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, data_prd.read_callback = data_source_read_callback; data_prd.source.ptr = NULL; - stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, + stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, &data_prd, data); break; default: - stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, + stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, NULL, data); } Curl_safefree(nva); if(stream_id < 0) { - H2BUGF(infof(data, - "http2_send() nghttp2_submit_request error (%s)%u", - nghttp2_strerror(stream_id), stream_id)); + DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", + nghttp2_strerror(stream_id), stream_id)); *err = CURLE_SEND_ERROR; - return -1; + len = -1; + goto out; } infof(data, "Using Stream ID: %u (easy handle %p)", stream_id, (void *)data); stream->stream_id = stream_id; - rv = h2_session_send(data, h2); - if(rv) { - H2BUGF(infof(data, - "http2_send() nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); - - *err = CURLE_SEND_ERROR; - return -1; + result = h2_session_send(cf, data); + if(result) { + *err = result; + len = -1; + goto out; } - if(should_close_session(httpc)) { - H2BUGF(infof(data, "http2_send: nothing to do in this session")); + if(should_close_session(ctx)) { + DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); *err = CURLE_HTTP2; - return -1; + len = -1; + goto out; } /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2 @@ -2030,169 +2133,126 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex, results that no writable socket check is performed. To workaround this, we issue nghttp2_session_resume_data() here to bring back DATA transmission from deferred state. */ - nghttp2_session_resume_data(h2, stream->stream_id); + nghttp2_session_resume_data(ctx->h2, stream->stream_id); +out: + CF_DATA_RESTORE(cf, save); return len; } -CURLcode Curl_http2_setup(struct Curl_easy *data, - struct connectdata *conn) +static int cf_h2_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *sock) { - CURLcode result; - struct http_conn *httpc = &conn->proto.httpc; + struct cf_h2_ctx *ctx = cf->ctx; + struct SingleRequest *k = &data->req; struct HTTP *stream = data->req.p.http; + int bitmap = GETSOCK_BLANK; + struct cf_call_data save; - DEBUGASSERT(data->state.buffer); + CF_DATA_SAVE(save, cf, data); + sock[0] = Curl_conn_cf_get_socket(cf, data); - stream->stream_id = -1; + if(!(k->keepon & KEEP_RECV_PAUSE)) + /* Unless paused - in an HTTP/2 connection we can basically always get a + frame so we should always be ready for one */ + bitmap |= GETSOCK_READSOCK(0); - Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS); - Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS); + /* we're (still uploading OR the HTTP/2 layer wants to send data) AND + there's a window to send data in */ + if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || + nghttp2_session_want_write(ctx->h2)) && + (nghttp2_session_get_remote_window_size(ctx->h2) && + nghttp2_session_get_stream_remote_window_size(ctx->h2, + stream->stream_id))) + bitmap |= GETSOCK_WRITESOCK(0); - stream->upload_left = 0; - stream->upload_mem = NULL; - stream->upload_len = 0; - stream->mem = data->state.buffer; - stream->len = data->set.buffer_size; + CF_DATA_RESTORE(cf, save); + return bitmap; +} - multi_connchanged(data->multi); - /* below this point only connection related inits are done, which only needs - to be done once per connection */ - if((conn->handler == &Curl_handler_http2_ssl) || - (conn->handler == &Curl_handler_http2)) - return CURLE_OK; /* already done */ +static CURLcode cf_h2_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_h2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; - if(conn->handler->flags & PROTOPT_SSL) - conn->handler = &Curl_handler_http2_ssl; - else - conn->handler = &Curl_handler_http2; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } - result = http2_init(data, conn); - if(result) { - Curl_dyn_free(&stream->header_recvbuf); - return result; + /* Connect the lower filters first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; } - infof(data, "Using HTTP2, server supports multiplexing"); + *done = FALSE; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 20; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; + CF_DATA_SAVE(save, cf, data); + if(!ctx->h2) { + result = cf_h2_ctx_init(cf, data, FALSE); + if(result) + goto out; + } - httpc->inbuflen = 0; - httpc->nread_inbuf = 0; + if(-1 == h2_process_pending_input(cf, data, &result)) { + result = CURLE_HTTP2; + goto out; + } - httpc->pause_stream_id = 0; - httpc->drain_total = 0; + *done = TRUE; + cf->connected = TRUE; + result = CURLE_OK; - return CURLE_OK; +out: + CF_DATA_RESTORE(cf, save); + return result; } -CURLcode Curl_http2_switched(struct Curl_easy *data, - const char *mem, size_t nread) +static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - CURLcode result; - struct connectdata *conn = data->conn; - struct http_conn *httpc = &conn->proto.httpc; - int rv; - struct HTTP *stream = data->req.p.http; - - result = Curl_http2_setup(data, conn); - if(result) - return result; - - httpc->recv_underlying = conn->recv[FIRSTSOCKET]; - httpc->send_underlying = conn->send[FIRSTSOCKET]; - conn->recv[FIRSTSOCKET] = http2_recv; - conn->send[FIRSTSOCKET] = http2_send; - - if(data->req.upgr101 == UPGR101_RECEIVED) { - /* stream 1 is opened implicitly on upgrade */ - stream->stream_id = 1; - /* queue SETTINGS frame (again) */ - rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen, - data->state.httpreq == HTTPREQ_HEAD, NULL); - if(rv) { - failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } + struct cf_h2_ctx *ctx = cf->ctx; - rv = nghttp2_session_set_stream_user_data(httpc->h2, - stream->stream_id, - data); - if(rv) { - infof(data, "http/2: failed to set user_data for stream %u", - stream->stream_id); - DEBUGASSERT(0); - } - } - else { - populate_settings(data, httpc); + if(ctx) { + struct cf_call_data save; - /* stream ID is unknown at this point */ - stream->stream_id = -1; - rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, - httpc->local_settings, - httpc->local_settings_num); - if(rv) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } + CF_DATA_SAVE(save, cf, data); + cf_h2_ctx_clear(ctx); + CF_DATA_RESTORE(cf, save); } +} - rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0, - HTTP2_HUGE_WINDOW_SIZE); - if(rv) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } +static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; - /* we are going to copy mem to httpc->inbuf. This is required since - mem is part of buffer pointed by stream->mem, and callbacks - called by nghttp2_session_mem_recv() will write stream specific - data into stream->mem, overwriting data already there. */ - if(H2_BUFSIZE < nread) { - failf(data, "connection buffer size is too small to store data following " - "HTTP Upgrade response header: buflen=%d, datalen=%zu", - H2_BUFSIZE, nread); - return CURLE_HTTP2; + (void)data; + if(ctx) { + cf_h2_ctx_free(ctx); + cf->ctx = NULL; } - - infof(data, "Copying HTTP/2 data in stream buffer to connection buffer" - " after upgrade: len=%zu", - nread); - - if(nread) - memcpy(httpc->inbuf, mem, nread); - - httpc->inbuflen = nread; - - DEBUGASSERT(httpc->nread_inbuf == 0); - - if(-1 == h2_process_pending_input(data, httpc, &result)) - return CURLE_HTTP2; - - return CURLE_OK; } -CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) +static CURLcode http2_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) { + struct cf_h2_ctx *ctx = cf->ctx; + DEBUGASSERT(data); - DEBUGASSERT(data->conn); - /* if it isn't HTTP/2, we're done */ - if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) || - !data->conn->proto.httpc.h2) - return CURLE_OK; #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE - else { + if(ctx && ctx->h2) { struct HTTP *stream = data->req.p.http; - struct http_conn *httpc = &data->conn->proto.httpc; uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE; - int rv = nghttp2_session_set_local_window_size(httpc->h2, + CURLcode result; + + int rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, stream->stream_id, window); @@ -2203,9 +2263,9 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) } /* make sure the window update gets sent */ - rv = h2_session_send(data, httpc->h2); - if(rv) - return CURLE_SEND_ERROR; + result = h2_session_send(cf, data); + if(result) + return result; DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u", window, stream->stream_id)); @@ -2214,7 +2274,7 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) { /* read out the stream local window again */ uint32_t window2 = - nghttp2_session_get_stream_local_window_size(httpc->h2, + nghttp2_session_get_stream_local_window_size(ctx->h2, stream->stream_id); DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u", window2, stream->stream_id)); @@ -2225,86 +2285,323 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) return CURLE_OK; } -CURLcode Curl_http2_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) +static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) { - if(parent) { - struct Curl_http2_dep **tail; - struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep)); - if(!dep) - return CURLE_OUT_OF_MEMORY; - dep->data = child; - - if(parent->set.stream_dependents && exclusive) { - struct Curl_http2_dep *node = parent->set.stream_dependents; - while(node) { - node->data->set.stream_depends_on = child; - node = node->next; - } + CURLcode result = CURLE_OK; + struct cf_call_data save; - tail = &child->set.stream_dependents; - while(*tail) - tail = &(*tail)->next; + (void)arg2; - DEBUGASSERT(!*tail); - *tail = parent->set.stream_dependents; - parent->set.stream_dependents = 0; - } + CF_DATA_SAVE(save, cf, data); + switch(event) { + case CF_CTRL_DATA_SETUP: { + result = http2_data_setup(cf, data); + break; + } + case CF_CTRL_DATA_PAUSE: { + result = http2_data_pause(cf, data, (arg1 != 0)); + break; + } + case CF_CTRL_DATA_DONE_SEND: { + result = http2_data_done_send(cf, data); + break; + } + case CF_CTRL_DATA_DONE: { + http2_data_done(cf, data, arg1 != 0); + break; + } + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} - tail = &parent->set.stream_dependents; - while(*tail) { - (*tail)->data->set.stream_depends_e = FALSE; - tail = &(*tail)->next; - } +static bool cf_h2_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + if(ctx && ctx->inbuflen > 0 && ctx->nread_inbuf > ctx->inbuflen) + return TRUE; + return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} - DEBUGASSERT(!*tail); - *tail = dep; +static bool cf_h2_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + CURLcode result; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = (ctx && ctx->h2 && !http2_connisdead(cf, data)); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + result = http2_send_ping(cf, data); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_h2_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct cf_call_data save; + size_t effective_max; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: + DEBUGASSERT(pres1); + + CF_DATA_SAVE(save, cf, data); + if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { + /* the limit is what we have in use right now */ + effective_max = CONN_INUSE(cf->conn); + } + else { + effective_max = ctx->max_concurrent_streams; + } + *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max; + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + default: + break; } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} - child->set.stream_depends_on = parent; - child->set.stream_depends_e = exclusive; - return CURLE_OK; +struct Curl_cftype Curl_cft_nghttp2 = { + "HTTP/2", + CF_TYPE_MULTIPLEX, + CURL_LOG_DEFAULT, + cf_h2_destroy, + cf_h2_connect, + cf_h2_close, + Curl_cf_def_get_host, + cf_h2_get_select_socks, + cf_h2_data_pending, + cf_h2_send, + cf_h2_recv, + cf_h2_cntrl, + cf_h2_is_alive, + cf_h2_keep_alive, + cf_h2_query, +}; + +static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h2_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(data->conn); + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) + goto out; + + result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx); + if(result) + goto out; + + Curl_conn_cf_add(data, conn, sockindex, cf); + result = CURLE_OK; + +out: + if(result) + cf_h2_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; } -void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child) +static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct Curl_http2_dep *last = 0; - struct Curl_http2_dep *data = parent->set.stream_dependents; - DEBUGASSERT(child->set.stream_depends_on == parent); + struct Curl_cfilter *cf_h2 = NULL; + struct cf_h2_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; - while(data && data->data != child) { - last = data; - data = data->next; + (void)data; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) + goto out; + + result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx); + if(result) + goto out; + + Curl_conn_cf_insert_after(cf, cf_h2); + result = CURLE_OK; + +out: + if(result) + cf_h2_ctx_free(ctx); + return result; +} + +bool Curl_cf_is_http2(struct Curl_cfilter *cf, const struct Curl_easy *data) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_nghttp2) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; } + return FALSE; +} - DEBUGASSERT(data); +bool Curl_conn_is_http2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE; +} - if(data) { - if(last) { - last->next = data->next; - } - else { - parent->set.stream_dependents = data->next; +bool Curl_http2_may_switch(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + (void)sockindex; + if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + /* We don't support HTTP/2 proxies yet. Also it's debatable + whether or not this setting should apply to HTTP/2 proxies. */ + infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); + return FALSE; } - free(data); +#endif + return TRUE; } + return FALSE; +} + +CURLcode Curl_http2_switch(struct Curl_easy *data, + struct connectdata *conn, int sockindex) +{ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGF(infof(data, DMSGI(data, sockindex, "switching to HTTP/2"))); + + result = http2_cfilter_add(&cf, data, conn, sockindex); + if(result) + return result; - child->set.stream_depends_on = 0; - child->set.stream_depends_e = FALSE; + result = cf_h2_ctx_init(cf, data, FALSE); + if(result) + return result; + + conn->httpversion = 20; /* we know we're on HTTP/2 now */ + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + multi_connchanged(data->multi); + + if(cf->next) { + bool done; + return Curl_conn_cf_connect(cf, data, FALSE, &done); + } + return CURLE_OK; +} + +CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct Curl_cfilter *cf_h2; + CURLcode result; + + DEBUGASSERT(!Curl_cf_is_http2(cf, data)); + + result = http2_cfilter_insert_after(cf, data); + 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; + multi_connchanged(data->multi); + + if(cf_h2->next) { + bool done; + return Curl_conn_cf_connect(cf_h2, data, FALSE, &done); + } + return CURLE_OK; } -void Curl_http2_cleanup_dependencies(struct Curl_easy *data) +CURLcode Curl_http2_upgrade(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + const char *mem, size_t nread) { - while(data->set.stream_dependents) { - struct Curl_easy *tmp = data->set.stream_dependents->data; - Curl_http2_remove_child(data, tmp); - if(data->set.stream_depends_on) - Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE); + struct Curl_cfilter *cf; + struct cf_h2_ctx *ctx; + CURLcode result; + + DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); + DEBUGF(infof(data, DMSGI(data, sockindex, "upgrading to HTTP/2"))); + DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); + + result = http2_cfilter_add(&cf, data, conn, sockindex); + 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) { + /* we are going to copy mem to httpc->inbuf. This is required since + mem is part of buffer pointed by stream->mem, and callbacks + called by nghttp2_session_mem_recv() will write stream specific + data into stream->mem, overwriting data already there. */ + if(H2_BUFSIZE < nread) { + failf(data, "connection buffer size is too small to store data " + "following HTTP Upgrade response header: buflen=%d, datalen=%zu", + H2_BUFSIZE, nread); + return CURLE_HTTP2; + } + + infof(data, "Copying HTTP/2 data in stream buffer to connection buffer" + " after upgrade: len=%zu", nread); + DEBUGASSERT(ctx->nread_inbuf == 0); + memcpy(ctx->inbuf, mem, nread); + ctx->inbuflen = nread; } - if(data->set.stream_depends_on) - Curl_http2_remove_child(data->set.stream_depends_on, data); + conn->httpversion = 20; /* we know we're on HTTP/2 now */ + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + conn->bundle->multiuse = BUNDLE_MULTIPLEX; + multi_connchanged(data->multi); + + if(cf->next) { + bool done; + return Curl_conn_cf_connect(cf, data, FALSE, &done); + } + return CURLE_OK; } /* Only call this function for a transfer that already got an HTTP/2 @@ -2312,7 +2609,7 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data) bool Curl_h2_http_1_1_error(struct Curl_easy *data) { struct HTTP *stream = data->req.p.http; - return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED); + return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED); } #else /* !USE_NGHTTP2 */ diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h index 966bf75..f78fbf0 100644 --- a/Utilities/cmcurl/lib/http2.h +++ b/Utilities/cmcurl/lib/http2.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -40,44 +40,41 @@ void Curl_http2_ver(char *p, size_t len); const char *Curl_http2_strerror(uint32_t err); -CURLcode Curl_http2_init(struct connectdata *conn); -void Curl_http2_init_state(struct UrlState *state); -void Curl_http2_init_userset(struct UserDefined *set); CURLcode Curl_http2_request_upgrade(struct dynbuf *req, struct Curl_easy *data); -CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn); -CURLcode Curl_http2_switched(struct Curl_easy *data, - const char *ptr, size_t nread); -/* called from http_setup_conn */ -void Curl_http2_setup_conn(struct connectdata *conn); -void Curl_http2_setup_req(struct Curl_easy *data); -void Curl_http2_done(struct Curl_easy *data, bool premature); -CURLcode Curl_http2_done_sending(struct Curl_easy *data, - struct connectdata *conn); -CURLcode Curl_http2_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive); -void Curl_http2_remove_child(struct Curl_easy *parent, - struct Curl_easy *child); -void Curl_http2_cleanup_dependencies(struct Curl_easy *data); -CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause); /* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ bool Curl_h2_http_1_1_error(struct Curl_easy *data); + +bool Curl_conn_is_http2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); +bool Curl_cf_is_http2(struct Curl_cfilter *cf, const struct Curl_easy *data); + +bool Curl_http2_may_switch(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); + +CURLcode Curl_http2_switch(struct Curl_easy *data, + struct connectdata *conn, int sockindex); + +CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data); + +CURLcode Curl_http2_upgrade(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + const char *ptr, size_t nread); + +extern struct Curl_cftype Curl_cft_nghttp2; + #else /* USE_NGHTTP2 */ + +#define Curl_cf_is_http2(a,b) FALSE +#define Curl_conn_is_http2(a,b,c) FALSE +#define Curl_http2_may_switch(a,b,c) FALSE + #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_setup_conn(x) Curl_nop_stmt -#define Curl_http2_setup_req(x) -#define Curl_http2_init_state(x) -#define Curl_http2_init_userset(x) -#define Curl_http2_done(x,y) -#define Curl_http2_done_sending(x,y) (void)y -#define Curl_http2_add_child(x, y, z) -#define Curl_http2_remove_child(x, y) -#define Curl_http2_cleanup_dependencies(x) -#define Curl_http2_stream_pause(x, y) +#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL #define Curl_h2_http_1_1_error(x) 0 #endif diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 8c6d1c9..f8ce169 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -48,9 +48,9 @@ do { \ ret = Curl_hmacit(Curl_HMAC_SHA256, \ (unsigned char *)k, \ - (unsigned int)kl, \ + kl, \ (unsigned char *)d, \ - (unsigned int)dl, o); \ + dl, o); \ if(ret) { \ goto fail; \ } \ diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.h b/Utilities/cmcurl/lib/http_aws_sigv4.h index 85755e9..57cc570 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.h +++ b/Utilities/cmcurl/lib/http_aws_sigv4.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index c344e6d..bda00d3 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index 2cf5507..ed50713 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_digest.c b/Utilities/cmcurl/lib/http_digest.c index f015f78..8daad99 100644 --- a/Utilities/cmcurl/lib/http_digest.c +++ b/Utilities/cmcurl/lib/http_digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_digest.h b/Utilities/cmcurl/lib/http_digest.h index eea90b7..7d5cfc1 100644 --- a/Utilities/cmcurl/lib/http_digest.h +++ b/Utilities/cmcurl/lib/http_digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c index 5909f85..153e3d4 100644 --- a/Utilities/cmcurl/lib/http_negotiate.c +++ b/Utilities/cmcurl/lib/http_negotiate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_negotiate.h b/Utilities/cmcurl/lib/http_negotiate.h index 6e2096c..76d8356 100644 --- a/Utilities/cmcurl/lib/http_negotiate.h +++ b/Utilities/cmcurl/lib/http_negotiate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index a19cc0d..b845ddf 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_ntlm.h b/Utilities/cmcurl/lib/http_ntlm.h index cec63b8..f37572b 100644 --- a/Utilities/cmcurl/lib/http_ntlm.h +++ b/Utilities/cmcurl/lib/http_ntlm.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index e30730a..fdd092d 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -26,7 +26,7 @@ #include "http_proxy.h" -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +#if !defined(CURL_DISABLE_PROXY) #include <curl/curl.h> #ifdef USE_HYPER @@ -49,6 +49,9 @@ #include "curl_memory.h" #include "memdebug.h" + +#if !defined(CURL_DISABLE_HTTP) + typedef enum { TUNNEL_INIT, /* init/default/no tunnel state */ TUNNEL_CONNECT, /* CONNECT request is being send */ @@ -63,8 +66,7 @@ struct tunnel_state { int sockindex; const char *hostname; int remote_port; - struct HTTP http_proxy; - struct HTTP *prot_save; + struct HTTP CONNECT; struct dynbuf rcvbuf; struct dynbuf req; size_t nsend; @@ -149,17 +151,6 @@ static CURLcode tunnel_init(struct tunnel_state **pts, Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST); - /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the - * member conn->proto.http; we want [protocol] through HTTP and we have - * to change the member temporarily for connecting to the HTTP - * proxy. After Curl_proxyCONNECT we have to set back the member to the - * original pointer - * - * This function might be called several times in the multi interface case - * if the proxy's CONNECT response is not instant. - */ - ts->prot_save = data->req.p.http; - data->req.p.http = &ts->http_proxy; *pts = ts; connkeep(conn, "HTTP proxy CONNECT"); return tunnel_reinit(ts, conn, data); @@ -183,34 +174,39 @@ static void tunnel_go_state(struct Curl_cfilter *cf, /* entering this one */ switch(new_state) { case TUNNEL_INIT: + DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); tunnel_reinit(ts, cf->conn, data); break; case TUNNEL_CONNECT: + DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); ts->tunnel_state = TUNNEL_CONNECT; ts->keepon = KEEPON_CONNECT; Curl_dyn_reset(&ts->rcvbuf); break; case TUNNEL_RECEIVE: + DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'")); ts->tunnel_state = TUNNEL_RECEIVE; break; case TUNNEL_RESPONSE: + DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); ts->tunnel_state = TUNNEL_RESPONSE; break; case TUNNEL_ESTABLISHED: + DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); infof(data, "CONNECT phase completed"); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; /* FALLTHROUGH */ case TUNNEL_FAILED: + DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); ts->tunnel_state = new_state; Curl_dyn_reset(&ts->rcvbuf); Curl_dyn_reset(&ts->req); /* restore the protocol pointer */ - data->req.p.http = ts->prot_save; data->info.httpcode = 0; /* clear it as it might've been used for the proxy */ /* If a proxy-authorization header was used for the proxy, then we should @@ -271,10 +267,11 @@ static CURLcode CONNECT_host(struct Curl_easy *data, } #ifndef USE_HYPER -static CURLcode start_CONNECT(struct Curl_easy *data, - struct connectdata *conn, +static CURLcode start_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, struct tunnel_state *ts) { + struct connectdata *conn = cf->conn; char *hostheader = NULL; char *host = NULL; const char *httpv; @@ -338,7 +335,8 @@ static CURLcode start_CONNECT(struct Curl_easy *data, goto out; /* Send the connect request to the proxy */ - result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0, + result = Curl_buffer_send(&ts->req, data, &ts->CONNECT, + &data->info.request_size, 0, ts->sockindex); ts->headerlines = 0; @@ -356,7 +354,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data, bool *done) { struct SingleRequest *k = &data->req; - struct HTTP *http = data->req.p.http; + struct HTTP *http = &ts->CONNECT; CURLcode result = CURLE_OK; if(http->sending != HTTPSEND_REQUEST) @@ -377,7 +375,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data, result = Curl_write(data, conn->writesockfd, /* socket to send to */ k->upload_fromhere, /* buffer pointer */ - ts->nsend, /* buffer size */ + ts->nsend, /* buffer size */ &bytes_written); /* actually sent */ if(result) goto out; @@ -398,13 +396,15 @@ out: return result; } -static CURLcode on_resp_header(struct Curl_easy *data, +static CURLcode on_resp_header(struct Curl_cfilter *cf, + struct Curl_easy *data, struct tunnel_state *ts, const char *header) { CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; int subversion = 0; + (void)cf; if((checkprefix("WWW-Authenticate:", header) && (401 == k->httpcode)) || @@ -416,8 +416,7 @@ static CURLcode on_resp_header(struct Curl_easy *data, if(!auth) return CURLE_OUT_OF_MEMORY; - DEBUGF(infof(data, "CONNECT: fwd auth header '%s'", - header)); + DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header)); result = Curl_http_input_auth(data, proxy, auth); free(auth); @@ -471,14 +470,14 @@ static CURLcode on_resp_header(struct Curl_easy *data, return result; } -static CURLcode recv_CONNECT_resp(struct Curl_easy *data, - struct connectdata *conn, +static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, struct tunnel_state *ts, bool *done) { CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - curl_socket_t tunnelsocket = conn->sock[ts->sockindex]; + curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); char *linep; size_t perline; int error; @@ -634,7 +633,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data, /* without content-length or chunked encoding, we can't keep the connection alive since the close is the end signal so we bail out at once instead */ - DEBUGF(infof(data, "CONNECT: no content-length or chunked")); + DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked")); ts->keepon = KEEPON_DONE; } } @@ -647,7 +646,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data, continue; } - result = on_resp_header(data, ts, linep); + result = on_resp_header(cf, data, ts, linep); if(result) return result; @@ -667,12 +666,13 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data, #else /* USE_HYPER */ /* The Hyper version of CONNECT */ -static CURLcode start_CONNECT(struct Curl_easy *data, - struct connectdata *conn, +static CURLcode start_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, struct tunnel_state *ts) { + struct connectdata *conn = cf->conn; struct hyptransfer *h = &data->hyp; - curl_socket_t tunnelsocket = conn->sock[ts->sockindex]; + curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); hyper_io *io = NULL; hyper_request *req = NULL; hyper_headers *headers = NULL; @@ -914,8 +914,8 @@ error: return result; } -static CURLcode recv_CONNECT_resp(struct Curl_easy *data, - struct connectdata *conn, +static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, struct tunnel_state *ts, bool *done) { @@ -925,7 +925,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data, (void)ts; *done = FALSE; - result = Curl_hyper_stream(data, conn, &didwhat, done, + result = Curl_hyper_stream(data, cf->conn, &didwhat, done, CURL_CSELECT_IN | CURL_CSELECT_OUT); if(result || !*done) return result; @@ -972,7 +972,8 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, switch(ts->tunnel_state) { case TUNNEL_INIT: /* Prepare the CONNECT request and make a first attempt to send. */ - result = start_CONNECT(data, cf->conn, ts); + DEBUGF(LOG_CF(data, cf, "CONNECT start")); + result = start_CONNECT(cf, data, ts); if(result) goto out; tunnel_go_state(cf, ts, TUNNEL_CONNECT, data); @@ -980,6 +981,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, case TUNNEL_CONNECT: /* see that the request is completely sent */ + DEBUGF(LOG_CF(data, cf, "CONNECT send")); result = send_CONNECT(data, cf->conn, ts, &done); if(result || !done) goto out; @@ -988,7 +990,8 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, case TUNNEL_RECEIVE: /* read what is there */ - result = recv_CONNECT_resp(data, cf->conn, ts, &done); + DEBUGF(LOG_CF(data, cf, "CONNECT receive")); + result = recv_CONNECT_resp(cf, data, ts, &done); if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; goto out; @@ -1001,24 +1004,29 @@ static CURLcode CONNECT(struct Curl_cfilter *cf, /* FALLTHROUGH */ case TUNNEL_RESPONSE: + DEBUGF(LOG_CF(data, cf, "CONNECT response")); if(data->req.newurl) { /* not the "final" response, we need to do a follow up request. * If the other side indicated a connection close, or if someone - * else told us to close this connection, do so now. */ + * else told us to close this connection, do so now. + */ if(ts->close_connection || conn->bits.close) { - /* Close the filter chain and trigger connect, non-blocking - * again, so the process is ongoing. This will - * a) the close resets our tunnel state - * b) the connect makes sure that there will be a socket - * to select on again. - * We return and expect to be called again. */ + /* Close this filter and the sub-chain, re-connect the + * sub-chain and continue. Closing this filter will + * reset our tunnel state. To avoid recursion, we return + * and expect to be called again. + */ + DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open")); infof(data, "Connect me again please"); - Curl_conn_close(data, cf->sockindex); - result = cf->next->cft->connect(cf->next, data, FALSE, &done); + Curl_conn_cf_close(cf, data); + connkeep(conn, "HTTP proxy CONNECT"); + result = Curl_conn_cf_connect(cf->next, data, FALSE, &done); goto out; } - /* staying on this connection, reset state */ - tunnel_go_state(cf, ts, TUNNEL_INIT, data); + else { + /* staying on this connection, reset state */ + tunnel_go_state(cf, ts, TUNNEL_INIT, data); + } } break; @@ -1063,10 +1071,12 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } + DEBUGF(LOG_CF(data, cf, "connect")); result = cf->next->cft->connect(cf->next, data, blocking, done); if(result || !*done) return result; + DEBUGF(LOG_CF(data, cf, "subchain is connected")); /* TODO: can we do blocking? */ /* We want "seamless" operations through HTTP proxy tunnel */ @@ -1117,22 +1127,21 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf, curl_socket_t *socks) { struct tunnel_state *ts = cf->ctx; - struct connectdata *conn = cf->conn; int fds; - DEBUGASSERT(conn); fds = cf->next->cft->get_select_socks(cf->next, data, socks); if(!fds && cf->next->connected && !cf->connected) { /* If we are not connected, but the filter "below" is * and not waiting on something, we are tunneling. */ - socks[0] = conn->sock[cf->sockindex]; + socks[0] = Curl_conn_cf_get_socket(cf, data); if(ts) { /* when we've sent a CONNECT to a proxy, we should rather either wait for the socket to become readable to be able to get the response headers or if we're still sending the request, wait for write. */ - if(ts->http_proxy.sending == HTTPSEND_REQUEST) + if(ts->CONNECT.sending == HTTPSEND_REQUEST) { return GETSOCK_WRITESOCK(0); + } return GETSOCK_READSOCK(0); } return GETSOCK_WRITESOCK(0); @@ -1140,24 +1149,18 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf, return fds; } -static void http_proxy_cf_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - if(cf->ctx) { - tunnel_free(cf, data); - } -} - static void http_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - http_proxy_cf_detach_data(cf, data); + DEBUGF(LOG_CF(data, cf, "destroy")); + tunnel_free(cf, data); } static void http_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { DEBUGASSERT(cf->next); + DEBUGF(LOG_CF(data, cf, "close")); cf->connected = FALSE; cf->next->cft->close(cf->next, data); if(cf->ctx) { @@ -1166,11 +1169,11 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf, } -static const struct Curl_cftype cft_http_proxy = { +struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", CF_TYPE_IP_CONNECT, + 0, http_proxy_cf_destroy, - Curl_cf_def_setup, http_proxy_cf_connect, http_proxy_cf_close, http_proxy_cf_get_host, @@ -1178,8 +1181,10 @@ static const struct Curl_cftype cft_http_proxy = { Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, - Curl_cf_def_attach_data, - http_proxy_cf_detach_data, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, }; CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data, @@ -1189,31 +1194,73 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data, struct Curl_cfilter *cf; CURLcode result; - result = Curl_cf_create(&cf, &cft_http_proxy, NULL); + result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL); if(!result) Curl_conn_cf_add(data, conn, sockindex, cf); return result; } +CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + (void)data; + result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#endif /* ! CURL_DISABLE_HTTP */ -static CURLcode send_haproxy_header(struct Curl_cfilter*cf, - struct Curl_easy *data) + +typedef enum { + HAPROXY_INIT, /* init/default/no tunnel state */ + HAPROXY_SEND, /* data_out being sent */ + HAPROXY_DONE /* all work done */ +} haproxy_state; + +struct cf_haproxy_ctx { + int state; + struct dynbuf data_out; +}; + +static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) { - struct dynbuf req; + DEBUGASSERT(ctx); + ctx->state = HAPROXY_INIT; + Curl_dyn_reset(&ctx->data_out); +} + +static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) +{ + if(ctx) { + Curl_dyn_free(&ctx->data_out); + free(ctx); + } +} + +static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, + struct Curl_easy *data) +{ + struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; const char *tcp_version; - Curl_dyn_init(&req, DYN_HAXPROXY); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->state == HAPROXY_INIT); #ifdef USE_UNIX_SOCKETS if(cf->conn->unix_domain_socket) /* the buffer is large enough to hold this! */ - result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n")); + result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); else { #endif /* USE_UNIX_SOCKETS */ /* Emit the correct prefix for IPv6 */ tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; - result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n", + result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", tcp_version, data->info.conn_local_ip, data->info.conn_primary_ip, @@ -1223,19 +1270,18 @@ static CURLcode send_haproxy_header(struct Curl_cfilter*cf, #ifdef USE_UNIX_SOCKETS } #endif /* USE_UNIX_SOCKETS */ - - if(!result) - result = Curl_buffer_send(&req, data, &data->info.request_size, - 0, FIRSTSOCKET); return result; } -static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf, +static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) { + struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; + size_t len; + DEBUGASSERT(ctx); if(cf->connected) { *done = TRUE; return CURLE_OK; @@ -1245,28 +1291,120 @@ static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf, if(result || !*done) return result; - result = send_haproxy_header(cf, data); - *done = (!result); + switch(ctx->state) { + case HAPROXY_INIT: + result = cf_haproxy_date_out_set(cf, data); + if(result) + goto out; + ctx->state = HAPROXY_SEND; + /* FALLTHROUGH */ + 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) + goto out; + Curl_dyn_tail(&ctx->data_out, len - (size_t)written); + if(Curl_dyn_len(&ctx->data_out) > 0) { + result = CURLE_OK; + goto out; + } + } + ctx->state = HAPROXY_DONE; + /* FALLTHROUGH */ + default: + Curl_dyn_free(&ctx->data_out); + break; + } + +out: + *done = (!result) && (ctx->state == HAPROXY_DONE); cf->connected = *done; return result; } -static const struct Curl_cftype cft_haproxy = { +static void cf_haproxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + DEBUGF(LOG_CF(data, cf, "destroy")); + cf_haproxy_ctx_free(cf->ctx); +} + +static void cf_haproxy_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + DEBUGF(LOG_CF(data, cf, "close")); + cf->connected = FALSE; + cf_haproxy_ctx_reset(cf->ctx); + if(cf->next) + cf->next->cft->close(cf->next, data); +} + +static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + int fds; + + fds = cf->next->cft->get_select_socks(cf->next, data, socks); + if(!fds && cf->next->connected && !cf->connected) { + /* If we are not connected, but the filter "below" is + * and not waiting on something, we are sending. */ + socks[0] = Curl_conn_cf_get_socket(cf, data); + return GETSOCK_WRITESOCK(0); + } + return fds; +} + + +struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", 0, - Curl_cf_def_destroy_this, - Curl_cf_def_setup, - haproxy_cf_connect, - Curl_cf_def_close, + 0, + cf_haproxy_destroy, + cf_haproxy_connect, + cf_haproxy_close, Curl_cf_def_get_host, - Curl_cf_def_get_select_socks, + cf_haproxy_get_select_socks, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, - Curl_cf_def_attach_data, - Curl_cf_def_detach_data, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, }; +static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf = NULL; + struct cf_haproxy_ctx *ctx; + CURLcode result; + + (void)data; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->state = HAPROXY_INIT; + Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY); + + result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx); + if(result) + goto out; + ctx = NULL; + +out: + cf_haproxy_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; +} + CURLcode Curl_conn_haproxy_add(struct Curl_easy *data, struct connectdata *conn, int sockindex) @@ -1274,10 +1412,28 @@ CURLcode Curl_conn_haproxy_add(struct Curl_easy *data, struct Curl_cfilter *cf; CURLcode result; - result = Curl_cf_create(&cf, &cft_haproxy, NULL); - if(!result) - Curl_conn_cf_add(data, conn, sockindex, cf); + result = cf_haproxy_create(&cf, data); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); + +out: + return result; +} + +CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_haproxy_create(&cf, data); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); + +out: return result; } -#endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_PROXY */ diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h index dfdc0e7..f573da2 100644 --- a/Utilities/cmcurl/lib/http_proxy.h +++ b/Utilities/cmcurl/lib/http_proxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -27,8 +27,9 @@ #include "curl_setup.h" #include "urldata.h" -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +#if !defined(CURL_DISABLE_PROXY) +#if !defined(CURL_DISABLE_HTTP) /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600*1000) @@ -36,10 +37,22 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data, struct connectdata *conn, int sockindex); +CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_http_proxy; + +#endif /* !CURL_DISABLE_HTTP */ + CURLcode Curl_conn_haproxy_add(struct Curl_easy *data, struct connectdata *conn, int sockindex); -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ +CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_haproxy; + +#endif /* !CURL_DISABLE_PROXY */ #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index 6255221..abba895 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -116,7 +116,7 @@ bool Curl_is_ASCII_name(const char *hostname) * Curl_idn_decode() returns an allocated IDN decoded string if it was * possible. NULL on error. */ -static char *Curl_idn_decode(const char *input) +static char *idn_decode(const char *input) { char *decoded = NULL; #ifdef USE_LIBIDN2 @@ -144,24 +144,29 @@ static char *Curl_idn_decode(const char *input) return decoded; } +char *Curl_idn_decode(const char *input) +{ + char *d = idn_decode(input); +#ifdef USE_LIBIDN2 + if(d) { + char *c = strdup(d); + idn2_free(d); + d = c; + } +#endif + return d; +} + /* * Frees data allocated by idnconvert_hostname() */ void Curl_free_idnconverted_hostname(struct hostname *host) { -#if defined(USE_LIBIDN2) if(host->encalloc) { - idn2_free(host->encalloc); /* must be freed with idn2_free() since this was - allocated by libidn */ + /* must be freed with idn2_free() if allocated by libidn */ + Curl_idn_free(host->encalloc); host->encalloc = NULL; } -#elif defined(USE_WIN32_IDN) - free(host->encalloc); /* must be freed with free() since this was - allocated by Curl_win32_idn_to_ascii */ - host->encalloc = NULL; -#else - (void)host; -#endif } #endif /* USE_IDN */ @@ -177,7 +182,7 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) #ifdef USE_IDN /* Check name for non-ASCII and convert hostname if we can */ if(!Curl_is_ASCII_name(host->name)) { - char *decoded = Curl_idn_decode(host->name); + char *decoded = idn_decode(host->name); if(decoded) { /* successful */ host->encalloc = decoded; @@ -190,4 +195,3 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) #endif return CURLE_OK; } - diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h index 2d04efc..6c0bbb7 100644 --- a/Utilities/cmcurl/lib/idn.h +++ b/Utilities/cmcurl/lib/idn.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -32,7 +32,15 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host); #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) #define USE_IDN void Curl_free_idnconverted_hostname(struct hostname *host); +char *Curl_idn_decode(const char *input); +#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) +#define Curl_idn_decode(x) NULL #endif #endif /* HEADER_CURL_IDN_H */ diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index c291948..6bf0ce1 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/if2ip.h b/Utilities/cmcurl/lib/if2ip.h index 5d15459..1f97350 100644 --- a/Utilities/cmcurl/lib/if2ip.h +++ b/Utilities/cmcurl/lib/if2ip.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2020, 2022, Daniel Stenberg, <daniel@haxx.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/imap.c b/Utilities/cmcurl/lib/imap.c index bd4c6f2..c2f675d 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -475,15 +475,17 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, /* Start the SSL connection */ struct imap_conn *imapc = &conn->proto.imapc; CURLcode result; + bool ssldone = FALSE; - if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) goto out; } - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone); + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); if(!result) { + imapc->ssldone = ssldone; if(imapc->state != IMAP_UPGRADETLS) state(data, IMAP_UPGRADETLS); @@ -952,7 +954,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, line += wordlen; } } - else if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) { + else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1386,8 +1388,10 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done) struct imap_conn *imapc = &conn->proto.imapc; if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone); - if(result || !imapc->ssldone) + bool ssldone = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + imapc->ssldone = ssldone; + if(result || !ssldone) return result; } @@ -1774,7 +1778,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) /* Calculate the tag based on the connection ID and command ID */ msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", 'A' + curlx_sltosi(data->conn->connection_id % 26), - (++imapc->cmdid)%1000); + ++imapc->cmdid); /* start with a blank buffer */ Curl_dyn_reset(&imapc->dyn); diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h index 43cc1e9..784ee97 100644 --- a/Utilities/cmcurl/lib/imap.h +++ b/Utilities/cmcurl/lib/imap.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -72,19 +72,19 @@ struct IMAP { struct */ struct imap_conn { struct pingpong pp; - imapstate state; /* Always use imap.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ - bool preauth; /* Is this connection PREAUTH? */ struct SASL sasl; /* SASL-related parameters */ - unsigned int preftype; /* Preferred authentication type */ - unsigned int cmdid; /* Last used command ID */ - char resptag[5]; /* Response tag to wait for */ - bool tls_supported; /* StartTLS capability supported by server */ - bool login_disabled; /* LOGIN command disabled by server */ - bool ir_supported; /* Initial response supported by server */ + struct dynbuf dyn; /* for the IMAP commands */ char *mailbox; /* The last selected mailbox */ char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ - struct dynbuf dyn; /* for the IMAP commands */ + imapstate state; /* Always use imap.c:state() to change state! */ + char resptag[5]; /* Response tag to wait for */ + unsigned char preftype; /* Preferred authentication type */ + unsigned char cmdid; /* Last used command ID */ + BIT(ssldone); /* Is connect() over SSL done? */ + BIT(preauth); /* Is this connection PREAUTH? */ + BIT(tls_supported); /* StartTLS capability supported by server */ + BIT(login_disabled); /* LOGIN command disabled by server */ + BIT(ir_supported); /* Initial response supported by server */ }; extern const struct Curl_handler Curl_handler_imap; @@ -96,6 +96,6 @@ extern const struct Curl_handler Curl_handler_imaps; /* Authentication type values */ #define IMAP_TYPE_NONE 0 -#define IMAP_TYPE_ANY ~0U +#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL) #endif /* HEADER_CURL_IMAP_H */ diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h index 18fbd8b..7c3ead4 100644 --- a/Utilities/cmcurl/lib/inet_ntop.h +++ b/Utilities/cmcurl/lib/inet_ntop.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c index 47fb778..f2e17b8 100644 --- a/Utilities/cmcurl/lib/inet_pton.c +++ b/Utilities/cmcurl/lib/inet_pton.c @@ -1,6 +1,6 @@ /* This is from the BIND 4.9.4 release, modified to compile by itself */ -/* Copyright (c) 2003 - 2022 by Internet Software Consortium. +/* Copyright (c) Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h index 92ae93e..82fde7e 100644 --- a/Utilities/cmcurl/lib/inet_pton.h +++ b/Utilities/cmcurl/lib/inet_pton.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/krb5.c b/Utilities/cmcurl/lib/krb5.c index 08a6825..3cd64e1 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -2,7 +2,7 @@ * * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (c) 2004 - 2022 Daniel Stenberg + * Copyright (C) Daniel Stenberg * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -46,6 +46,8 @@ #endif #include "urldata.h" +#include "cfilters.h" +#include "cf-socket.h" #include "curl_base64.h" #include "ftp.h" #include "curl_gssapi.h" @@ -207,8 +209,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) gss_ctx_id_t *context = app_data; struct gss_channel_bindings_struct chan; size_t base64_sz = 0; - struct sockaddr_in **remote_addr = - (struct sockaddr_in **)&conn->ip_addr->ai_addr; + struct sockaddr_in *remote_addr = + (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr; char *stringp; if(getsockname(conn->sock[FIRSTSOCKET], @@ -220,7 +222,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; chan.acceptor_addrtype = GSS_C_AF_INET; chan.acceptor_address.length = l - 4; - chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr; + chan.acceptor_address.value = &remote_addr->sin_addr.s_addr; chan.application_data.length = 0; chan.application_data.value = NULL; @@ -454,15 +456,15 @@ static int ftp_send_command(struct Curl_easy *data, const char *message, ...) /* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode saying whether an error occurred or CURLE_OK if |len| was read. */ static CURLcode -socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len) +socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) { char *to_p = to; CURLcode result; ssize_t nread = 0; while(len > 0) { - result = Curl_read_plain(data, fd, to_p, len, &nread); - if(!result) { + nread = Curl_conn_recv(data, sockindex, to_p, len, &result); + if(nread > 0) { len -= nread; to_p += nread; } @@ -480,7 +482,7 @@ socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len) CURLcode saying whether an error occurred or CURLE_OK if |len| was written. */ static CURLcode -socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, +socket_write(struct Curl_easy *data, int sockindex, const void *to, size_t len) { const char *to_p = to; @@ -488,8 +490,8 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, ssize_t written; while(len > 0) { - result = Curl_write_plain(data, fd, to_p, len, &written); - if(!result) { + written = Curl_conn_send(data, sockindex, to_p, len, &result); + if(written > 0) { len -= written; to_p += written; } @@ -502,7 +504,7 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, return CURLE_OK; } -static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd, +static CURLcode read_data(struct Curl_easy *data, int sockindex, struct krb5buffer *buf) { struct connectdata *conn = data->conn; @@ -510,7 +512,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd, CURLcode result; int nread; - result = socket_read(data, fd, &len, sizeof(len)); + result = socket_read(data, sockindex, &len, sizeof(len)); if(result) return result; @@ -525,7 +527,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd, if(!len || !buf->data) return CURLE_OUT_OF_MEMORY; - result = socket_read(data, fd, buf->data, len); + result = socket_read(data, sockindex, buf->data, len); if(result) return result; nread = conn->mech->decode(conn->app_data, buf->data, len, @@ -554,13 +556,12 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, size_t bytes_read; size_t total_read = 0; struct connectdata *conn = data->conn; - curl_socket_t fd = conn->sock[sockindex]; *err = CURLE_OK; /* Handle clear text response. */ if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return Curl_recv_plain(data, sockindex, buffer, len, err); + return Curl_conn_recv(data, sockindex, buffer, len, err); if(conn->in_buffer.eof_flag) { conn->in_buffer.eof_flag = 0; @@ -573,7 +574,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, buffer += bytes_read; while(len > 0) { - if(read_data(data, fd, &conn->in_buffer)) + if(read_data(data, sockindex, &conn->in_buffer)) return -1; if(conn->in_buffer.size == 0) { if(bytes_read > 0) diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 92006d6..5e53f4c 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc index 23134a7..daa2d62 100644 --- a/Utilities/cmcurl/lib/libcurl.rc +++ b/Utilities/cmcurl/lib/libcurl.rc @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/llist.c b/Utilities/cmcurl/lib/llist.c index fa2d366..5b6b033 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/llist.h b/Utilities/cmcurl/lib/llist.h index 2fcb91c..320580e 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/md4.c b/Utilities/cmcurl/lib/md4.c index c13b080..318e9da 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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,11 +86,7 @@ #include "memdebug.h" -#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) - -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) - -#elif defined(USE_GNUTLS) +#if defined(USE_GNUTLS) typedef struct md4_ctx MD4_CTX; @@ -109,6 +105,10 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) md4_digest(ctx, MD4_DIGEST_SIZE, result); } +#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) + +#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) + #elif defined(AN_APPLE_OS) typedef CC_MD4_CTX MD4_CTX; diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index 9610e0c..f57ef39 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index 15fb491..d6952a0 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/memdebug.h b/Utilities/cmcurl/lib/memdebug.h index 7fc90e8..c9eb5dc 100644 --- a/Utilities/cmcurl/lib/memdebug.h +++ b/Utilities/cmcurl/lib/memdebug.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/mime.c b/Utilities/cmcurl/lib/mime.c index e3f2821d..83846c5 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/mime.h b/Utilities/cmcurl/lib/mime.h index b9ea0f1..04adf2d 100644 --- a/Utilities/cmcurl/lib/mime.h +++ b/Utilities/cmcurl/lib/mime.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index 8a7c17a..5de935b 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1999 - 2022, Daniel Stenberg, <daniel@haxx.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/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 8ba826f..0b54bc0 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2019, Björn Stenberg, <bjorn@haxx.se> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Björn Stenberg, <bjorn@haxx.se> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h index c400d9b..6396136 100644 --- a/Utilities/cmcurl/lib/mqtt.h +++ b/Utilities/cmcurl/lib/mqtt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Björn Stenberg, <bjorn@haxx.se> + * Copyright (C) Björn Stenberg, <bjorn@haxx.se> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index b96ee7c..f020a0b 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -655,6 +655,9 @@ static CURLcode multi_done(struct Curl_easy *data, result = CURLE_ABORTED_BY_CALLBACK; } + /* Inform connection filters that this transfer is done */ + Curl_conn_ev_data_done(data, premature); + process_pending_handles(data->multi); /* connection / multiplex */ CONNCACHE_LOCK(data); @@ -709,12 +712,12 @@ static CURLcode multi_done(struct Curl_easy *data, conn->proxy_negotiate_state == GSS_AUTHRECV) #endif ) || conn->bits.close - || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { + || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d" - ", close=%d, premature=%d, stream=%d", + ", close=%d, premature=%d, conn_multiplex=%d", conn->connection_id, data->set.reuse_forbid, conn->bits.close, premature, - (conn->handler->flags & PROTOPT_STREAM))); + Curl_conn_is_multiplex(conn, FIRSTSOCKET))); connclose(conn, "disconnecting"); Curl_conncache_remove_conn(data, conn, FALSE); CONNCACHE_UNLOCK(data); @@ -954,7 +957,7 @@ void Curl_detach_connection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) { - Curl_conn_detach_data(conn, data); + Curl_conn_ev_data_detach(conn, data); Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); } data->conn = NULL; @@ -973,9 +976,9 @@ void Curl_attach_connection(struct Curl_easy *data, data->conn = conn; Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, &data->conn_queue); - Curl_conn_attach_data(conn, data); if(conn->handler->attach) conn->handler->attach(data, conn); + Curl_conn_ev_data_attach(conn, data); } static int domore_getsock(struct Curl_easy *data, @@ -1002,11 +1005,7 @@ static int protocol_getsock(struct Curl_easy *data, { if(conn->handler->proto_getsock) return conn->handler->proto_getsock(data, conn, socks); - /* Backup getsock logic. Since there is a live socket in use, we must wait - for it or it will be removed from watching when the multi_socket API is - used. */ - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); + return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); } /* returns bitmapped flags for this handle and its sockets. The 'socks[]' @@ -1111,6 +1110,22 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, return CURLM_OK; } +#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 + * + * "On Windows the internal state of FD_WRITE as returned from + * WSAEnumNetworkEvents is only reset after successful send()." + */ +static void reset_socket_fdwrite(curl_socket_t s) +{ + int t; + int l = (int)sizeof(t); + if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) + send(s, NULL, 0, 0); +} +#endif + #define NUM_POLLS_ON_STACK 10 static CURLMcode multi_wait(struct Curl_multi *multi, @@ -1232,7 +1247,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, s = sockbunch[i]; #ifdef USE_WINSOCK mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - send(s, NULL, 0, 0); /* reset FD_WRITE */ + reset_socket_fdwrite(s); #endif ufds[nfds].fd = s; ufds[nfds].events = POLLOUT; @@ -1266,7 +1281,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, mask |= FD_OOB; if(extra_fds[i].events & CURL_WAIT_POLLOUT) { mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - send(extra_fds[i].fd, NULL, 0, 0); /* reset FD_WRITE */ + reset_socket_fdwrite(extra_fds[i].fd); } if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) { if(ufds_malloc) @@ -1862,6 +1877,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multistate(data, MSTATE_COMPLETED); } +#ifdef DEBUGBUILD + if(!multi->warned) { + infof(data, "!!! WARNING !!!"); + infof(data, "This is a debug build of libcurl, " + "do not use in production."); + multi->warned = true; + } +#endif + do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ @@ -3248,7 +3272,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, multi->push_userp = va_arg(param, void *); break; case CURLMOPT_PIPELINING: - multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX; + multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; break; case CURLMOPT_TIMERFUNCTION: multi->timer_cb = va_arg(param, curl_multi_timer_callback); diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 5a83656..6cda65d 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -162,14 +162,17 @@ struct Curl_multi { #define IPV6_DEAD 1 #define IPV6_WORKS 2 unsigned char ipv6_up; /* IPV6_* defined */ - bool multiplexing; /* multiplexing wanted */ - bool recheckstate; /* see Curl_multi_connchanged */ - bool in_callback; /* true while executing a callback */ + BIT(multiplexing); /* multiplexing wanted */ + BIT(recheckstate); /* see Curl_multi_connchanged */ + BIT(in_callback); /* true while executing a callback */ #ifdef USE_OPENSSL - bool ssl_seeded; + BIT(ssl_seeded); #endif - bool dead; /* a callback returned error, everything needs to crash and + BIT(dead); /* a callback returned error, everything needs to crash and burn */ +#ifdef DEBUGBUILD + BIT(warned); /* true after user warned of DEBUGBUILD */ +#endif }; #endif /* HEADER_CURL_MULTIHANDLE_H */ diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h index 0cb9d4f..cae02cb 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/netrc.c b/Utilities/cmcurl/lib/netrc.c index 4461b84..aa1b80a 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/netrc.h b/Utilities/cmcurl/lib/netrc.h index 53d0056..9f2815f 100644 --- a/Utilities/cmcurl/lib/netrc.h +++ b/Utilities/cmcurl/lib/netrc.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/nonblock.c b/Utilities/cmcurl/lib/nonblock.c index 8447b6f..f4eb656 100644 --- a/Utilities/cmcurl/lib/nonblock.c +++ b/Utilities/cmcurl/lib/nonblock.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/nonblock.h b/Utilities/cmcurl/lib/nonblock.h index a42f443..4a1a615 100644 --- a/Utilities/cmcurl/lib/nonblock.h +++ b/Utilities/cmcurl/lib/nonblock.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/noproxy.c b/Utilities/cmcurl/lib/noproxy.c index 9b13fe8..f1c1ed2 100644 --- a/Utilities/cmcurl/lib/noproxy.c +++ b/Utilities/cmcurl/lib/noproxy.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -119,8 +119,10 @@ enum nametype { * Checks if the host is in the noproxy list. returns TRUE if it matches and * therefore the proxy should NOT be used. ****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy) +bool Curl_check_noproxy(const char *name, const char *no_proxy, + bool *spacesep) { + *spacesep = FALSE; /* * If we don't have a hostname at all, like for example with a FILE * transfer, we have nothing to interrogate the noproxy list with. @@ -244,6 +246,15 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) if(match) return TRUE; } /* if(tokenlen) */ + /* pass blanks after pattern */ + while(ISBLANK(*p)) + p++; + /* if not a comma! */ + if(*p && (*p != ',')) { + *spacesep = TRUE; + continue; + } + /* pass any number of commas */ while(*p == ',') p++; } /* while(*p) */ diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h index 8800a21..a3a6807 100644 --- a/Utilities/cmcurl/lib/noproxy.h +++ b/Utilities/cmcurl/lib/noproxy.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -37,7 +37,8 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6, unsigned int bits); #endif -bool Curl_check_noproxy(const char *name, const char *no_proxy); +bool Curl_check_noproxy(const char *name, const char *no_proxy, + bool *spacesep); #endif diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index ab81e57..b9feeda 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, Howard Chu, <hyc@openldap.org> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Howard Chu, <hyc@openldap.org> * * 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/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index 5ed8819..82ac1d8 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/parsedate.h b/Utilities/cmcurl/lib/parsedate.h index 4e43477..84c37f1 100644 --- a/Utilities/cmcurl/lib/parsedate.h +++ b/Utilities/cmcurl/lib/parsedate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index 9b95580..2f4aa1c 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index cefae07..80d3f77 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/pop3.c b/Utilities/cmcurl/lib/pop3.c index ce17f2a..36707e5 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -370,16 +370,18 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, /* Start the SSL connection */ struct pop3_conn *pop3c = &conn->proto.pop3c; CURLcode result; + bool ssldone = FALSE; - if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) goto out; } - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone); + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); if(!result) { + pop3c->ssldone = ssldone; if(pop3c->state != POP3_UPGRADETLS) state(data, POP3_UPGRADETLS); @@ -769,7 +771,7 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, if(pop3code != '+') pop3c->authtypes |= POP3_TYPE_CLEARTEXT; - if(!data->set.use_ssl || Curl_conn_is_ssl(data, FIRSTSOCKET)) + if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET)) result = pop3_perform_authentication(data, conn); else if(pop3code == '+' && pop3c->tls_supported) /* Switch to TLS connection now */ @@ -1056,7 +1058,9 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) struct pop3_conn *pop3c = &conn->proto.pop3c; if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone); + bool ssldone = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + pop3c->ssldone = ssldone; if(result || !pop3c->ssldone) return result; } diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h index bb0645f..83f0f83 100644 --- a/Utilities/cmcurl/lib/pop3.h +++ b/Utilities/cmcurl/lib/pop3.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -62,16 +62,16 @@ struct POP3 { struct pop3_conn { struct pingpong pp; pop3state state; /* Always use pop3.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ - bool tls_supported; /* StartTLS capability supported by server */ size_t eob; /* Number of bytes of the EOB (End Of Body) that have been received so far */ size_t strip; /* Number of bytes from the start to ignore as non-body */ struct SASL sasl; /* SASL-related storage */ - unsigned int authtypes; /* Accepted authentication types */ - unsigned int preftype; /* Preferred authentication type */ char *apoptimestamp; /* APOP timestamp from the server greeting */ + unsigned char authtypes; /* Accepted authentication types */ + unsigned char preftype; /* Preferred authentication type */ + BIT(ssldone); /* Is connect() over SSL done? */ + BIT(tls_supported); /* StartTLS capability supported by server */ }; extern const struct Curl_handler Curl_handler_pop3; @@ -84,7 +84,7 @@ extern const struct Curl_handler Curl_handler_pop3s; /* Authentication type values */ #define POP3_TYPE_NONE 0 -#define POP3_TYPE_ANY ~0U +#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL) /* This is the 5-bytes End-Of-Body marker for POP3 */ #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index 4a1e1da..a222888 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -166,14 +166,11 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data) /* * - * Curl_pgrsTime(). Store the current time at the given label. This fetches a - * fresh "now" and returns it. - * - * @unittest: 1399 + * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ -struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) +void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, + struct curltime timestamp) { - struct curltime now = Curl_now(); timediff_t *delta = NULL; switch(timer) { @@ -183,15 +180,15 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) break; case TIMER_STARTOP: /* This is set at the start of a transfer */ - data->progress.t_startop = now; + data->progress.t_startop = timestamp; break; case TIMER_STARTSINGLE: /* This is set at the start of each single fetch */ - data->progress.t_startsingle = now; + data->progress.t_startsingle = timestamp; data->progress.is_t_startransfer_set = false; break; case TIMER_STARTACCEPT: - data->progress.t_acceptdata = now; + data->progress.t_acceptdata = timestamp; break; case TIMER_NAMELOOKUP: delta = &data->progress.t_nslookup; @@ -214,7 +211,7 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) * changing the t_starttransfer time. */ if(data->progress.is_t_startransfer_set) { - return now; + return; } else { data->progress.is_t_startransfer_set = true; @@ -224,15 +221,30 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) /* this is the normal end-of-transfer thing */ break; case TIMER_REDIRECT: - data->progress.t_redirect = Curl_timediff_us(now, data->progress.start); + data->progress.t_redirect = Curl_timediff_us(timestamp, + data->progress.start); break; } if(delta) { - timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle); + timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle); if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; } +} + +/* + * + * Curl_pgrsTime(). Store the current time at the given label. This fetches a + * fresh "now" and returns it. + * + * @unittest: 1399 + */ +struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) +{ + struct curltime now = Curl_now(); + + Curl_pgrsTimeWas(data, timer, now); return now; } diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h index a129315..0049cd0 100644 --- a/Utilities/cmcurl/lib/progress.h +++ b/Utilities/cmcurl/lib/progress.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -57,6 +57,13 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, curl_off_t limit, struct curltime start, struct curltime now); +/** + * Update progress timer with the elapsed time from its start to `timestamp`. + * This allows updating timers later and is used by happy eyeballing, where + * we only want to record the winner's times. + */ +void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, + struct curltime timestamp); #define PGRS_HIDE (1<<4) #define PGRS_UL_SIZE_KNOWN (1<<5) diff --git a/Utilities/cmcurl/lib/psl.c b/Utilities/cmcurl/lib/psl.c index 60c98a4..626a203 100644 --- a/Utilities/cmcurl/lib/psl.c +++ b/Utilities/cmcurl/lib/psl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/psl.h b/Utilities/cmcurl/lib/psl.h index 34f0a5c..23cfa92 100644 --- a/Utilities/cmcurl/lib/psl.h +++ b/Utilities/cmcurl/lib/psl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/quic.h b/Utilities/cmcurl/lib/quic.h deleted file mode 100644 index b357747..0000000 --- a/Utilities/cmcurl/lib/quic.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef HEADER_CURL_QUIC_H -#define HEADER_CURL_QUIC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef ENABLE_QUIC -#ifdef USE_NGTCP2 -#include "vquic/ngtcp2.h" -#endif -#ifdef USE_QUICHE -#include "vquic/quiche.h" -#endif -#ifdef USE_MSH3 -#include "vquic/msh3.h" -#endif - -#include "urldata.h" - -/* functions provided by the specific backends */ -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, - socklen_t addrlen); -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected); -void Curl_quic_ver(char *p, size_t len); -CURLcode Curl_quic_done_sending(struct Curl_easy *data); -void Curl_quic_done(struct Curl_easy *data, bool premature); -bool Curl_quic_data_pending(const struct Curl_easy *data); -void Curl_quic_disconnect(struct Curl_easy *data, - struct connectdata *conn, int tempindex); -CURLcode Curl_quic_idle(struct Curl_easy *data); - -#else /* ENABLE_QUIC */ -#define Curl_quic_done_sending(x) -#define Curl_quic_done(x,y) -#define Curl_quic_data_pending(x) -#define Curl_quic_disconnect(x,y,z) -#endif /* !ENABLE_QUIC */ - -#endif /* HEADER_CURL_QUIC_H */ diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index a549624..4b6ac07 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/rand.h b/Utilities/cmcurl/lib/rand.h index 30fc296..cbe0567 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/rename.c b/Utilities/cmcurl/lib/rename.c index cfb3699..97a66e9 100644 --- a/Utilities/cmcurl/lib/rename.c +++ b/Utilities/cmcurl/lib/rename.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.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/rename.h b/Utilities/cmcurl/lib/rename.h index 9958e2c..0444082 100644 --- a/Utilities/cmcurl/lib/rename.h +++ b/Utilities/cmcurl/lib/rename.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.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/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 75e620d..9d27929 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -38,6 +38,7 @@ #include "strcase.h" #include "select.h" #include "connect.h" +#include "cfilters.h" #include "strdup.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -134,36 +135,6 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data, /* - * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not - * want to block the application forever while receiving a stream. Therefore, - * we cannot assume that an RTSP socket is dead just because it is readable. - * - * Instead, if it is readable, run Curl_connalive() to peek at the socket - * and distinguish between closed and data. - */ -static bool rtsp_connisdead(struct Curl_easy *data, struct connectdata *check) -{ - int sval; - bool ret_val = TRUE; - - sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0); - if(sval == 0) { - /* timeout */ - ret_val = FALSE; - } - else if(sval & CURL_CSELECT_ERR) { - /* socket is in an error state */ - ret_val = TRUE; - } - else if(sval & CURL_CSELECT_IN) { - /* readable with no error. could still be closed */ - ret_val = !Curl_connalive(data, check); - } - - return ret_val; -} - -/* * Function to check on various aspects of a connection. */ static unsigned int rtsp_conncheck(struct Curl_easy *data, @@ -174,7 +145,7 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data, (void)data; if(checks_to_perform & CONNCHECK_ISDEAD) { - if(rtsp_connisdead(data, conn)) + if(!Curl_conn_is_alive(data, conn)) ret_val |= CONNRESULT_DEAD; } @@ -592,7 +563,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) } /* issue the request */ - result = Curl_buffer_send(&req_buffer, data, + result = Curl_buffer_send(&req_buffer, data, data->req.p.http, &data->info.request_size, 0, FIRSTSOCKET); if(result) { failf(data, "Failed sending RTSP request"); diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index fa6606a..6e55616 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/select.c b/Utilities/cmcurl/lib/select.c index 2ac0746..3b8d468 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/select.h b/Utilities/cmcurl/lib/select.h index f2cf8bb..5b1ca23 100644 --- a/Utilities/cmcurl/lib/select.h +++ b/Utilities/cmcurl/lib/select.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/sendf.c b/Utilities/cmcurl/lib/sendf.c index 6326240..2b08271 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -138,149 +138,6 @@ static size_t convert_lineends(struct Curl_easy *data, } #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) -{ - struct postponed_data * const psnd = &(conn->postponed[sockindex]); - return psnd->buffer && psnd->allocated_size && - psnd->recv_size > psnd->recv_processed; -} - -static CURLcode pre_receive_plain(struct Curl_easy *data, - struct connectdata *conn, int num) -{ - const curl_socket_t sockfd = conn->sock[num]; - struct postponed_data * const psnd = &(conn->postponed[num]); - size_t bytestorecv = psnd->allocated_size - psnd->recv_size; - ssize_t recvedbytes; - - /* WinSock will destroy unread received data if send() is - failed. - To avoid lossage of received data, recv() must be - performed before every send() if any incoming data is - available. However, skip this, if buffer is already full. */ - if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 && - conn->recv[num] == Curl_conn_recv && - (!psnd->buffer || bytestorecv)) { - const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, 0); - if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) { - /* Have some incoming data */ - if(!psnd->buffer) { - /* Use buffer double default size for intermediate buffer */ - psnd->allocated_size = 2 * data->set.buffer_size; - psnd->buffer = malloc(psnd->allocated_size); - if(!psnd->buffer) - return CURLE_OUT_OF_MEMORY; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ - bytestorecv = psnd->allocated_size; - } - - DEBUGASSERT(psnd->bindsock == sockfd); - recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size, - bytestorecv); - if(recvedbytes > 0) - psnd->recv_size += recvedbytes; - } - } - return CURLE_OK; -} - -static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf, - size_t len) -{ - struct postponed_data * const psnd = &(conn->postponed[num]); - size_t copysize; - if(!psnd->buffer) - return 0; - - DEBUGASSERT(psnd->allocated_size > 0); - DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); - DEBUGASSERT(psnd->recv_processed <= psnd->recv_size); - /* Check and process data that already received and storied in internal - intermediate buffer */ - if(psnd->recv_size > psnd->recv_processed) { - DEBUGASSERT(psnd->bindsock == conn->sock[num]); - copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed); - memcpy(buf, psnd->buffer + psnd->recv_processed, copysize); - psnd->recv_processed += copysize; - } - else - copysize = 0; /* buffer was allocated, but nothing was received */ - - /* Free intermediate buffer if it has no unprocessed data */ - if(psnd->recv_processed == psnd->recv_size) { - free(psnd->buffer); - psnd->buffer = NULL; - psnd->allocated_size = 0; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = CURL_SOCKET_BAD; -#endif /* DEBUGBUILD */ - } - return (ssize_t)copysize; -} -#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ -/* Use "do-nothing" macros instead of functions when workaround not used */ -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) -{ - (void)conn; - (void)sockindex; - return false; -} -#define pre_receive_plain(d,c,n) CURLE_OK -#define get_pre_recved(c,n,b,l) 0 -#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ - -/* Curl_infof() is for info message along the way */ -#define MAXINFO 2048 - -void Curl_infof(struct Curl_easy *data, const char *fmt, ...) -{ - DEBUGASSERT(!strchr(fmt, '\n')); - if(data && data->set.verbose) { - va_list ap; - int len; - char buffer[MAXINFO + 2]; - va_start(ap, fmt); - len = mvsnprintf(buffer, MAXINFO, fmt, ap); - va_end(ap); - buffer[len++] = '\n'; - buffer[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, buffer, len); - } -} - -/* Curl_failf() is for messages stating why we failed. - * The message SHALL NOT include any LF or CR. - */ - -void Curl_failf(struct Curl_easy *data, const char *fmt, ...) -{ - DEBUGASSERT(!strchr(fmt, '\n')); - if(data->set.verbose || data->set.errorbuffer) { - va_list ap; - int len; - char error[CURL_ERROR_SIZE + 2]; - va_start(ap, fmt); - len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); - - if(data->set.errorbuffer && !data->state.errorbuf) { - strcpy(data->set.errorbuffer, error); - data->state.errorbuf = TRUE; /* wrote error string */ - } - error[len++] = '\n'; - error[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, error, len); - va_end(ap); - } -} - /* * Curl_write() is an internal write function that sends data to the * server. Works with plain sockets, SCP, SSL or kerberos. @@ -301,7 +158,7 @@ CURLcode Curl_write(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(data->conn); conn = data->conn; - num = (sockfd == conn->sock[SECONDARYSOCKET]); + num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); #ifdef CURLDEBUG { @@ -338,153 +195,6 @@ CURLcode Curl_write(struct Curl_easy *data, } } -/* Curl_send_plain sends raw data without a size restriction on 'len'. */ -ssize_t Curl_send_plain(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code) -{ - struct connectdata *conn; - curl_socket_t sockfd; - ssize_t bytes_written; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - sockfd = conn->sock[num]; - /* WinSock will destroy unread received data if send() is - failed. - To avoid lossage of received data, recv() must be - performed before every send() if any incoming data is - available. */ - if(pre_receive_plain(data, conn, num)) { - *code = CURLE_OUT_OF_MEMORY; - return -1; - } - -#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ - if(conn->bits.tcp_fastopen) { - bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN, - conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen); - conn->bits.tcp_fastopen = FALSE; - } - else -#endif - bytes_written = swrite(sockfd, mem, len); - - *code = CURLE_OK; - if(-1 == bytes_written) { - int err = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) || - (EINPROGRESS == err) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *code = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Send failure: %s", - Curl_strerror(err, buffer, sizeof(buffer))); - data->state.os_errno = err; - *code = CURLE_SEND_ERROR; - } - } - return bytes_written; -} - -/* - * Curl_write_plain() is an internal write function that sends data to the - * server using plain sockets only. Otherwise meant to have the exact same - * proto as Curl_write(). - * - * This function wraps Curl_send_plain(). The only difference besides the - * prototype is '*written' (bytes written) is set to 0 on error. - * 'sockfd' must be one of the connection's two main sockets and the value of - * 'len' must not be changed. - */ -CURLcode Curl_write_plain(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) -{ - CURLcode result; - struct connectdata *conn = data->conn; - int num; - DEBUGASSERT(conn); - DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] || - sockfd == conn->sock[SECONDARYSOCKET]); - if(sockfd != conn->sock[FIRSTSOCKET] && - sockfd != conn->sock[SECONDARYSOCKET]) - return CURLE_BAD_FUNCTION_ARGUMENT; - - num = (sockfd == conn->sock[SECONDARYSOCKET]); - - *written = Curl_send_plain(data, num, mem, len, &result); - if(*written == -1) - *written = 0; - - return result; -} - -/* Curl_recv_plain receives raw data without a size restriction on 'len'. */ -ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code) -{ - struct connectdata *conn; - curl_socket_t sockfd; - ssize_t nread; - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - sockfd = conn->sock[num]; - /* Check and return data that already received and storied in internal - intermediate buffer */ - nread = get_pre_recved(conn, num, buf, len); - if(nread > 0) { - *code = CURLE_OK; - return nread; - } - - nread = sread(sockfd, buf, len); - - *code = CURLE_OK; - if(-1 == nread) { - int err = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *code = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Recv failure: %s", - Curl_strerror(err, buffer, sizeof(buffer))); - data->state.os_errno = err; - *code = CURLE_RECV_ERROR; - } - } - return nread; -} - static CURLcode pausewrite(struct Curl_easy *data, int type, /* what type of data */ const char *ptr, @@ -498,8 +208,7 @@ static CURLcode pausewrite(struct Curl_easy *data, unsigned int i; bool newtype = TRUE; - /* If this transfers over HTTP/2, pause the stream! */ - Curl_http2_stream_pause(data, TRUE); + Curl_conn_ev_data_pause(data, TRUE); if(s->tempcount) { for(i = 0; i< s->tempcount; i++) { @@ -678,41 +387,6 @@ CURLcode Curl_client_write(struct Curl_easy *data, } /* - * Curl_read_plain() is an internal read function that reads data from the - * server using plain sockets only. Otherwise meant to have the exact same - * proto as Curl_read(). - * - * This function wraps Curl_recv_plain(). The only difference besides the - * prototype is '*n' (bytes read) is set to 0 on error. - * 'sockfd' must be one of the connection's two main sockets and the value of - * 'sizerequested' must not be changed. - */ -CURLcode Curl_read_plain(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 */ -{ - CURLcode result; - struct connectdata *conn = data->conn; - int num; - DEBUGASSERT(conn); - DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] || - sockfd == conn->sock[SECONDARYSOCKET]); - if(sockfd != conn->sock[FIRSTSOCKET] && - sockfd != conn->sock[SECONDARYSOCKET]) - return CURLE_BAD_FUNCTION_ARGUMENT; - - num = (sockfd == conn->sock[SECONDARYSOCKET]); - - *n = Curl_recv_plain(data, num, buf, sizerequested, &result); - if(*n == -1) - *n = 0; - - return result; -} - -/* * Internal read-from-socket function. This is meant to deal with plain * sockets, SSL sockets and kerberos sockets. * @@ -752,30 +426,3 @@ out: return result; } -/* return 0 on success */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size) -{ - if(data->set.verbose) { - static const char s_infotype[CURLINFO_END][3] = { - "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; - if(data->set.fdebug) { - bool inCallback = Curl_is_in_callback(data); - Curl_set_in_callback(data, true); - (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); - Curl_set_in_callback(data, inCallback); - } - else { - switch(type) { - case CURLINFO_TEXT: - case CURLINFO_HEADER_OUT: - case CURLINFO_HEADER_IN: - fwrite(s_infotype[type], 2, 1, data->set.err); - fwrite(ptr, size, 1, data->set.err); - break; - default: /* nada */ - break; - } - } - } -} diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 8af5c46..d0c9275 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -26,26 +26,8 @@ #include "curl_setup.h" -void Curl_infof(struct Curl_easy *, const char *fmt, ...); -void Curl_failf(struct Curl_easy *, const char *fmt, ...); +#include "curl_log.h" -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif - -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define infof Curl_infof - -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define failf Curl_failf #define CLIENTWRITE_BODY (1<<0) #define CLIENTWRITE_HEADER (1<<1) @@ -58,20 +40,6 @@ void Curl_failf(struct Curl_easy *, const char *fmt, ...); CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, size_t len) WARN_UNUSED_RESULT; -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex); - -/* internal read-function, does plain socket only */ -CURLcode Curl_read_plain(struct Curl_easy *data, - curl_socket_t sockfd, - char *buf, - size_t sizerequested, - ssize_t *n); - -ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code); -ssize_t Curl_send_plain(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code); - /* 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, @@ -83,15 +51,4 @@ CURLcode Curl_write(struct Curl_easy *data, const void *mem, size_t len, ssize_t *written); -/* internal write-function, does plain sockets ONLY */ -CURLcode Curl_write_plain(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, size_t len, - ssize_t *written); - -/* the function used to output verbose information */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size); - - #endif /* HEADER_CURL_SENDF_H */ diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index b77e95b..604693a 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -174,7 +174,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) *val |= h->protocol; } - } while(str++); + } while(str && str++); if(!*val) /* no protocol listed */ @@ -463,8 +463,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) version_max >= CURL_SSLVERSION_MAX_LAST) return CURLE_BAD_FUNCTION_ARGUMENT; - primary->version = version; - primary->version_max = version_max; + primary->version = (unsigned char)version; + primary->version_max = (unsigned int)version_max; } #else result = CURLE_NOT_BUILT_IN; @@ -732,13 +732,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE); break; - case CURLOPT_HTTP200ALIASES: - /* - * Set a list of aliases for HTTP 200 in response header - */ - data->set.http200aliases = va_arg(param, struct curl_slist *); - break; - #if !defined(CURL_DISABLE_COOKIES) case CURLOPT_COOKIE: /* @@ -760,18 +753,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; /* append the cookie file name to the list of file names, and deal with them later */ - cl = curl_slist_append(data->state.cookielist, argptr); + cl = curl_slist_append(data->set.cookielist, argptr); if(!cl) { - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; + curl_slist_free_all(data->set.cookielist); + data->set.cookielist = NULL; return CURLE_OUT_OF_MEMORY; } - data->state.cookielist = cl; /* store the list for later use */ + data->set.cookielist = cl; /* store the list for later use */ } else { /* clear the list of cookie files */ - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; + curl_slist_free_all(data->set.cookielist); + data->set.cookielist = NULL; if(!data->share || !data->share->cookies) { /* throw away all existing cookies if this isn't a shared cookie @@ -902,22 +895,38 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * the listed enums in curl/curl.h. */ arg = va_arg(param, long); - if(arg < CURL_HTTP_VERSION_NONE) - return CURLE_BAD_FUNCTION_ARGUMENT; + switch(arg) { + case CURL_HTTP_VERSION_NONE: +#ifdef USE_HTTP2 + /* TODO: this seems an undesirable quirk to force a behaviour on + * lower implementations that they should recognize independantly? */ + arg = CURL_HTTP_VERSION_2TLS; +#endif + /* accepted */ + break; + case CURL_HTTP_VERSION_1_0: + case CURL_HTTP_VERSION_1_1: + /* accepted */ + break; +#ifdef USE_HTTP2 + case CURL_HTTP_VERSION_2_0: + case CURL_HTTP_VERSION_2TLS: + case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE: + /* accepted */ + break; +#endif #ifdef ENABLE_QUIC - if(arg == CURL_HTTP_VERSION_3) - ; - else + case CURL_HTTP_VERSION_3: + case CURL_HTTP_VERSION_3ONLY: + /* accepted */ + break; #endif -#ifndef USE_HTTP2 - if(arg >= CURL_HTTP_VERSION_2) - return CURLE_UNSUPPORTED_PROTOCOL; -#else - if(arg >= CURL_HTTP_VERSION_LAST) + default: + /* not accepted */ + if(arg < CURL_HTTP_VERSION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_UNSUPPORTED_PROTOCOL; - if(arg == CURL_HTTP_VERSION_NONE) - arg = CURL_HTTP_VERSION_2TLS; -#endif + } data->set.httpwant = (unsigned char)arg; break; @@ -944,6 +953,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.http09_allowed = arg ? TRUE : FALSE; #endif break; + + case CURLOPT_HTTP200ALIASES: + /* + * Set a list of aliases for HTTP 200 in response header + */ + data->set.http200aliases = va_arg(param, struct curl_slist *); + break; #endif /* CURL_DISABLE_HTTP */ #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ @@ -1309,6 +1325,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; break; #endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) case CURLOPT_FTP_CREATE_MISSING_DIRS: /* * An FTP/SFTP option that modifies an upload to create missing @@ -1322,6 +1339,26 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) else data->set.ftp_create_missing_dirs = (unsigned char)arg; break; + + case CURLOPT_POSTQUOTE: + /* + * List of RAW FTP commands to use after a transfer + */ + data->set.postquote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_PREQUOTE: + /* + * List of RAW FTP commands to use prior to RETR (Wesley Laxton) + */ + data->set.prequote = va_arg(param, struct curl_slist *); + break; + case CURLOPT_QUOTE: + /* + * List of RAW FTP commands to use before a transfer + */ + data->set.quote = va_arg(param, struct curl_slist *); + break; +#endif case CURLOPT_READDATA: /* * FILE pointer to read the file to be uploaded from. Or possibly @@ -1431,7 +1468,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_TIMEOUT_MS: uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) + if(uarg > UINT_MAX) uarg = UINT_MAX; data->set.timeout = (unsigned int)uarg; break; @@ -1449,7 +1486,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_CONNECTTIMEOUT_MS: uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) + if(uarg > UINT_MAX) uarg = UINT_MAX; data->set.connecttimeout = (unsigned int)uarg; break; @@ -1460,7 +1497,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * The maximum time for curl to wait for FTP server connect */ uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) + if(uarg > UINT_MAX) uarg = UINT_MAX; data->set.accepttimeout = (unsigned int)uarg; break; @@ -1506,24 +1543,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; - case CURLOPT_POSTQUOTE: - /* - * List of RAW FTP commands to use after a transfer - */ - data->set.postquote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_PREQUOTE: - /* - * List of RAW FTP commands to use prior to RETR (Wesley Laxton) - */ - data->set.prequote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_QUOTE: - /* - * List of RAW FTP commands to use before a transfer - */ - data->set.quote = va_arg(param, struct curl_slist *); - break; case CURLOPT_RESOLVE: /* * List of HOST:PORT:[addresses] strings to populate the DNS cache with @@ -1871,16 +1890,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); if((arg < 0) || (arg > 65535)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localportrange = curlx_sltosi(arg); + data->set.localportrange = curlx_sltous(arg); break; case CURLOPT_GSSAPI_DELEGATION: /* * GSS-API credential delegation bitmask */ - arg = va_arg(param, long); - if(arg < CURLGSSAPI_DELEGATION_NONE) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.gssapi_delegation = arg; + uarg = va_arg(param, unsigned long); + data->set.gssapi_delegation = (unsigned char)uarg& + (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG); break; case CURLOPT_SSL_VERIFYPEER: /* @@ -2260,9 +2278,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->cookies = NULL; #endif +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_SSL if(data->share->sslsession == data->state.session) data->state.session = NULL; - +#endif #ifdef USE_LIBPSL if(data->psl == &data->share->psl) data->psl = data->multi? &data->multi->psl: NULL; @@ -2296,10 +2319,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->cookies = data->share->cookies; } #endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = data->share->hsts; + } +#endif /* CURL_DISABLE_HTTP */ +#ifdef USE_SSL if(data->share->sslsession) { data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; data->state.session = data->share->sslsession; } +#endif #ifdef USE_LIBPSL if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) data->psl = &data->share->psl; @@ -2515,6 +2547,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; + case CURLOPT_SSH_KNOWNHOSTS: + /* + * Store the file name to read known hosts from. + */ + result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], + va_arg(param, char *)); + break; +#ifdef USE_LIBSSH2 case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* * Option to allow for the SHA256 of the host public key to be checked @@ -2524,14 +2564,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the file name to read known hosts from. - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], - va_arg(param, char *)); - break; -#ifdef USE_LIBSSH2 case CURLOPT_SSH_HOSTKEYFUNCTION: /* the callback to check the hostkey without the knownhost file */ data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback); @@ -2544,6 +2576,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ssh_hostkeyfunc_userp = va_arg(param, void *); break; #endif + case CURLOPT_SSH_KEYFUNCTION: /* setting to NULL is fine since the ssh.c functions themselves will then revert to use the internal default */ @@ -2590,7 +2623,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.new_file_perms = (unsigned int)arg; break; - +#endif +#ifdef USE_SSH case CURLOPT_NEW_DIRECTORY_PERMS: /* * Uses these permissions instead of 0755 @@ -2825,52 +2859,33 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_TLSAUTH_USERNAME: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME] && - !data->set.ssl.primary.authtype) - data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_USERNAME: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && - !data->set.proxy_ssl.primary.authtype) - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to - SRP */ break; #endif case CURLOPT_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME] && - !data->set.ssl.primary.authtype) - data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */ break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_PASSWORD: result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && - !data->set.proxy_ssl.primary.authtype) - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */ break; #endif case CURLOPT_TLSAUTH_TYPE: argptr = va_arg(param, char *); - if(!argptr || - strncasecompare(argptr, "SRP", strlen("SRP"))) - data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; - else - data->set.ssl.primary.authtype = CURL_TLSAUTH_NONE; + if(argptr && !strncasecompare(argptr, "SRP", strlen("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"))) - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; - else - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_NONE; + if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP"))) + return CURLE_BAD_FUNCTION_ARGUMENT; break; #endif #endif @@ -2956,29 +2971,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; break; case CURLOPT_STREAM_WEIGHT: -#ifndef USE_NGHTTP2 - return CURLE_NOT_BUILT_IN; -#else +#if defined(USE_HTTP2) || defined(USE_HTTP3) arg = va_arg(param, long); if((arg >= 1) && (arg <= 256)) - data->set.stream_weight = (int)arg; + data->set.priority.weight = (int)arg; break; +#else + return CURLE_NOT_BUILT_IN; #endif case CURLOPT_STREAM_DEPENDS: case CURLOPT_STREAM_DEPENDS_E: { -#ifndef USE_NGHTTP2 - return CURLE_NOT_BUILT_IN; -#else struct Curl_easy *dep = va_arg(param, struct Curl_easy *); if(!dep || GOOD_EASY_HANDLE(dep)) { - if(data->set.stream_depends_on) { - Curl_http2_remove_child(data->set.stream_depends_on, data); - } - Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E)); + return Curl_data_priority_add_child(dep, data, + option == CURLOPT_STREAM_DEPENDS_E); } break; -#endif } case CURLOPT_CONNECT_TO: data->set.connect_to = va_arg(param, struct curl_slist *); @@ -2988,7 +2997,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) + if(uarg > UINT_MAX) uarg = UINT_MAX; data->set.happy_eyeballs_timeout = (unsigned int)uarg; break; @@ -3049,19 +3058,39 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_HSTSWRITEDATA: data->set.hsts_write_userp = va_arg(param, void *); break; - case CURLOPT_HSTS: + case CURLOPT_HSTS: { + struct curl_slist *h; if(!data->hsts) { data->hsts = Curl_hsts_init(); if(!data->hsts) return CURLE_OUT_OF_MEMORY; } argptr = va_arg(param, char *); - result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); - if(result) - return result; - if(argptr) - (void)Curl_hsts_loadfile(data, data->hsts, argptr); + if(argptr) { + result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); + if(result) + return result; + /* this needs to build a list of file names to read from, so that it can + read them later, as we might get a shared HSTS handle to load them + into */ + h = curl_slist_append(data->set.hstslist, argptr); + if(!h) { + curl_slist_free_all(data->set.hstslist); + data->set.hstslist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->set.hstslist = h; /* store the list for later use */ + } + else { + /* clear the list of HSTS files */ + curl_slist_free_all(data->set.hstslist); + data->set.hstslist = NULL; + if(!data->share || !data->share->hsts) + /* throw away the HSTS cache unless shared */ + Curl_hsts_cleanup(&data->hsts); + } break; + } case CURLOPT_HSTS_CTRL: arg = va_arg(param, long); if(arg & CURLHSTS_ENABLE) { diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h index ffc77a7..3c14a05 100644 --- a/Utilities/cmcurl/lib/setopt.h +++ b/Utilities/cmcurl/lib/setopt.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h index 7854397..7595834 100644 --- a/Utilities/cmcurl/lib/setup-os400.h +++ b/Utilities/cmcurl/lib/setup-os400.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -205,7 +205,7 @@ extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen); extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen); extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, - struct sockaddr *dstaddr, int addrlen); + const struct sockaddr *dstaddr, int addrlen); extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, struct sockaddr *fromaddr, int *addrlen); extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen); diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h index b570683..46657b2 100644 --- a/Utilities/cmcurl/lib/setup-vms.h +++ b/Utilities/cmcurl/lib/setup-vms.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h index bc5f8ef..1394838 100644 --- a/Utilities/cmcurl/lib/setup-win32.h +++ b/Utilities/cmcurl/lib/setup-win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/sha256.c b/Utilities/cmcurl/lib/sha256.c index c96a9fc..fdfd631 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com> + * 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/share.c b/Utilities/cmcurl/lib/share.c index 1a083e7..c0a8d80 100644 --- a/Utilities/cmcurl/lib/share.c +++ b/Utilities/cmcurl/lib/share.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -29,9 +29,11 @@ #include "share.h" #include "psl.h" #include "vtls/vtls.h" -#include "curl_memory.h" +#include "hsts.h" -/* The last #include file should be: */ +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" #include "memdebug.h" struct Curl_share * @@ -89,6 +91,18 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) #endif break; + case CURL_LOCK_DATA_HSTS: +#ifndef CURL_DISABLE_HSTS + if(!share->hsts) { + share->hsts = Curl_hsts_init(); + if(!share->hsts) + res = CURLSHE_NOMEM; + } +#else /* CURL_DISABLE_HSTS */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL if(!share->sslsession) { @@ -141,6 +155,16 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) #endif break; + case CURL_LOCK_DATA_HSTS: +#ifndef CURL_DISABLE_HSTS + if(share->hsts) { + Curl_hsts_cleanup(&share->hsts); + } +#else /* CURL_DISABLE_HSTS */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL Curl_safefree(share->sslsession); @@ -207,6 +231,10 @@ curl_share_cleanup(struct Curl_share *share) Curl_cookie_cleanup(share->cookies); #endif +#ifndef CURL_DISABLE_HSTS + Curl_hsts_cleanup(&share->hsts); +#endif + #ifdef USE_SSL if(share->sslsession) { size_t i; diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h index 32be416..7f55aac 100644 --- a/Utilities/cmcurl/lib/share.h +++ b/Utilities/cmcurl/lib/share.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -59,10 +59,14 @@ struct Curl_share { #ifdef USE_LIBPSL struct PslCache psl; #endif - +#ifndef CURL_DISABLE_HSTS + struct hsts *hsts; +#endif +#ifdef USE_SSL struct Curl_ssl_session *sslsession; size_t max_ssl_sessions; long sessionage; +#endif }; CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h index d12b317..14ab25b 100644 --- a/Utilities/cmcurl/lib/sigpipe.h +++ b/Utilities/cmcurl/lib/sigpipe.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/slist.c b/Utilities/cmcurl/lib/slist.c index 6c80722..366b247 100644 --- a/Utilities/cmcurl/lib/slist.c +++ b/Utilities/cmcurl/lib/slist.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/slist.h b/Utilities/cmcurl/lib/slist.h index 4e5834c..9561fd0 100644 --- a/Utilities/cmcurl/lib/slist.h +++ b/Utilities/cmcurl/lib/slist.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/smb.c b/Utilities/cmcurl/lib/smb.c index 48d5a2f..dc0abe7 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -763,6 +763,11 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) void *msg = NULL; const struct smb_nt_create_response *smb_m; + if(data->set.upload && (data->state.infilesize < 0)) { + failf(data, "SMB upload needs to know the size up front"); + return CURLE_SEND_ERROR; + } + /* Start the request */ if(req->state == SMB_REQUESTING) { result = smb_send_tree_connect(data); @@ -993,6 +998,7 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data, /* The share must be present */ if(!slash) { Curl_safefree(smbc->share); + failf(data, "missing share in URL path for SMB"); return CURLE_URL_MALFORMAT; } diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h index 919f3ac..c35f3e9 100644 --- a/Utilities/cmcurl/lib/smb.h +++ b/Utilities/cmcurl/lib/smb.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies - * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies + * 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/smtp.c b/Utilities/cmcurl/lib/smtp.c index 6d0783f4..7a03030 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -398,15 +398,17 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) struct connectdata *conn = data->conn; struct smtp_conn *smtpc = &conn->proto.smtpc; CURLcode result; + bool ssldone = FALSE; - if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { + if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); if(result) goto out; } - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone); + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); if(!result) { + smtpc->ssldone = ssldone; if(smtpc->state != SMTP_UPGRADETLS) state(data, SMTP_UPGRADETLS); @@ -891,7 +893,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, if(smtpcode/100 != 2 && smtpcode != 1) { if(data->set.use_ssl <= CURLUSESSL_TRY - || Curl_conn_is_ssl(data, FIRSTSOCKET)) + || Curl_conn_is_ssl(conn, FIRSTSOCKET)) result = smtp_perform_helo(data, conn); else { failf(data, "Remote access denied: %d", smtpcode); @@ -956,7 +958,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, } if(smtpcode != 1) { - if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) { + if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* We don't have a SSL/TLS connection yet, but SSL is requested */ if(smtpc->tls_supported) /* Switch to TLS connection now */ @@ -1288,7 +1290,9 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) struct smtp_conn *smtpc = &conn->proto.smtpc; if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { - result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone); + bool ssldone = FALSE; + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); + smtpc->ssldone = ssldone; if(result || !smtpc->ssldone) return result; } diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h index 24c5589..7a04c21 100644 --- a/Utilities/cmcurl/lib/smtp.h +++ b/Utilities/cmcurl/lib/smtp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -57,28 +57,28 @@ struct SMTP { curl_pp_transfer transfer; char *custom; /* Custom Request */ struct curl_slist *rcpt; /* Recipient list */ - bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on - total number of recipients) succeeded so far */ - bool trailing_crlf; /* Specifies if the trailing CRLF is present */ int rcpt_last_error; /* The last error received for RCPT TO command */ size_t eob; /* Number of bytes of the EOB (End Of Body) that have been received so far */ + BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on + total number of recipients) succeeded so far */ + BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */ }; /* smtp_conn is used for struct connection-oriented data in the connectdata struct */ struct smtp_conn { struct pingpong pp; + struct SASL sasl; /* SASL-related storage */ smtpstate state; /* Always use smtp.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ char *domain; /* Client address/name to send in the EHLO */ - struct SASL sasl; /* SASL-related storage */ - bool tls_supported; /* StartTLS capability supported by server */ - bool size_supported; /* If server supports SIZE extension according to + BIT(ssldone); /* Is connect() over SSL done? */ + BIT(tls_supported); /* StartTLS capability supported by server */ + BIT(size_supported); /* If server supports SIZE extension according to RFC 1870 */ - bool utf8_supported; /* If server supports SMTPUTF8 extension according + BIT(utf8_supported); /* If server supports SMTPUTF8 extension according to RFC 6531 */ - bool auth_supported; /* AUTH capability supported by server */ + BIT(auth_supported); /* AUTH capability supported by server */ }; extern const struct Curl_handler Curl_handler_smtp; diff --git a/Utilities/cmcurl/lib/sockaddr.h b/Utilities/cmcurl/lib/sockaddr.h index 77ec833..5a6bb20 100644 --- a/Utilities/cmcurl/lib/sockaddr.h +++ b/Utilities/cmcurl/lib/sockaddr.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/socketpair.c b/Utilities/cmcurl/lib/socketpair.c index 0f8798f..b94c984 100644 --- a/Utilities/cmcurl/lib/socketpair.c +++ b/Utilities/cmcurl/lib/socketpair.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -65,7 +65,7 @@ int Curl_socketpair(int domain, int type, int protocol, union { struct sockaddr_in inaddr; struct sockaddr addr; - } a, a2; + } a; curl_socket_t listener; curl_socklen_t addrlen = sizeof(a.inaddr); int reuse = 1; @@ -85,9 +85,22 @@ int Curl_socketpair(int domain, int type, int protocol, socks[0] = socks[1] = CURL_SOCKET_BAD; +#if defined(WIN32) || defined(__CYGWIN__) + /* don't set SO_REUSEADDR on Windows */ + (void)reuse; +#ifdef SO_EXCLUSIVEADDRUSE + { + int exclusive = 1; + if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1) + goto error; + } +#endif +#else if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) goto error; +#endif if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) goto error; if(getsockname(listener, &a.addr, &addrlen) == -1 || @@ -107,24 +120,59 @@ int Curl_socketpair(int domain, int type, int protocol, pfd[0].fd = listener; pfd[0].events = POLLIN; pfd[0].revents = 0; - (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */ + (void)Curl_poll(pfd, 1, 1000); /* one second */ socks[1] = accept(listener, NULL, NULL); if(socks[1] == CURL_SOCKET_BAD) goto error; + else { + struct curltime check; + struct curltime start = Curl_now(); + char *p = (char *)✓ + size_t s = sizeof(check); - /* verify that nothing else connected */ - addrlen = sizeof(a.inaddr); - if(getsockname(socks[0], &a.addr, &addrlen) == -1 || - addrlen < (int)sizeof(a.inaddr)) - goto error; - addrlen = sizeof(a2.inaddr); - if(getpeername(socks[1], &a2.addr, &addrlen) == -1 || - addrlen < (int)sizeof(a2.inaddr)) - goto error; - if(a.inaddr.sin_family != a2.inaddr.sin_family || - a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr || - a.inaddr.sin_port != a2.inaddr.sin_port) - goto error; + /* write data to the socket */ + swrite(socks[0], &start, sizeof(start)); + /* verify that we read the correct data */ + do { + ssize_t nread; + + pfd[0].fd = socks[1]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + (void)Curl_poll(pfd, 1, 1000); /* one second */ + + nread = sread(socks[1], p, s); + if(nread == -1) { + int sockerr = SOCKERRNO; + /* Don't block forever */ + if(Curl_timediff(Curl_now(), start) > (60 * 1000)) + goto error; + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it + returned due to its inability to send off data without + blocking. We therefore treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || + (EINTR == sockerr) || (EINPROGRESS == sockerr) +#endif + ) { + continue; + } + goto error; + } + s -= nread; + if(s) { + p += nread; + continue; + } + if(memcmp(&start, &check, sizeof(check))) + goto error; + break; + } while(1); + } sclose(listener); return 0; diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index de70df6..306ab5d 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.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/socks.c b/Utilities/cmcurl/lib/socks.c index d491e08..95c2b00 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -89,8 +89,8 @@ struct socks_state { * * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. */ -int Curl_blockread_all(struct Curl_easy *data, /* transfer */ - curl_socket_t sockfd, /* read from this socket */ +int Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ char *buf, /* store read data here */ ssize_t buffersize, /* max amount to read */ ssize_t *n) /* amount bytes read */ @@ -98,6 +98,8 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ ssize_t nread = 0; ssize_t allread = 0; int result; + CURLcode err = CURLE_OK; + *n = 0; for(;;) { timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -108,15 +110,19 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */ } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) { + if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) { result = ~CURLE_OK; break; } - result = Curl_read_plain(data, sockfd, buf, buffersize, &nread); - if(CURLE_AGAIN == result) - continue; - if(result) - break; + nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err); + if(nread <= 0) { + result = err; + if(CURLE_AGAIN == err) + continue; + if(err) { + break; + } + } if(buffersize == nread) { allread += nread; @@ -192,6 +198,68 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data, #endif } +static CURLproxycode socks_state_send(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + CURLproxycode failcode, + const char *description) +{ + ssize_t nwritten; + CURLcode result; + + nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, + sx->outstanding, &result); + if(nwritten <= 0) { + if(CURLE_AGAIN == result) { + return CURLPX_OK; + } + else if(CURLE_OK == result) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + failf(data, "Failed to send %s: %s", description, + curl_easy_strerror(result)); + return failcode; + } + DEBUGASSERT(sx->outstanding >= nwritten); + /* not done, remain in state */ + sx->outstanding -= nwritten; + sx->outp += nwritten; + return CURLPX_OK; +} + +static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data, + CURLproxycode failcode, + const char *description) +{ + ssize_t nread; + CURLcode result; + + nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, + sx->outstanding, &result); + if(nread <= 0) { + if(CURLE_AGAIN == result) { + return CURLPX_OK; + } + else if(CURLE_OK == result) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + failf(data, "SOCKS4: Failed receiving %s: %s", description, + curl_easy_strerror(result)); + return failcode; + } + /* remain in reading state */ + DEBUGASSERT(sx->outstanding >= nread); + sx->outstanding -= nread; + sx->outp += nread; + return CURLPX_OK; +} + /* * This function logs in to a SOCKS4 proxy and sends the specifics to the final * destination server. @@ -212,10 +280,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; unsigned char *socksreq = (unsigned char *)data->state.buffer; CURLcode result; - curl_socket_t sockfd = conn->sock[cf->sockindex]; + CURLproxycode presult; struct Curl_dns_entry *dns = NULL; - ssize_t actualread; - ssize_t written; /* make sure that the buffer is at least 600 bytes */ DEBUGASSERT(READBUFFER_MIN >= 600); @@ -250,7 +316,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, /* DNS resolve only for SOCKS4, not SOCKS4a */ if(!protocol4a) { enum resolve_t rc = - Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns); + Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns); if(rc == CURLRESOLV_ERROR) return CURLPX_RESOLVE_HOST; @@ -375,19 +441,14 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, /* FALLTHROUGH */ case CONNECT_REQ_SENDING: /* Send request */ - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS4 connect request."); - return CURLPX_SEND_CONNECT; - } - if(written != sx->outstanding) { - /* not done, remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "SOCKS4 connect request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } - /* done sending! */ sx->outstanding = 8; /* receive data size */ sx->outp = socksreq; @@ -396,22 +457,12 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, /* FALLTHROUGH */ case CONNECT_SOCKS_READ: /* Receive response */ - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "SOCKS4: Failed receiving connect request ack: %s", - curl_easy_strerror(result)); - return CURLPX_RECV_CONNECT; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, + "connect request ack"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { /* remain in reading state */ - sx->outstanding -= actualread; - sx->outp += actualread; return CURLPX_OK; } sxstate(sx, data, CONNECT_DONE); @@ -518,10 +569,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, unsigned char *socksreq = (unsigned char *)data->state.buffer; char dest[256] = "unknown"; /* printable hostname:port */ int idx; - ssize_t actualread; - ssize_t written; CURLcode result; - curl_socket_t sockfd = conn->sock[cf->sockindex]; + CURLproxycode presult; bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; const size_t hostname_len = strlen(sx->hostname); @@ -567,30 +616,25 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, /* write the number of authentication methods */ socksreq[1] = (unsigned char) (idx - 2); - result = Curl_write_plain(data, sockfd, socksreq, idx, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLPX_SEND_CONNECT; - } - if(written != idx) { - sxstate(sx, data, CONNECT_SOCKS_SEND); - sx->outstanding = idx - written; - sx->outp = &socksreq[written]; + sx->outp = socksreq; + sx->outstanding = idx; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } sxstate(sx, data, CONNECT_SOCKS_READ); goto CONNECT_SOCKS_READ_INIT; case CONNECT_SOCKS_SEND: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLPX_SEND_CONNECT; - } - if(written != sx->outstanding) { - /* not done, remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, + "initial SOCKS5 request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } /* FALLTHROUGH */ @@ -600,21 +644,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sx->outp = socksreq; /* store it here */ /* FALLTHROUGH */ case CONNECT_SOCKS_READ: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to receive initial SOCKS5 response."); - return CURLPX_RECV_CONNECT; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "Connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, + "initial SOCKS5 response"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { /* remain in reading state */ - sx->outstanding -= actualread; - sx->outp += actualread; return CURLPX_OK; } else if(socksreq[0] != 5) { @@ -634,7 +669,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else if(allow_gssapi && (socksreq[1] == 1)) { sxstate(sx, data, CONNECT_GSSAPI_INIT); - result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data); + result = Curl_SOCKS5_gssapi_negotiate(cf, data); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; @@ -713,16 +748,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, } /* FALLTHROUGH */ case CONNECT_AUTH_SEND: - result = Curl_write_plain(data, sockfd, sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS5 sub-negotiation request."); - return CURLPX_SEND_AUTH; - } - if(sx->outstanding != written) { - /* remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH, + "SOCKS5 sub-negotiation request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in sending state */ return CURLPX_OK; } sx->outp = socksreq; @@ -730,21 +761,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sxstate(sx, data, CONNECT_AUTH_READ); /* FALLTHROUGH */ case CONNECT_AUTH_READ: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to receive SOCKS5 sub-negotiation response."); - return CURLPX_RECV_AUTH; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH, + "SOCKS5 sub-negotiation response"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ return CURLPX_OK; } /* ignore the first (VER) byte */ @@ -761,7 +783,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, case CONNECT_REQ_INIT: if(socks5_resolve_local) { enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port, - FALSE, &dns); + TRUE, &dns); if(rc == CURLRESOLV_ERROR) return CURLPX_RESOLVE_HOST; @@ -909,16 +931,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sxstate(sx, data, CONNECT_REQ_SENDING); /* FALLTHROUGH */ case CONNECT_REQ_SENDING: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS5 connect request."); - return CURLPX_SEND_REQUEST; - } - if(sx->outstanding != written) { - /* remain in state */ - sx->outstanding -= written; - sx->outp += written; + presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST, + "SOCKS5 connect request"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in send state */ return CURLPX_OK; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -932,25 +950,15 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, sxstate(sx, data, CONNECT_REQ_READ); /* FALLTHROUGH */ case CONNECT_REQ_READ: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLPX_RECV_REQACK; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK, + "SOCKS5 connect request ack"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ return CURLPX_OK; } - - if(socksreq[0] != 5) { /* version */ + else if(socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); return CURLPX_BAD_VERSION; @@ -1031,21 +1039,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, #endif /* FALLTHROUGH */ case CONNECT_REQ_READ_MORE: - result = Curl_read_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLPX_RECV_ADDRESS; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; + presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS, + "SOCKS5 connect request address"); + if(CURLPX_OK != presult) + return presult; + else if(sx->outstanding) { + /* remain in reading state */ return CURLPX_OK; } sxstate(sx, data, CONNECT_DONE); @@ -1151,7 +1150,6 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, result = connect_SOCKS(cf, sx, data); if(!result && sx->state == CONNECT_DONE) { cf->connected = TRUE; - Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]); Curl_verboseconnect(data, conn); socks_proxy_cf_free(cf); } @@ -1171,7 +1169,7 @@ static int socks_cf_get_select_socks(struct Curl_cfilter *cf, if(!fds && cf->next->connected && !cf->connected && sx) { /* If we are not connected, the filter below is and has nothing * to wait on, we determine what to wait for. */ - socks[0] = cf->conn->sock[cf->sockindex]; + socks[0] = Curl_conn_cf_get_socket(cf, data); switch(sx->state) { case CONNECT_RESOLVING: case CONNECT_SOCKS_READ: @@ -1205,13 +1203,6 @@ static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, socks_proxy_cf_free(cf); } -static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - (void)data; - socks_proxy_cf_free(cf); -} - static void socks_cf_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, const char **phost, @@ -1229,11 +1220,11 @@ static void socks_cf_get_host(struct Curl_cfilter *cf, } } -static const struct Curl_cftype cft_socks_proxy = { +struct Curl_cftype Curl_cft_socks_proxy = { "SOCKS-PROXYY", CF_TYPE_IP_CONNECT, + 0, socks_proxy_cf_destroy, - Curl_cf_def_setup, socks_proxy_cf_connect, socks_proxy_cf_close, socks_cf_get_host, @@ -1241,8 +1232,10 @@ static const struct Curl_cftype cft_socks_proxy = { Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, - Curl_cf_def_attach_data, - socks_proxy_cf_detach_data, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, }; CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data, @@ -1252,10 +1245,23 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data, struct Curl_cfilter *cf; CURLcode result; - result = Curl_cf_create(&cf, &cft_socks_proxy, NULL); + result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); if(!result) Curl_conn_cf_add(data, conn, sockindex, cf); return result; } +CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + (void)data; + result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + #endif /* CURL_DISABLE_PROXY */ diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h index 2e2fa18..ba5b54a 100644 --- a/Utilities/cmcurl/lib/socks.h +++ b/Utilities/cmcurl/lib/socks.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -37,8 +37,8 @@ * * This is STUPID BLOCKING behavior */ -int Curl_blockread_all(struct Curl_easy *data, - curl_socket_t sockfd, +int Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, char *buf, ssize_t buffersize, ssize_t *n); @@ -47,7 +47,7 @@ int Curl_blockread_all(struct Curl_easy *data, /* * This function handles the SOCKS5 GSS-API negotiation and initialization */ -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data); #endif @@ -55,6 +55,11 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data, struct connectdata *conn, int sockindex); +CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_socks_proxy; + #endif /* CURL_DISABLE_PROXY */ #endif /* HEADER_CURL_SOCKS_H */ diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index f14099f..2ede8c7 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012, Markus Moeller, <markus_moeller@compuserve.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,6 +30,7 @@ #include "curl_gssapi.h" #include "urldata.h" #include "sendf.h" +#include "cfilters.h" #include "connect.h" #include "timeval.h" #include "socks.h" @@ -101,14 +102,14 @@ static int check_gss_err(struct Curl_easy *data, return 0; } -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[sockindex]; + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode code; ssize_t actualread; - ssize_t written; + ssize_t nwritten; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; @@ -203,8 +204,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, us_length = htons((short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -213,10 +214,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - code = Curl_write_plain(data, sock, (char *)gss_send_token.value, - gss_send_token.length, &written); - - if(code || ((ssize_t)gss_send_token.length != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, + (char *)gss_send_token.value, + gss_send_token.length, &code); + if(code || ((ssize_t)gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); @@ -242,7 +243,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, * +----+------+-----+----------------+ */ - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); gss_release_name(&gss_status, &server); @@ -281,7 +282,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, + result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { @@ -410,8 +411,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -420,17 +421,18 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_write_plain(data, sock, socksreq, 1, &written); - if(code || ( 1 != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + if(code || ( 1 != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - code = Curl_write_plain(data, sock, (char *)gss_w_token.value, - gss_w_token.length, &written); - if(code || ((ssize_t)gss_w_token.length != written)) { + nwritten = Curl_conn_cf_send(cf->next, data, + (char *)gss_w_token.value, + gss_w_token.length, &code); + if(code || ((ssize_t)gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -439,7 +441,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_release_buffer(&gss_status, &gss_w_token); } - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -470,7 +472,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, + result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, gss_recv_token.length, &actualread); if(result || (actualread != us_length)) { diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c index 210a0df..d1200ea 100644 --- a/Utilities/cmcurl/lib/socks_sspi.c +++ b/Utilities/cmcurl/lib/socks_sspi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012, 2011, Markus Moeller, <markus_moeller@compuserve.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,6 +29,7 @@ #include "urldata.h" #include "sendf.h" +#include "cfilters.h" #include "connect.h" #include "strerror.h" #include "timeval.h" @@ -62,11 +63,11 @@ static int check_sspi_err(struct Curl_easy *data, } /* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[sockindex]; + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode code; ssize_t actualread; ssize_t written; @@ -206,7 +207,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, us_length = htons((short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); if(code || (4 != written)) { failf(data, "Failed to send SSPI authentication request."); free(service_name); @@ -219,8 +220,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_COULDNT_CONNECT; } - code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &written); + written = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &code); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI authentication token."); free(service_name); @@ -260,7 +262,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, * +----+------+-----+----------------+ */ - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); free(service_name); @@ -300,7 +302,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, s_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer, + result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, sspi_recv_token.cbBuffer, &actualread); if(result || (actualread != us_length)) { @@ -468,7 +470,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); if(code || (4 != written)) { failf(data, "Failed to send SSPI encryption request."); if(sspi_send_token.pvBuffer) @@ -479,7 +481,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); if(code || (1 != written)) { failf(data, "Failed to send SSPI encryption type."); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -487,8 +489,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, } } else { - code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &written); + written = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, &code); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI encryption type."); if(sspi_send_token.pvBuffer) @@ -500,7 +503,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); } - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); s_pSecFn->DeleteSecurityContext(&sspi_context); @@ -532,7 +535,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, return CURLE_OUT_OF_MEMORY; } - result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer, + result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer, &actualread); if(result || (actualread != us_length)) { diff --git a/Utilities/cmcurl/lib/speedcheck.c b/Utilities/cmcurl/lib/speedcheck.c index 3ddc43d..580efbd 100644 --- a/Utilities/cmcurl/lib/speedcheck.c +++ b/Utilities/cmcurl/lib/speedcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/speedcheck.h b/Utilities/cmcurl/lib/speedcheck.h index cb44eb0..bff2f32 100644 --- a/Utilities/cmcurl/lib/speedcheck.h +++ b/Utilities/cmcurl/lib/speedcheck.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/splay.c b/Utilities/cmcurl/lib/splay.c index 33b44aa..48e079b 100644 --- a/Utilities/cmcurl/lib/splay.c +++ b/Utilities/cmcurl/lib/splay.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1997 - 2022, Daniel Stenberg, <daniel@haxx.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/splay.h b/Utilities/cmcurl/lib/splay.h index 015e2ca..dd1d07a 100644 --- a/Utilities/cmcurl/lib/splay.h +++ b/Utilities/cmcurl/lib/splay.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1997 - 2022, Daniel Stenberg, <daniel@haxx.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/strcase.c b/Utilities/cmcurl/lib/strcase.c index 7fb9c80..7c0b4ef 100644 --- a/Utilities/cmcurl/lib/strcase.c +++ b/Utilities/cmcurl/lib/strcase.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/strcase.h b/Utilities/cmcurl/lib/strcase.h index 192e0da..8c50bbc 100644 --- a/Utilities/cmcurl/lib/strcase.h +++ b/Utilities/cmcurl/lib/strcase.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/strdup.c b/Utilities/cmcurl/lib/strdup.c index ac22b6d..07a6139 100644 --- a/Utilities/cmcurl/lib/strdup.c +++ b/Utilities/cmcurl/lib/strdup.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -37,7 +37,7 @@ #include "memdebug.h" #ifndef HAVE_STRDUP -char *curlx_strdup(const char *str) +char *Curl_strdup(const char *str) { size_t len; char *newstr; diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h index fb46808..c3430b5 100644 --- a/Utilities/cmcurl/lib/strdup.h +++ b/Utilities/cmcurl/lib/strdup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -26,7 +26,7 @@ #include "curl_setup.h" #ifndef HAVE_STRDUP -extern char *curlx_strdup(const char *str); +char *Curl_strdup(const char *str); #endif #ifdef WIN32 wchar_t* Curl_wcsdup(const wchar_t* src); diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index b9a51e2..3ec10e3 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2004 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -550,6 +550,9 @@ curl_url_strerror(CURLUcode error) case CURLUE_BAD_USER: return "Bad user"; + case CURLUE_LACKS_IDN: + return "libcurl lacks IDN support"; + case CURLUE_LAST: break; } diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h index 658f16c..399712f 100644 --- a/Utilities/cmcurl/lib/strerror.h +++ b/Utilities/cmcurl/lib/strerror.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/strtok.c b/Utilities/cmcurl/lib/strtok.c index 6120bcc..d8e1e81 100644 --- a/Utilities/cmcurl/lib/strtok.c +++ b/Utilities/cmcurl/lib/strtok.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/strtok.h b/Utilities/cmcurl/lib/strtok.h index 641a3da..321cba2 100644 --- a/Utilities/cmcurl/lib/strtok.h +++ b/Utilities/cmcurl/lib/strtok.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c index fb8d921..077b257 100644 --- a/Utilities/cmcurl/lib/strtoofft.c +++ b/Utilities/cmcurl/lib/strtoofft.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/strtoofft.h b/Utilities/cmcurl/lib/strtoofft.h index 311dae4..34d293b 100644 --- a/Utilities/cmcurl/lib/strtoofft.h +++ b/Utilities/cmcurl/lib/strtoofft.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index bede9c7..0cdaf3b 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index 167804e..24899cb 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index 22bc81e..a964bfd 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/telnet.h b/Utilities/cmcurl/lib/telnet.h index 6dd99b4..30782d8 100644 --- a/Utilities/cmcurl/lib/telnet.h +++ b/Utilities/cmcurl/lib/telnet.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/tftp.c b/Utilities/cmcurl/lib/tftp.c index 9e6d949..164d3c7 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -48,6 +48,7 @@ #include "urldata.h" #include <curl/curl.h> +#include "cf-socket.h" #include "transfer.h" #include "sendf.h" #include "tftp.h" @@ -529,8 +530,8 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, not have a size_t argument, like older unixes that want an 'int' */ senddata = sendto(state->sockfd, (void *)state->spacket.data, (SEND_TYPE_ARG3)sbytes, 0, - data->conn->ip_addr->ai_addr, - data->conn->ip_addr->ai_addrlen); + &data->conn->remote_addr->sa_addr, + data->conn->remote_addr->addrlen); if(senddata != (ssize_t)sbytes) { char buffer[STRERROR_LEN]; failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); @@ -1014,7 +1015,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) state->requested_blksize = blksize; ((struct sockaddr *)&state->local_addr)->sa_family = - (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family); + (CURL_SA_FAMILY_T)(conn->remote_addr->family); tftp_set_timeouts(state); @@ -1033,7 +1034,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) * IPv4 and IPv6... */ int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, - conn->ip_addr->ai_addrlen); + conn->remote_addr->addrlen); if(rc) { char buffer[STRERROR_LEN]; failf(data, "bind() failed; %s", diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index 3f1fda6..5d2d5da 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/timediff.c b/Utilities/cmcurl/lib/timediff.c index c589318..1b762bb 100644 --- a/Utilities/cmcurl/lib/timediff.c +++ b/Utilities/cmcurl/lib/timediff.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/timediff.h b/Utilities/cmcurl/lib/timediff.h index 90e5474..fb318d4 100644 --- a/Utilities/cmcurl/lib/timediff.h +++ b/Utilities/cmcurl/lib/timediff.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/timeval.c b/Utilities/cmcurl/lib/timeval.c index 647d7b0..dca1c6f 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/timeval.h b/Utilities/cmcurl/lib/timeval.h index 8d4fef4..92e484a 100644 --- a/Utilities/cmcurl/lib/timeval.h +++ b/Utilities/cmcurl/lib/timeval.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/transfer.c b/Utilities/cmcurl/lib/transfer.c index ba0410f..69df214 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -73,6 +73,7 @@ #include "url.h" #include "getinfo.h" #include "vtls/vtls.h" +#include "vquic/vquic.h" #include "select.h" #include "multiif.h" #include "connect.h" @@ -367,27 +368,12 @@ static int data_pending(struct Curl_easy *data) { struct connectdata *conn = data->conn; -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) - return Curl_quic_data_pending(data); -#endif - if(conn->handler->protocol&PROTO_FAMILY_FTP) return Curl_conn_data_pending(data, SECONDARYSOCKET); /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || -#ifdef USE_NGHTTP2 - /* For HTTP/2, we may read up everything including response body - with header fields in Curl_http_readwrite_headers. If no - content-length is provided, curl waits for the connection - close, which we emulate it using conn->proto.httpc.closed = - TRUE. The thing is if we read everything, then http2_recv won't - be called and we cannot signal the HTTP/2 stream has closed. As - a workaround, we return nonzero here to call http2_recv. */ - ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) || -#endif Curl_conn_data_pending(data, FIRSTSOCKET); } @@ -454,29 +440,16 @@ static CURLcode readwrite_data(struct Curl_easy *data, bool is_empty_data = FALSE; size_t buffersize = data->set.buffer_size; size_t bytestoread = buffersize; -#ifdef USE_NGHTTP2 - bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 20)); -#endif - bool is_http3 = -#ifdef ENABLE_QUIC - ((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 30)); -#else - FALSE; -#endif - - if( -#ifdef USE_NGHTTP2 - /* For HTTP/2, read data without caring about the content length. This - is safe because body in HTTP/2 is always segmented thanks to its - framing layer. Meanwhile, we have to call Curl_read to ensure that - http2_handle_stream_close is called when we read all incoming bytes - for a particular stream. */ - !is_http2 && -#endif - !is_http3 && /* Same reason mentioned above. */ - k->size != -1 && !k->header) { + /* For HTTP/2 and HTTP/3, read data without caring about the content + length. This is safe because body in HTTP/2 is always segmented + thanks to its framing layer. Meanwhile, we have to call Curl_read + to ensure that http2_handle_stream_close is called when we read all + incoming bytes for a particular stream. */ + bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET); + bool data_eof_handled = is_http3 + || Curl_conn_is_http2(data, conn, FIRSTSOCKET); + + if(!data_eof_handled && k->size != -1 && !k->header) { /* make sure we don't read too much */ curl_off_t totalleft = k->size - k->bytecount; if(totalleft < (curl_off_t)bytestoread) @@ -499,7 +472,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, else { /* read nothing but since we wanted nothing we consider this an OK situation to proceed from */ - DEBUGF(infof(data, "readwrite_data: we're done")); + DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done"))); nread = 0; } @@ -518,14 +491,9 @@ static CURLcode readwrite_data(struct Curl_easy *data, buf[nread] = 0; } else { - /* if we receive 0 or less here, either the http2 stream is closed or the + /* if we receive 0 or less here, either the data transfer is done or the server closed the connection and we bail out from this! */ -#ifdef USE_NGHTTP2 - if(is_http2 && !nread) - DEBUGF(infof(data, "nread == 0, stream closed, bailing")); - else -#endif - if(is_http3 && !nread) + if(data_eof_handled) DEBUGF(infof(data, "nread == 0, stream closed, bailing")); else DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); @@ -776,8 +744,8 @@ static CURLcode readwrite_data(struct Curl_easy *data, k->keepon &= ~KEEP_RECV; } - if(k->keepon & KEEP_RECV_PAUSE) { - /* this is a paused transfer */ + if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) { + /* this is a paused or stopped transfer */ break; } @@ -799,19 +767,18 @@ static CURLcode readwrite_data(struct Curl_easy *data, } out: - DEBUGF(infof(data, "readwrite_data(handle=%p) -> %d", data, result)); + if(result) + DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result)); return result; } CURLcode Curl_done_sending(struct Curl_easy *data, struct SingleRequest *k) { - struct connectdata *conn = data->conn; k->keepon &= ~KEEP_SEND; /* we're done writing */ /* These functions should be moved into the handler struct! */ - Curl_http2_done_sending(data, conn); - Curl_quic_done_sending(data); + Curl_conn_ev_data_done_send(data); return CURLE_OK; } @@ -1088,6 +1055,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, { struct SingleRequest *k = &data->req; CURLcode result; + struct curltime now; int didwhat = 0; curl_socket_t fd_read; @@ -1113,6 +1081,8 @@ CURLcode Curl_readwrite(struct connectdata *conn, if(data->state.drain) { select_res |= CURL_CSELECT_IN; DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data")); + if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) + select_res |= CURL_CSELECT_OUT; } #endif @@ -1155,7 +1125,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, } #endif - k->now = Curl_now(); + now = Curl_now(); if(!didwhat) { /* no read no write, this is a timeout? */ if(k->exp100 == EXP100_AWAITING_CONTINUE) { @@ -1172,7 +1142,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, */ - timediff_t ms = Curl_timediff(k->now, k->start100); + 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; @@ -1182,35 +1152,31 @@ CURLcode Curl_readwrite(struct connectdata *conn, } } -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - result = Curl_quic_idle(data); - if(result) - goto out; - } -#endif + result = Curl_conn_ev_data_idle(data); + if(result) + goto out; } if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(data, k->now); + result = Curl_speedcheck(data, now); if(result) goto out; if(k->keepon) { - if(0 > Curl_timeleft(data, &k->now, FALSE)) { + if(0 > Curl_timeleft(data, &now, FALSE)) { if(k->size != -1) { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(k->now, data->progress.t_startsingle), + Curl_timediff(now, data->progress.t_startsingle), k->bytecount, k->size); } else { failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(k->now, data->progress.t_startsingle), + Curl_timediff(now, data->progress.t_startsingle), k->bytecount); } result = CURLE_OPERATION_TIMEDOUT; @@ -1264,7 +1230,8 @@ CURLcode Curl_readwrite(struct connectdata *conn, KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE; result = CURLE_OK; out: - DEBUGF(infof(data, "Curl_readwrite(handle=%p) -> %d", data, result)); + if(result) + DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result)); return result; } @@ -1377,6 +1344,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; Curl_safefree(data->info.wouldredirect); + Curl_data_priority_clear_state(data); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; @@ -1389,15 +1357,16 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) else data->state.infilesize = 0; -#ifndef CURL_DISABLE_COOKIES /* If there is a list of cookie files to read, do it now! */ - if(data->state.cookielist) - Curl_cookie_loadfiles(data); -#endif + Curl_cookie_loadfiles(data); + /* If there is a list of host pairs to deal with */ if(data->state.resolve) result = Curl_loadhostpairs(data); + /* If there is a list of hsts files to read */ + Curl_hsts_loadfiles(data); + if(!result) { /* Allow data->set.use_port to set which port to use. This needs to be * disabled for example when we follow Location: headers to URLs using @@ -1433,7 +1402,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } } #endif - Curl_http2_init_state(&data->state); result = Curl_hsts_loadcb(data, data->hsts); } @@ -1871,7 +1839,7 @@ Curl_setup_transfer( 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 || httpsending) { /* when multiplexing, the read/write sockets need to be the same! */ conn->sockfd = sockindex == -1 ? ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index 4092508..536ac24 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/url.c b/Utilities/cmcurl/lib/url.c index 3ab63a0..1bb93df 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -431,10 +431,17 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_dyn_free(&data->state.headerb); Curl_safefree(data->state.ulbuf); Curl_flush_cookies(data, TRUE); +#ifndef CURL_DISABLE_COOKIES + curl_slist_free_all(data->set.cookielist); /* clean up list */ +#endif 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]); - Curl_hsts_cleanup(&data->hsts); +#ifndef CURL_DISABLE_HSTS + if(!data->share || !data->share->hsts) + Curl_hsts_cleanup(&data->hsts); + curl_slist_free_all(data->set.hstslist); /* clean up list */ +#endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) Curl_http_auth_cleanup_digest(data); #endif @@ -445,7 +452,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_resolver_cancel(data); Curl_resolver_cleanup(data->state.async.resolver); - Curl_http2_cleanup_dependencies(data); + Curl_data_priority_cleanup(data); /* No longer a dirty share, if it exists */ if(data->share) { @@ -531,11 +538,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) /* Timeout every 24 hours by default */ set->general_ssl.ca_cache_timeout = 24 * 60 * 60; - set->proxyport = 0; - set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ #ifndef CURL_DISABLE_PROXY + set->proxyport = 0; + set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ /* SOCKS5 proxy auth defaults to username/password + GSS-API */ set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; @@ -556,11 +563,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) #endif set->ssl.primary.verifypeer = TRUE; set->ssl.primary.verifyhost = TRUE; -#ifdef USE_TLS_SRP - set->ssl.primary.authtype = CURL_TLSAUTH_NONE; -#endif - /* defaults to any auth type */ +#ifdef USE_SSH + /* defaults to any auth type */ set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; + set->new_directory_perms = 0755; /* Default permissions */ +#endif set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by default */ #ifndef CURL_DISABLE_PROXY @@ -568,7 +575,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) #endif set->new_file_perms = 0644; /* Default permissions */ - set->new_directory_perms = 0755; /* Default permissions */ set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | CURLPROTO_FTPS; @@ -631,14 +637,15 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->maxage_conn = 118; set->maxlifetime_conn = 0; set->http09_allowed = FALSE; - set->httpwant = #ifdef USE_HTTP2 - CURL_HTTP_VERSION_2TLS + set->httpwant = CURL_HTTP_VERSION_2TLS #else - CURL_HTTP_VERSION_1_1 + set->httpwant = CURL_HTTP_VERSION_1_1 #endif ; - Curl_http2_init_userset(set); +#if defined(USE_HTTP2) || defined(USE_HTTP3) + memset(&set->priority, 0, sizeof(set->priority)); +#endif set->quick_exit = 0L; return result; } @@ -698,45 +705,6 @@ CURLcode Curl_open(struct Curl_easy **curl) return result; } -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -static void conn_reset_postponed_data(struct connectdata *conn, int num) -{ - struct postponed_data * const psnd = &(conn->postponed[num]); - if(psnd->buffer) { - DEBUGASSERT(psnd->allocated_size > 0); - DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); - DEBUGASSERT(psnd->recv_size ? - (psnd->recv_processed < psnd->recv_size) : - (psnd->recv_processed == 0)); - DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD); - free(psnd->buffer); - psnd->buffer = NULL; - psnd->allocated_size = 0; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ - } - else { - DEBUGASSERT(psnd->allocated_size == 0); - DEBUGASSERT(psnd->recv_size == 0); - DEBUGASSERT(psnd->recv_processed == 0); - DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD); - } -} - -static void conn_reset_all_postponed_data(struct connectdata *conn) -{ - conn_reset_postponed_data(conn, 0); - conn_reset_postponed_data(conn, 1); -} -#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ -/* Use "do-nothing" macro instead of function when workaround not used */ -#define conn_reset_all_postponed_data(c) do {} while(0) -#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ - - static void conn_shutdown(struct Curl_easy *data) { DEBUGASSERT(data); @@ -777,13 +745,14 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->sasl_authzid); Curl_safefree(conn->options); Curl_safefree(conn->oauth_bearer); +#ifndef CURL_DISABLE_HTTP Curl_dyn_free(&conn->trailer); +#endif Curl_safefree(conn->host.rawalloc); /* host name buffer */ Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); - conn_reset_all_postponed_data(conn); Curl_llist_destroy(&conn->easyq, NULL); Curl_safefree(conn->localdev); Curl_free_primary_ssl_config(&conn->ssl_config); @@ -867,24 +836,6 @@ void Curl_disconnect(struct Curl_easy *data, } /* - * This function should return TRUE if the socket is to be assumed to - * be dead. Most commonly this happens when the server has closed the - * connection due to inactivity. - */ -static bool SocketIsDead(curl_socket_t sock) -{ - int sval; - bool ret_val = TRUE; - - sval = SOCKET_READABLE(sock, 0); - if(sval == 0) - /* timeout */ - ret_val = FALSE; - - return ret_val; -} - -/* * IsMultiplexingPossible() * * Return a bitmask with the available multiplexing options for the given @@ -1014,8 +965,7 @@ static bool extract_if_dead(struct connectdata *conn, } else { - /* Use the general method for determining the death of a connection */ - dead = SocketIsDead(conn->sock[FIRSTSOCKET]); + dead = !Curl_conn_is_alive(data, conn); } if(dead) { @@ -1344,8 +1294,10 @@ ConnectionExists(struct Curl_easy *data, /* If multiplexing isn't enabled on the h2 connection and h1 is explicitly requested, handle it: */ if((needle->handler->protocol & PROTO_FAMILY_HTTP) && - (check->httpversion >= 20) && - (data->state.httpwant < CURL_HTTP_VERSION_2_0)) + (((check->httpversion >= 20) && + (data->state.httpwant < CURL_HTTP_VERSION_2_0)) + || ((check->httpversion >= 30) && + (data->state.httpwant < CURL_HTTP_VERSION_3)))) continue; if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) { @@ -1467,9 +1419,8 @@ ConnectionExists(struct Curl_easy *data, #ifdef USE_NGHTTP2 /* If multiplexed, make sure we don't go over concurrency limit */ if(check->bits.multiplex) { - /* Multiplexed connections can only be HTTP/2 for now */ - struct http_conn *httpc = &check->proto.httpc; - if(multiplexed >= httpc->settings.max_concurrent_streams) { + if(multiplexed >= Curl_conn_get_max_concurrent(data, check, + FIRSTSOCKET)) { infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", multiplexed); continue; @@ -1551,15 +1502,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */ conn->connection_id = -1; /* no ID */ conn->port = -1; /* unknown at this point */ conn->remote_port = -1; /* unknown at this point */ -#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD) - conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ - conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */ /* Default protocol-independent behavior doesn't support persistent connections, so we set this to force-close. Protocols that support @@ -2329,7 +2274,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto error; } - /* path will be "/", if no path was was found */ + /* path will be "/", if no path was found */ if(strcmp("/", path)) { is_unix_proxy = TRUE; free(host); @@ -2412,6 +2357,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, char *socksproxy = NULL; char *no_proxy = NULL; CURLcode result = CURLE_OK; + bool spacesep = FALSE; /************************************************************* * Extract the user and password from the authentication string @@ -2458,7 +2404,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy)) { + data->set.str[STRING_NOPROXY] : no_proxy, + &spacesep)) { Curl_safefree(proxy); Curl_safefree(socksproxy); } @@ -2467,6 +2414,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ + if(spacesep) + infof(data, "space-separated NOPROXY patterns are deprecated"); Curl_safefree(no_proxy); @@ -2795,7 +2744,7 @@ static CURLcode override_login(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } /* no user was set but a password, set a blank user */ - if(userp && !*userp && *passwdp) { + if(!*userp && *passwdp) { *userp = strdup(""); if(!*userp) return CURLE_OUT_OF_MEMORY; @@ -3345,12 +3294,6 @@ static void reuse_conn(struct Curl_easy *data, struct connectdata *temp, struct connectdata *existing) { - /* 'local_ip' and 'local_port' get filled with local's numerical - ip address and port number whenever an outgoing connection is - **established** from the primary socket to a remote address. */ - char local_ip[MAX_IPADR_LEN] = ""; - int local_port = -1; - /* get the user+password information from the temp struct since it may * be new for this request even when we re-use an existing connection */ if(temp->user) { @@ -3382,6 +3325,20 @@ static void reuse_conn(struct Curl_easy *data, } #endif + /* Finding a connection for reuse in the cache matches, among other + * things on the "remote-relevant" hostname. This is not necessarily + * the authority of the URL, e.g. conn->host. For example: + * - we use a proxy (not tunneling). we want to send all requests + * that use the same proxy on this connection. + * - we have a "connect-to" setting that may redirect the hostname of + * a new request to the same remote endpoint of an existing conn. + * We want to reuse an existing conn to the remote endpoint. + * Since connection reuse does not match on conn->host necessarily, we + * switch `existing` conn to `temp` conn's host settings. + * TODO: is this correct in the case of TLS connections that have + * used the original hostname in SNI to negotiate? Do we send + * requests for another host through the different SNI? + */ Curl_free_idnconverted_hostname(&existing->host); Curl_free_idnconverted_hostname(&existing->conn_to_host); Curl_safefree(existing->host.rawalloc); @@ -3398,15 +3355,6 @@ static void reuse_conn(struct Curl_easy *data, existing->hostname_resolve = temp->hostname_resolve; temp->hostname_resolve = NULL; - /* persist connection info in session handle */ - if(existing->transport == TRNSPRT_TCP) { - Curl_conninfo_local(data, existing->sock[FIRSTSOCKET], - local_ip, &local_port); - } - Curl_persistconninfo(data, existing, local_ip, local_port); - - conn_reset_all_postponed_data(temp); /* free buffers */ - /* re-use init */ existing->bits.reuse = TRUE; /* yes, we're re-using here */ @@ -3880,6 +3828,13 @@ static CURLcode create_conn(struct Curl_easy *data, * Resolve the address of the server or proxy *************************************************************/ result = resolve_server(data, conn, async); + if(result) + goto out; + + /* Everything general done, inform filters that they need + * to prepare for a data transfer. + */ + result = Curl_conn_ev_data_setup(data); out: return result; @@ -4007,7 +3962,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) data->state.httpreq = HTTPREQ_HEAD; k->start = Curl_now(); /* start time */ - k->now = k->start; /* current time is now */ k->header = TRUE; /* assume header */ k->bytecount = 0; k->ignorebody = FALSE; @@ -4018,3 +3972,103 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) return CURLE_OK; } + +#if defined(USE_HTTP2) || defined(USE_HTTP3) + +#ifdef USE_NGHTTP2 + +static void priority_remove_child(struct Curl_easy *parent, + struct Curl_easy *child) +{ + struct Curl_data_prio_node **pnext = &parent->set.priority.children; + struct Curl_data_prio_node *pnode = parent->set.priority.children; + + DEBUGASSERT(child->set.priority.parent == parent); + while(pnode && pnode->data != child) { + pnext = &pnode->next; + pnode = pnode->next; + } + + DEBUGASSERT(pnode); + if(pnode) { + *pnext = pnode->next; + free(pnode); + } + + child->set.priority.parent = 0; + child->set.priority.exclusive = FALSE; +} + +CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, + struct Curl_easy *child, + bool exclusive) +{ + if(child->set.priority.parent) { + priority_remove_child(child->set.priority.parent, child); + } + + if(parent) { + struct Curl_data_prio_node **tail; + struct Curl_data_prio_node *pnode; + + pnode = calloc(1, sizeof(*pnode)); + if(!pnode) + return CURLE_OUT_OF_MEMORY; + pnode->data = child; + + if(parent->set.priority.children && exclusive) { + /* exclusive: move all existing children underneath the new child */ + struct Curl_data_prio_node *node = parent->set.priority.children; + while(node) { + node->data->set.priority.parent = child; + node = node->next; + } + + tail = &child->set.priority.children; + while(*tail) + tail = &(*tail)->next; + + DEBUGASSERT(!*tail); + *tail = parent->set.priority.children; + parent->set.priority.children = 0; + } + + tail = &parent->set.priority.children; + while(*tail) { + (*tail)->data->set.priority.exclusive = FALSE; + tail = &(*tail)->next; + } + + DEBUGASSERT(!*tail); + *tail = pnode; + } + + child->set.priority.parent = parent; + child->set.priority.exclusive = exclusive; + return CURLE_OK; +} + +#endif /* USE_NGHTTP2 */ + +void Curl_data_priority_cleanup(struct Curl_easy *data) +{ +#ifdef USE_NGHTTP2 + while(data->set.priority.children) { + struct Curl_easy *tmp = data->set.priority.children->data; + priority_remove_child(data, tmp); + if(data->set.priority.parent) + Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); + } + + if(data->set.priority.parent) + priority_remove_child(data->set.priority.parent, data); +#endif + (void)data; +} + +void Curl_data_priority_clear_state(struct Curl_easy *data) +{ + memset(&data->state.priority, 0, sizeof(data->state.priority)); +} + +#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */ diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index 1a03c56..3b58df4 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -59,4 +59,20 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme, void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn); #endif +#if defined(USE_HTTP2) || defined(USE_HTTP3) +void Curl_data_priority_cleanup(struct Curl_easy *data); +void Curl_data_priority_clear_state(struct Curl_easy *data); +#else +#define Curl_data_priority_cleanup(x) +#define Curl_data_priority_clear_state(x) +#endif /* !(defined(USE_HTTP2) || defined(USE_HTTP3)) */ + +#ifdef USE_NGHTTP2 +CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, + struct Curl_easy *child, + bool exclusive); +#else +#define Curl_data_priority_add_child(x, y, z) CURLE_NOT_BUILT_IN +#endif + #endif /* HEADER_CURL_URL_H */ diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index 43a83ef..28e5dd7 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index b96af35..29927b3 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -33,6 +33,7 @@ #include "inet_pton.h" #include "inet_ntop.h" #include "strdup.h" +#include "idn.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -116,14 +117,11 @@ static const char *find_host_sep(const char *url) } /* - * Decide in an encoding-independent manner whether a character in a URL must - * be escaped. This is used in urlencode_str(). + * Decide whether a character in a URL must be escaped. */ -static bool urlchar_needs_escaping(int c) -{ - return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)); -} +#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c))) +static const char hexdigits[] = "0123456789abcdef"; /* urlencode_str() writes data into an output dynbuf and URL-encodes the * spaces in the source URL accordingly. * @@ -167,7 +165,10 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, left = FALSE; if(urlchar_needs_escaping(*iptr)) { - if(Curl_dyn_addf(o, "%%%02x", *iptr)) + char out[3]={'%'}; + out[1] = hexdigits[*iptr>>4]; + out[2] = hexdigits[*iptr & 0xf]; + if(Curl_dyn_addn(o, out, 3)) return CURLUE_OUT_OF_MEMORY; } else { @@ -492,35 +493,21 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, bool has_scheme) { - char *portptr = NULL; - char endbracket; - int len; + char *portptr; char *hostname = Curl_dyn_ptr(host); /* * Find the end of an IPv6 address, either on the ']' ending bracket or * a percent-encoded zone index. */ - if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n", - &endbracket, &len)) { - if(']' == endbracket) - portptr = &hostname[len]; - else if('%' == endbracket) { - int zonelen = len; - if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) { - if(']' != endbracket) - return CURLUE_BAD_IPV6; - portptr = &hostname[--zonelen + len + 1]; - } - else - return CURLUE_BAD_IPV6; - } - else + if(hostname[0] == '[') { + portptr = strchr(hostname, ']'); + if(!portptr) return CURLUE_BAD_IPV6; - + portptr++; /* this is a RFC2732-style specified IP-address */ - if(portptr && *portptr) { + if(*portptr) { if(*portptr != ':') - return CURLUE_BAD_IPV6; + return CURLUE_BAD_PORT_NUMBER; } else portptr = NULL; @@ -584,11 +571,9 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, hostname++; hlen -= 2; - if(hostname[hlen] != ']') - return CURLUE_BAD_IPV6; - - /* only valid letters are ok */ + /* only valid IPv6 letters are ok */ len = strspn(hostname, l); + if(hlen != len) { hlen = len; if(hostname[len] == '%') { @@ -602,8 +587,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, while(*h && (*h != ']') && (i < 15)) zoneid[i++] = *h++; if(!i || (']' != *h)) - /* impossible to reach? */ - return CURLUE_MALFORMED_INPUT; + return CURLUE_BAD_IPV6; zoneid[i] = 0; u->zoneid = strdup(zoneid); if(!u->zoneid) @@ -782,25 +766,28 @@ static CURLUcode decode_host(struct dynbuf *host) * * RETURNS * - * an allocated dedotdotified output string + * Zero for success and 'out' set to an allocated dedotdotified string. */ -UNITTEST char *dedotdotify(const char *input, size_t clen); -UNITTEST char *dedotdotify(const char *input, size_t clen) +UNITTEST int dedotdotify(const char *input, size_t clen, char **outp); +UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) { - char *out = malloc(clen + 1); char *outptr; const char *orginput = input; char *queryp; + char *out; + + *outp = NULL; + /* the path always starts with a slash, and a slash has not dot */ + if((clen < 2) || !memchr(input, '.', clen)) + return 0; + + out = malloc(clen + 1); if(!out) - return NULL; /* out of memory */ + return 1; /* out of memory */ *out = 0; /* null-terminates, for inputs like "./" */ outptr = out; - if(!*input) - /* zero length input string, return that */ - return out; - /* * To handle query-parts properly, we must find it and remove it during the * dotdot-operation and then append it again at the end to the output @@ -905,7 +892,8 @@ UNITTEST char *dedotdotify(const char *input, size_t clen) memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */ } - return out; + *outp = out; + return 0; /* success */ } static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) @@ -1152,7 +1140,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) size_t qlen = strlen(query) - fraglen; /* includes '?' */ pathlen = strlen(path) - qlen - fraglen; if(qlen > 1) { - if(qlen && (flags & CURLU_URLENCODE)) { + if(flags & CURLU_URLENCODE) { struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); /* skip the leading question mark */ @@ -1199,8 +1187,8 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) path = u->path = Curl_dyn_ptr(&enc); } - if(!pathlen) { - /* there is no path left, unset */ + if(pathlen <= 1) { + /* there is no path left or just the slash, unset */ path = NULL; } else { @@ -1224,13 +1212,16 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if(!(flags & CURLU_PATH_AS_IS)) { /* remove ../ and ./ sequences according to RFC3986 */ - char *newp = dedotdotify((char *)path, pathlen); - if(!newp) { + char *dedot; + int err = dedotdotify((char *)path, pathlen, &dedot); + if(err) { result = CURLUE_OUT_OF_MEMORY; goto fail; } - free(u->path); - u->path = newp; + if(dedot) { + free(u->path); + u->path = dedot; + } } } @@ -1379,6 +1370,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, char portbuf[7]; bool urldecode = (flags & CURLU_URLDECODE)?1:0; bool urlencode = (flags & CURLU_URLENCODE)?1:0; + bool punycode = FALSE; bool plusdecode = FALSE; (void)flags; if(!u) @@ -1408,6 +1400,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, case CURLUPART_HOST: ptr = u->host; ifmissing = CURLUE_NO_HOST; + punycode = (flags & CURLU_PUNYCODE)?1:0; break; case CURLUPART_ZONEID: ptr = u->zoneid; @@ -1460,6 +1453,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, char *options = u->options; char *port = u->port; char *allochost = NULL; + punycode = (flags & CURLU_PUNYCODE)?1:0; if(u->scheme && strcasecompare("file", u->scheme)) { url = aprintf("file://%s%s%s", u->path, @@ -1514,6 +1508,17 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, if(!allochost) return CURLUE_OUT_OF_MEMORY; } + else if(punycode) { + if(!Curl_is_ASCII_name(u->host)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + allochost = Curl_idn_decode(u->host); + if(!allochost) + return CURLUE_OUT_OF_MEMORY; +#endif + } + } else { /* only encode '%' in output host name */ char *host = u->host; @@ -1611,6 +1616,19 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what, free(*part); *part = Curl_dyn_ptr(&enc); } + else if(punycode) { + if(!Curl_is_ASCII_name(u->host)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + char *allochost = Curl_idn_decode(*part); + if(!allochost) + return CURLUE_OUT_OF_MEMORY; + free(*part); + *part = allochost; +#endif + } + } return CURLUE_OK; } @@ -1807,7 +1825,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_OUT_OF_MEMORY; } else { - result = Curl_dyn_addf(&enc, "%%%02x", *i); + char out[3]={'%'}; + out[1] = hexdigits[*i>>4]; + out[2] = hexdigits[*i & 0xf]; + result = Curl_dyn_addn(&enc, out, 3); if(result) return CURLUE_OUT_OF_MEMORY; } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 3d7545c..4cfffa7 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -113,23 +113,6 @@ typedef unsigned int curl_prot_t; input easier and better. */ #define CURL_MAX_INPUT_LENGTH 8000000 -/* Macros intended for DEBUGF logging, use like: - * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much")); - * and it will output: - * [CONN-1-0][CF-SSL] this filter very much rocks - * on connection #1 with sockindex 0 for filter of type "SSL". */ -#define DMSG(d,msg) \ - "[CONN-%ld] "msg, (d)->conn->connection_id -#define DMSGI(d,i,msg) \ - "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i) -#define CMSG(c,msg) \ - "[CONN-%ld] "msg, (conn)->connection_id -#define CMSGI(c,i,msg) \ - "[CONN-%ld-%d] "msg, (conn)->connection_id, (i) -#define CFMSG(cf,msg) \ - "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \ - (cf)->sockindex, (cf)->cft->name - #include "cookie.h" #include "psl.h" @@ -187,8 +170,8 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, #include "mqtt.h" #include "wildcard.h" #include "multihandle.h" -#include "quic.h" #include "c-hyper.h" +#include "cf-socket.h" #ifdef HAVE_GSSAPI # ifdef HAVE_GSSGNU @@ -268,8 +251,6 @@ typedef enum { struct ssl_backend_data; struct ssl_primary_config { - long version; /* what version the client wants to use */ - long version_max; /* max supported version the client wants to use */ char *CApath; /* certificate dir (doesn't work on windows) */ char *CAfile; /* certificate to verify peer against */ char *issuercert; /* optional issuer certificate filename */ @@ -284,10 +265,11 @@ struct ssl_primary_config { #ifdef USE_TLS_SRP char *username; /* TLS username (for, e.g., SRP) */ char *password; /* TLS password (for, e.g., SRP) */ - enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */ #endif char *curves; /* list of curves to use */ unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ + unsigned int version_max; /* max supported version the client wants to use */ + unsigned char version; /* what version the client wants to use */ BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ @@ -648,7 +630,6 @@ struct SingleRequest { curl_off_t pendingheader; /* this many bytes left to send is actually header and not body */ struct curltime start; /* transfer started at this time */ - struct curltime now; /* current time */ enum { HEADER_NORMAL, /* no bad header at all */ HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest @@ -707,6 +688,7 @@ struct SingleRequest { struct dohdata *doh; /* DoH specific data for this request */ #endif unsigned char setcookies; + unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ BIT(content_range); /* set TRUE if Content-Range: was found */ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding @@ -783,8 +765,8 @@ struct Curl_handler { /* This function *MAY* be set to a protocol-dependent function that is run * by the curl_disconnect(), as a step in the disconnection. If the handler * is called because the connection has been considered dead, - * dead_connection is set to TRUE. The connection is already disassociated - * from the transfer here. + * dead_connection is set to TRUE. The connection is (again) associated with + * the transfer here. */ CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, bool dead_connection); @@ -830,7 +812,7 @@ struct Curl_handler { #define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per request instead of per connection */ #define PROTOPT_ALPN (1<<8) /* set ALPN for this */ -#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */ +/* (1<<9) was PROTOPT_STREAM, now free */ #define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field of the URL */ #define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a @@ -848,20 +830,6 @@ struct Curl_handler { #define CONNRESULT_NONE 0 /* No extra information. */ #define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -struct postponed_data { - char *buffer; /* Temporal store for received data during - sending, must be freed */ - size_t allocated_size; /* Size of temporal store */ - size_t recv_size; /* Size of received data during sending */ - size_t recv_processed; /* Size of processed part of postponed data */ -#ifdef DEBUGBUILD - curl_socket_t bindsock;/* Structure must be bound to specific socket, - used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ -}; -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ - struct proxy_info { struct hostname host; int port; @@ -909,16 +877,9 @@ struct connectdata { there is no name resolve done. */ struct Curl_dns_entry *dns_entry; - /* 'ip_addr' is the particular IP we connected to. It points to a struct - within the DNS cache, so this pointer is only valid as long as the DNS - cache entry remains locked. It gets unlocked in multi_done() */ - struct Curl_addrinfo *ip_addr; - struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */ - -#ifdef ENABLE_QUIC - struct quicsocket hequic[2]; /* two, for happy eyeballs! */ - struct quicsocket *quic; -#endif + /* 'remote_addr' is the particular IP we connected to. it is owned, set + * and NULLed by the connected socket filter (if there is one). */ + const struct Curl_sockaddr_ex *remote_addr; struct hostname host; char *hostname_resolve; /* host name to resolve to address, allocated */ @@ -947,31 +908,16 @@ struct connectdata { struct curltime lastused; /* when returned to the connection cache */ curl_socket_t sock[2]; /* two sockets, the second is used for the data transfer when doing FTP */ - curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */ - int tempfamily[2]; /* family used for the temp sockets */ Curl_recv *recv[2]; Curl_send *send[2]; struct Curl_cfilter *cfilter[2]; /* connection filters */ -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND - struct postponed_data postponed[2]; /* two buffers for two sockets */ -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ struct ssl_primary_config ssl_config; #ifndef CURL_DISABLE_PROXY struct ssl_primary_config proxy_ssl_config; #endif struct ConnectBits bits; /* various state-flags for this connection */ - /* connecttime: when connect() is called on the current IP address. Used to - be able to track when to move on to try next IP - but only when the multi - interface is used. */ - struct curltime connecttime; - - /* The field below gets set in Curl_connecthost */ - /* how long time in milliseconds to spend on trying to connect to each IP - address, per family */ - timediff_t timeoutms_per_addr[2]; - const struct Curl_handler *handler; /* Connection's protocol handler */ const struct Curl_handler *given; /* The protocol first given */ @@ -1034,16 +980,15 @@ struct connectdata { struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ #endif +#ifndef CURL_DISABLE_HTTP /* for chunked-encoded trailer */ struct dynbuf trailer; +#endif union { #ifndef CURL_DISABLE_FTP struct ftp_conn ftpc; #endif -#ifndef CURL_DISABLE_HTTP - struct http_conn httpc; -#endif #ifdef USE_SSH struct ssh_conn sshc; #endif @@ -1070,6 +1015,9 @@ struct connectdata { #ifndef CURL_DISABLE_MQTT struct mqtt_conn mqtt; #endif +#ifdef USE_WEBSOCKETS + struct ws_conn ws; +#endif } proto; struct connectbundle *bundle; /* The bundle we are member of */ @@ -1086,14 +1034,13 @@ struct connectdata { that subsequent bound-requested connections aren't accidentally re-using wrong connections. */ char *localdev; - int localportrange; + unsigned short localportrange; int cselect_bits; /* bitmask of socket events */ int waitfor; /* current READ/WRITE bits to wait for */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif - /* The field below gets set in Curl_connecthost */ - int num_addr; /* number of addresses to try to connect to */ + /* 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 @@ -1238,10 +1185,29 @@ struct auth { should be RFC compliant */ }; -struct Curl_http2_dep { - struct Curl_http2_dep *next; +#ifdef USE_NGHTTP2 +struct Curl_data_prio_node { + struct Curl_data_prio_node *next; struct Curl_easy *data; }; +#endif + +/** + * Priority information for an easy handle in relation to others + * on the same connection. + * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218 + */ +struct Curl_data_priority { +#ifdef USE_NGHTTP2 + /* tree like dependencies only implemented in nghttp2 */ + struct Curl_easy *parent; + struct Curl_data_prio_node *children; +#endif + int weight; +#ifdef USE_NGHTTP2 + BIT(exclusive); +#endif +}; /* * This struct is for holding data that was attempted to get sent to the user's @@ -1270,6 +1236,7 @@ typedef enum { EXPIRE_TOOFAST, EXPIRE_QUIC, EXPIRE_FTP_ACCEPT, + EXPIRE_ALPN_EYEBALLS, EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; @@ -1389,24 +1356,17 @@ struct UrlState { size_t drain; /* Increased when this stream has data to read, even if its socket is not necessarily is readable. Decreased when checked. */ + struct Curl_data_priority priority; /* shallow copy of data->set */ #endif curl_read_callback fread_func; /* read callback/function */ void *in; /* CURLOPT_READDATA */ -#ifdef USE_HTTP2 - struct Curl_easy *stream_depends_on; - int stream_weight; -#endif CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ char *url; /* work URL, copied from UserDefined */ char *referer; /* referer string */ -#ifndef CURL_DISABLE_COOKIES - struct curl_slist *cookielist; /* list of cookie files set by - curl_easy_setopt(COOKIEFILE) calls */ -#endif struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP @@ -1471,7 +1431,6 @@ struct UrlState { 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(stream_depends_e); /* set or don't set the Exclusive bit */ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ BIT(cookie_engine); BIT(prefer_ascii); /* ASCII rather than binary */ @@ -1620,15 +1579,9 @@ struct UserDefined { void *out; /* CURLOPT_WRITEDATA */ void *in_set; /* CURLOPT_READDATA */ void *writeheader; /* write the header to this if non-NULL */ - unsigned short proxyport; /* If non-zero, use this port number by - default. If the proxy string features a - ":[port]" that one will override this. */ unsigned short use_port; /* which port to use (when not using default) */ unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ -#ifndef CURL_DISABLE_PROXY - unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ -#endif long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ @@ -1638,8 +1591,9 @@ struct UserDefined { of strlen(), and then the data *may* be binary (contain zero bytes) */ unsigned short localport; /* local port number to bind to */ - int localportrange; /* number of additional port numbers to test in case the - 'localport' one can't be bind()ed */ + unsigned short localportrange; /* number of additional port numbers to test + in case the 'localport' one can't be + bind()ed */ curl_write_callback fwrite_func; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ @@ -1661,7 +1615,13 @@ struct UserDefined { void *prereq_userp; /* pre-initial request user data */ void *seek_client; /* pointer to pass to the seek callback */ +#ifndef CURL_DISABLE_COOKIES + struct curl_slist *cookielist; /* list of cookie files set by + curl_easy_setopt(COOKIEFILE) calls */ +#endif #ifndef CURL_DISABLE_HSTS + struct curl_slist *hstslist; /* list of HSTS files set by + curl_easy_setopt(HSTS) calls */ curl_hstsread_callback hsts_read; void *hsts_read_userp; curl_hstswrite_callback hsts_write; @@ -1688,17 +1648,8 @@ struct UserDefined { download */ curl_off_t set_resume_from; /* continue [ftp] transfer from here */ struct curl_slist *headers; /* linked list of extra headers */ - struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ struct curl_httppost *httppost; /* linked list of old POST data */ curl_mimepart mimepost; /* MIME/POST data. */ - struct curl_slist *quote; /* after connection is established */ - struct curl_slist *postquote; /* after the transfer */ - struct curl_slist *prequote; /* before the transfer, after type */ - struct curl_slist *source_quote; /* 3rd party quote */ - struct curl_slist *source_prequote; /* in 3rd party transfer mode - before - the transfer on source host */ - struct curl_slist *source_postquote; /* in 3rd party transfer mode - after - the transfer on source host */ #ifndef CURL_DISABLE_TELNET struct curl_slist *telnet_options; /* linked list of telnet options */ #endif @@ -1708,13 +1659,18 @@ struct UserDefined { the hostname and port to connect to */ time_t timevalue; /* what time to compare with */ unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ - unsigned char proxytype; /* what kind of proxy: curl_proxytype */ unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ unsigned char httpwant; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ struct ssl_config_data ssl; /* user defined SSL stuff */ #ifndef CURL_DISABLE_PROXY struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ + struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ + unsigned short proxyport; /* If non-zero, use this port number by + default. If the proxy string features a + ":[port]" that one will override this. */ + unsigned char proxytype; /* what kind of proxy: curl_proxytype */ + unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ #endif struct ssl_general_config general_ssl; /* general user defined SSL stuff */ int dns_cache_timeout; /* DNS cache timeout (seconds) */ @@ -1722,7 +1678,9 @@ struct UserDefined { unsigned int upload_buffer_size; /* size of upload buffer to use, keep it >= CURL_MAX_WRITE_SIZE */ void *private_data; /* application-private data */ +#ifndef CURL_DISABLE_HTTP struct curl_slist *http200aliases; /* linked list of aliases for http200 */ +#endif unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ curl_off_t max_filesize; /* Maximum file size to download */ @@ -1732,26 +1690,32 @@ struct UserDefined { unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ #endif - /* Desppie the name ftp_create_missing_dirs is for FTP(S) and SFTP +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + struct curl_slist *quote; /* after connection is established */ + struct curl_slist *postquote; /* after the transfer */ + struct curl_slist *prequote; /* before the transfer, after type */ + /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP 1 - create directories that don't exist 2 - the same but also allow MKD to fail once */ unsigned char ftp_create_missing_dirs; +#endif #ifdef USE_LIBSSH2 curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */ void *ssh_hostkeyfunc_userp; /* custom pointer to callback */ #endif - +#ifdef USE_SSH curl_sshkeycallback ssh_keyfunc; /* key matching callback */ void *ssh_keyfunc_userp; /* custom pointer to callback */ + int ssh_auth_types; /* allowed SSH auth types */ + unsigned int new_directory_perms; /* when creating remote dirs */ +#endif #ifndef CURL_DISABLE_NETRC unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ #endif curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or IMAP or POP3 or others! */ unsigned int new_file_perms; /* when creating remote files */ - unsigned int new_directory_perms; /* when creating remote dirs */ - int ssh_auth_types; /* allowed SSH auth types */ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ struct curl_blob *blobs[BLOB_LAST]; #ifdef ENABLE_IPV6 @@ -1759,8 +1723,9 @@ struct UserDefined { #endif curl_prot_t allowed_protocols; curl_prot_t redir_protocols; +#ifndef CURL_DISABLE_MIME unsigned int mime_options; /* Mime option flags. */ - +#endif #ifndef CURL_DISABLE_RTSP void *rtp_out; /* write RTP to this if non-NULL */ /* Common RTSP header options */ @@ -1775,8 +1740,9 @@ struct UserDefined { to pattern (e.g. if WILDCARDMATCH is on) */ void *fnmatch_data; #endif - long gssapi_delegation; /* GSS-API credential delegation, see the - documentation of CURLOPT_GSSAPI_DELEGATION */ + /* GSS-API credential delegation, see the documentation of + CURLOPT_GSSAPI_DELEGATION */ + unsigned char gssapi_delegation; int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ @@ -1784,10 +1750,8 @@ struct UserDefined { size_t maxconnects; /* Max idle connections in the connection cache */ long expect_100_timeout; /* in milliseconds */ -#ifdef USE_HTTP2 - struct Curl_easy *stream_depends_on; - int stream_weight; - struct Curl_http2_dep *stream_dependents; +#if defined(USE_HTTP2) || defined(USE_HTTP3) + struct Curl_data_priority priority; #endif curl_resolver_start_callback resolver_start; /* optional callback called before resolver start */ @@ -1798,8 +1762,10 @@ struct UserDefined { struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ #endif CURLU *uh; /* URL handle for the current parsed URL */ +#ifndef CURL_DISABLE_HTTP void *trailer_data; /* pointer to pass to trailer data callback */ curl_trailer_callback trailer_callback; /* trailing data callback */ +#endif char keep_post; /* keep POSTs as POSTs after a 30x request; each bit represents a request, from 301 to 303 */ #ifndef CURL_DISABLE_SMTP @@ -1877,7 +1843,6 @@ struct UserDefined { BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers from user callbacks */ BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */ - BIT(stream_depends_e); /* set or don't set the Exclusive bit */ BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1 header */ BIT(abstract_unix_socket); diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c index b82b171..c651fc5 100644 --- a/Utilities/cmcurl/lib/vauth/cleartext.c +++ b/Utilities/cmcurl/lib/vauth/cleartext.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -28,7 +28,8 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) + !defined(CURL_DISABLE_POP3) || \ + (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) #include <curl/curl.h> #include "urldata.h" diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c index 475d31b..5894ed4 100644 --- a/Utilities/cmcurl/lib/vauth/cram.c +++ b/Utilities/cmcurl/lib/vauth/cram.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index c81ce10..b7a0d92 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h index d785bdd..68fdb28 100644 --- a/Utilities/cmcurl/lib/vauth/digest.h +++ b/Utilities/cmcurl/lib/vauth/digest.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index 6c95a3e..8fb8669 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * 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/vauth/gsasl.c b/Utilities/cmcurl/lib/vauth/gsasl.c index a73c644..c7d0a8d 100644 --- a/Utilities/cmcurl/lib/vauth/gsasl.c +++ b/Utilities/cmcurl/lib/vauth/gsasl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Simon Josefsson, <simon@josefsson.org>, et al. + * Copyright (C) Simon Josefsson, <simon@josefsson.org>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c index bac7804..65eb3e1 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. + * 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/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c index 015bc66..c487149 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 0141e17..2a5d4a4 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/ntlm.h b/Utilities/cmcurl/lib/vauth/ntlm.h index 14ebba2..31ce921 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.h +++ b/Utilities/cmcurl/lib/vauth/ntlm.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c index 193576a..5118963 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c +++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/oauth2.c b/Utilities/cmcurl/lib/vauth/oauth2.c index 1604b30..a4adbdc 100644 --- a/Utilities/cmcurl/lib/vauth/oauth2.c +++ b/Utilities/cmcurl/lib/vauth/oauth2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -27,7 +27,8 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) + !defined(CURL_DISABLE_POP3) || \ + (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) #include <curl/curl.h> #include "urldata.h" diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c index 25dff96..e1d52b7 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c index d845cac..d3245d0 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c index 58fe051..62fc7c4 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.c +++ b/Utilities/cmcurl/lib/vauth/vauth.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h index c310c66..e17d7aa 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.h +++ b/Utilities/cmcurl/lib/vauth/vauth.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index c80841c..55b54a0 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -24,12 +24,16 @@ #include "curl_setup.h" +#ifdef USE_NGHTTP2 +#include <nghttp2/nghttp2.h> +#endif + #include <curl/curl.h> #include "urldata.h" #include "vtls/vtls.h" #include "http2.h" #include "vssh/ssh.h" -#include "quic.h" +#include "vquic/vquic.h" #include "curl_printf.h" #include "easy_lock.h" diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c index e8f14f9..872d5b4 100644 --- a/Utilities/cmcurl/lib/version_win32.c +++ b/Utilities/cmcurl/lib/version_win32.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h index 7a9a6a1..3899174 100644 --- a/Utilities/cmcurl/lib/version_win32.h +++ b/Utilities/cmcurl/lib/version_win32.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>. + * Copyright (C) Steve Holme, <steve_holme@hotmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c new file mode 100644 index 0000000..1930703 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c @@ -0,0 +1,841 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_MSH3 + +#include "urldata.h" +#include "timeval.h" +#include "multiif.h" +#include "sendf.h" +#include "curl_log.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "h2h3.h" +#include "curl_msh3.h" +#include "socketpair.h" +#include "vquic/vquic.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +#define DEBUG_CF 1 + +#if DEBUG_CF && defined(DEBUGBUILD) +#define CF_DEBUGF(x) x +#else +#define CF_DEBUGF(x) do { } while(0) +#endif + +#define MSH3_REQ_INIT_BUF_LEN 16384 +#define MSH3_REQ_MAX_BUF_LEN 0x100000 + +#ifdef _WIN32 +#define msh3_lock CRITICAL_SECTION +#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) +#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) +#define msh3_lock_acquire(lock) EnterCriticalSection(lock) +#define msh3_lock_release(lock) LeaveCriticalSection(lock) +#else /* !_WIN32 */ +#include <pthread.h> +#define msh3_lock pthread_mutex_t +#define msh3_lock_initialize(lock) do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(lock, &attr); \ + pthread_mutexattr_destroy(&attr); \ +}while(0) +#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) +#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) +#define msh3_lock_release(lock) pthread_mutex_unlock(lock) +#endif /* _WIN32 */ + + +static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, + void *IfContext); +static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, + void *IfContext); +static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, + void *IfContext, + MSH3_REQUEST *Request); +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header); +static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t *Length, + const uint8_t *Data); +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError); +static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, + void *IfContext); +static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, + void *IfContext, void *SendContext); + + +void Curl_msh3_ver(char *p, size_t len) +{ + uint32_t v[4]; + MsH3Version(v); + (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); +} + +#define SP_LOCAL 0 +#define SP_REMOTE 1 + +struct cf_msh3_ctx { + MSH3_API *api; + MSH3_CONNECTION *qconn; + struct Curl_sockaddr_ex addr; + curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */ + char l_ip[MAX_IPADR_LEN]; /* local IP as string */ + int l_port; /* local port number */ + struct curltime connect_started; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + /* Flags written by msh3/msquic thread */ + bool handshake_complete; + bool handshake_succeeded; + bool connected; + /* Flags written by curl thread */ + BIT(verbose); + BIT(active); +}; + +static const MSH3_CONNECTION_IF msh3_conn_if = { + msh3_conn_connected, + msh3_conn_shutdown_complete, + msh3_conn_new_request +}; + +static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, + void *IfContext) +{ + struct cf_msh3_ctx *ctx = IfContext; + (void)Connection; + if(ctx->verbose) + CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: connected\n")); + ctx->handshake_succeeded = true; + ctx->connected = true; + ctx->handshake_complete = true; +} + +static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, + void *IfContext) +{ + struct cf_msh3_ctx *ctx = IfContext; + (void)Connection; + if(ctx->verbose) + CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: shutdown complete\n")); + ctx->connected = false; + ctx->handshake_complete = true; +} + +static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, + void *IfContext, + MSH3_REQUEST *Request) +{ + (void)Connection; + (void)IfContext; + (void)Request; +} + +static const MSH3_REQUEST_IF msh3_request_if = { + msh3_header_received, + msh3_data_received, + msh3_complete, + msh3_shutdown_complete, + msh3_data_sent +}; + +static CURLcode msh3_data_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + (void)cf; + + DEBUGASSERT(stream); + if(!stream->recv_buf) { + DEBUGF(LOG_CF(data, cf, "req: setup")); + stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN); + if(!stream->recv_buf) { + return CURLE_OUT_OF_MEMORY; + } + stream->req = ZERO_NULL; + msh3_lock_initialize(&stream->recv_lock); + stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN; + stream->recv_buf_max = MSH3_REQ_MAX_BUF_LEN; + stream->recv_header_len = 0; + stream->recv_header_complete = false; + stream->recv_data_len = 0; + stream->recv_data_complete = false; + stream->recv_error = CURLE_OK; + } + return CURLE_OK; +} + +/* Requires stream->recv_lock to be held */ +static bool msh3request_ensure_room(struct HTTP *stream, size_t len) +{ + uint8_t *new_recv_buf; + const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; + + if(cur_recv_len + len > stream->recv_buf_alloc) { + size_t new_recv_buf_alloc_len = stream->recv_buf_alloc; + do { + new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */ + } while(cur_recv_len + len > new_recv_buf_alloc_len); + CF_DEBUGF(fprintf(stderr, "* enlarging buffer to %zu\n", + new_recv_buf_alloc_len)); + new_recv_buf = malloc(new_recv_buf_alloc_len); + if(!new_recv_buf) { + CF_DEBUGF(fprintf(stderr, "* FAILED: enlarging buffer to %zu\n", + new_recv_buf_alloc_len)); + return false; + } + if(cur_recv_len) { + memcpy(new_recv_buf, stream->recv_buf, cur_recv_len); + } + stream->recv_buf_alloc = new_recv_buf_alloc_len; + free(stream->recv_buf); + stream->recv_buf = new_recv_buf; + } + return true; +} + +static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, + void *IfContext, + const MSH3_HEADER *Header) +{ + struct Curl_easy *data = IfContext; + struct HTTP *stream = data->req.p.http; + size_t total_len; + (void)Request; + + if(stream->recv_header_complete) { + CF_DEBUGF(fprintf(stderr, "* ignoring header after data\n")); + return; + } + + msh3_lock_acquire(&stream->recv_lock); + + if((Header->NameLength == 7) && + !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) { + total_len = 10 + Header->ValueLength; + if(!msh3request_ensure_room(stream, total_len)) { + CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n", + (int)Header->NameLength, Header->Name)); + stream->recv_error = CURLE_OUT_OF_MEMORY; + goto release_lock; + } + msnprintf((char *)stream->recv_buf + stream->recv_header_len, + stream->recv_buf_alloc - stream->recv_header_len, + "HTTP/3 %.*s \r\n", (int)Header->ValueLength, Header->Value); + } + else { + total_len = 4 + Header->NameLength + Header->ValueLength; + if(!msh3request_ensure_room(stream, total_len)) { + CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n", + (int)Header->NameLength, Header->Name)); + stream->recv_error = CURLE_OUT_OF_MEMORY; + goto release_lock; + } + msnprintf((char *)stream->recv_buf + stream->recv_header_len, + stream->recv_buf_alloc - stream->recv_header_len, + "%.*s: %.*s\r\n", + (int)Header->NameLength, Header->Name, + (int)Header->ValueLength, Header->Value); + } + + stream->recv_header_len += total_len; + data->state.drain = 1; + +release_lock: + msh3_lock_release(&stream->recv_lock); +} + +static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, + void *IfContext, uint32_t *Length, + const uint8_t *Data) +{ + struct Curl_easy *data = IfContext; + struct HTTP *stream = data->req.p.http; + size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; + + (void)Request; + if(data && data->set.verbose) + CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: received %u. %zu buffered, " + "%zu allocated\n", + *Length, cur_recv_len, stream->recv_buf_alloc)); + /* TODO - Update this code to limit data bufferring by `stream->recv_buf_max` + and return `false` when we reach that limit. Then, when curl drains some + of the buffer, making room, call MsH3RequestSetReceiveEnabled to enable + receive callbacks again. */ + msh3_lock_acquire(&stream->recv_lock); + + if(!stream->recv_header_complete) { + if(data && data->set.verbose) + CF_DEBUGF(fprintf(stderr, "* [MSH3] req: Headers complete!\n")); + if(!msh3request_ensure_room(stream, 2)) { + stream->recv_error = CURLE_OUT_OF_MEMORY; + goto release_lock; + } + stream->recv_buf[stream->recv_header_len++] = '\r'; + stream->recv_buf[stream->recv_header_len++] = '\n'; + stream->recv_header_complete = true; + cur_recv_len += 2; + } + if(!msh3request_ensure_room(stream, *Length)) { + stream->recv_error = CURLE_OUT_OF_MEMORY; + goto release_lock; + } + memcpy(stream->recv_buf + cur_recv_len, Data, *Length); + stream->recv_data_len += (size_t)*Length; + data->state.drain = 1; + +release_lock: + msh3_lock_release(&stream->recv_lock); + return true; +} + +static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, + bool Aborted, uint64_t AbortError) +{ + struct Curl_easy *data = IfContext; + struct HTTP *stream = data->req.p.http; + + (void)Request; + (void)AbortError; + if(data && data->set.verbose) + CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: complete, aborted=%s\n", + Aborted ? "true" : "false")); + msh3_lock_acquire(&stream->recv_lock); + if(Aborted) { + stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */ + } + stream->recv_header_complete = true; + stream->recv_data_complete = true; + msh3_lock_release(&stream->recv_lock); +} + +static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, + void *IfContext) +{ + struct Curl_easy *data = IfContext; + struct HTTP *stream = data->req.p.http; + (void)Request; + (void)stream; +} + +static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, + void *IfContext, void *SendContext) +{ + struct Curl_easy *data = IfContext; + struct HTTP *stream = data->req.p.http; + (void)Request; + (void)stream; + (void)SendContext; +} + +static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + size_t outsize = 0; + + (void)cf; + DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len)); + + if(stream->recv_error) { + failf(data, "request aborted"); + data->state.drain = 0; + *err = stream->recv_error; + return -1; + } + + *err = CURLE_OK; + msh3_lock_acquire(&stream->recv_lock); + + if(stream->recv_header_len) { + outsize = len; + if(stream->recv_header_len < outsize) { + outsize = stream->recv_header_len; + } + memcpy(buf, stream->recv_buf, outsize); + if(outsize < stream->recv_header_len + stream->recv_data_len) { + memmove(stream->recv_buf, stream->recv_buf + outsize, + stream->recv_header_len + stream->recv_data_len - outsize); + } + stream->recv_header_len -= outsize; + DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of header", outsize)); + } + else if(stream->recv_data_len) { + outsize = len; + if(stream->recv_data_len < outsize) { + outsize = stream->recv_data_len; + } + memcpy(buf, stream->recv_buf, outsize); + if(outsize < stream->recv_data_len) { + memmove(stream->recv_buf, stream->recv_buf + outsize, + stream->recv_data_len - outsize); + } + stream->recv_data_len -= outsize; + DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of data", outsize)); + if(stream->recv_data_len == 0 && stream->recv_data_complete) + data->state.drain = 1; + } + else if(stream->recv_data_complete) { + DEBUGF(LOG_CF(data, cf, "req: receive complete")); + data->state.drain = 0; + } + else { + DEBUGF(LOG_CF(data, cf, "req: nothing here, call again")); + *err = CURLE_AGAIN; + outsize = -1; + } + + msh3_lock_release(&stream->recv_lock); + + return (ssize_t)outsize; +} + +static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + struct h2h3req *hreq; + size_t hdrlen = 0; + size_t sentlen = 0; + + /* Sizes must match for cast below to work" */ + DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo)); + DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len)); + + if(!stream->req) { + /* The first send on the request contains the headers and possibly some + data. Parse out the headers and create the request, then if there is + any data left over go ahead and send it too. */ + + *err = msh3_data_setup(cf, data); + if(*err) { + failf(data, "could not setup data"); + return -1; + } + + *err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq); + if(*err) { + failf(data, "Curl_pseudo_headers failed"); + return -1; + } + + DEBUGF(LOG_CF(data, cf, "req: send %zu headers", hreq->entries)); + stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, + (MSH3_HEADER*)hreq->header, hreq->entries, + hdrlen == len ? MSH3_REQUEST_FLAG_FIN : + MSH3_REQUEST_FLAG_NONE); + Curl_pseudo_free(hreq); + if(!stream->req) { + failf(data, "request open failed"); + *err = CURLE_SEND_ERROR; + return -1; + } + *err = CURLE_OK; + return len; + } + + DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len)); + if(len > 0xFFFFFFFF) { + /* msh3 doesn't support size_t sends currently. */ + *err = CURLE_SEND_ERROR; + return -1; + } + + /* TODO - Need an explicit signal to know when to FIN. */ + if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, (uint32_t)len, + stream)) { + *err = CURLE_SEND_ERROR; + return -1; + } + + /* TODO - msh3/msquic will hold onto this memory until the send complete + event. How do we make sure curl doesn't free it until then? */ + sentlen += len; + *err = CURLE_OK; + return sentlen; +} + +static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + int bitmap = GETSOCK_BLANK; + + if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { + socks[0] = ctx->sock[SP_LOCAL]; + + if(stream->recv_error) { + bitmap |= GETSOCK_READSOCK(0); + data->state.drain = 1; + } + else if(stream->recv_header_len || stream->recv_data_len) { + bitmap |= GETSOCK_READSOCK(0); + data->state.drain = 1; + } + } + DEBUGF(LOG_CF(data, cf, "select_sock %u -> %d", + (uint32_t)data->state.drain, bitmap)); + + return bitmap; +} + +static bool cf_msh3_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + + (void)cf; + DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %hhu", + (bool)(stream->recv_header_len || stream->recv_data_len))); + return stream->recv_header_len || stream->recv_data_len; +} + +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 cf_msh3_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_SETUP: + result = msh3_data_setup(cf, data); + break; + case CF_CTRL_DATA_DONE: + DEBUGF(LOG_CF(data, cf, "req: done")); + if(stream) { + if(stream->recv_buf) { + Curl_safefree(stream->recv_buf); + msh3_lock_uninitialize(&stream->recv_lock); + } + if(stream->req) { + MsH3RequestClose(stream->req); + stream->req = ZERO_NULL; + } + } + break; + case CF_CTRL_DATA_DONE_SEND: + DEBUGF(LOG_CF(data, cf, "req: send done")); + stream->upload_done = TRUE; + break; + case CF_CTRL_CONN_INFO_UPDATE: + DEBUGF(LOG_CF(data, cf, "req: update info")); + cf_msh3_active(cf, data); + break; + case CF_CTRL_CONN_REPORT_STATS: + if(cf->sockindex == FIRSTSOCKET) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + break; + + default: + break; + } + return result; +} + +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + bool verify = !!cf->conn->ssl_config.verifypeer; + MSH3_ADDR addr = {0}; + memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen); + MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); + ctx->verbose = (data && data->set.verbose); + + if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) { + /* TODO: need a way to provide trust anchors to MSH3 */ +#ifdef DEBUGBUILD + /* we need this for our test cases to run */ + DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " + "switching off verifypeer in DEBUG mode")); + verify = 0; +#else + DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " + "attempting with built-in verification")); +#endif + } + + DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)", + cf->conn->host.name, (int)cf->conn->remote_port, verify)); + + ctx->api = MsH3ApiOpen(); + if(!ctx->api) { + failf(data, "can't create msh3 api"); + return CURLE_FAILED_INIT; + } + + ctx->qconn = MsH3ConnectionOpen(ctx->api, + &msh3_conn_if, + ctx, + cf->conn->host.name, + &addr, + !verify); + if(!ctx->qconn) { + failf(data, "can't create msh3 connection"); + if(ctx->api) { + MsH3ApiClose(ctx->api); + ctx->api = NULL; + } + return CURLE_FAILED_INIT; + } + + return CURLE_OK; +} + +static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)blocking; + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) { + if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) { + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + return CURLE_COULDNT_CONNECT; + } + } + + *done = FALSE; + if(!ctx->qconn) { + ctx->connect_started = Curl_now(); + result = cf_connect_start(cf, data); + if(result) + goto out; + } + + if(ctx->handshake_complete) { + ctx->handshake_at = Curl_now(); + if(ctx->handshake_succeeded) { + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + Curl_pgrsTime(data, TIMER_APPCONNECT); + } + else { + failf(data, "failed to connect, handshake failed"); + result = CURLE_COULDNT_CONNECT; + } + } + +out: + return result; +} + +static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + + (void)data; + if(ctx) { + DEBUGF(LOG_CF(data, cf, "destroying")); + if(ctx->qconn) + MsH3ConnectionClose(ctx->qconn); + if(ctx->api) + MsH3ApiClose(ctx->api); + + if(ctx->active) { + /* We share our socket at cf->conn->sock[cf->sockindex] when active. + * If it is no longer there, someone has stolen (and hopefully + * closed it) and we just forget about it. + */ + if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { + DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active", + (int)ctx->sock[SP_LOCAL])); + cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; + } + else { + DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at " + "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL])); + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + } + if(cf->sockindex == FIRSTSOCKET) + cf->conn->remote_addr = NULL; + } + if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { + sclose(ctx->sock[SP_LOCAL]); + } + if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) { + sclose(ctx->sock[SP_REMOTE]); + } + memset(ctx, 0, sizeof(*ctx)); + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + } +} + +static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + cf_msh3_close(cf, data); + free(cf->ctx); + cf->ctx = NULL; +} + +static CURLcode cf_msh3_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + /* TODO: we do not have access to this so far, fake it */ + (void)ctx; + *pres1 = 100; + return CURLE_OK; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_msh3_ctx *ctx = cf->ctx; + + (void)data; + return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn && + ctx->connected; +} + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_msh3_destroy, + cf_msh3_connect, + cf_msh3_close, + Curl_cf_def_get_host, + cf_msh3_get_select_socks, + cf_msh3_data_pending, + cf_msh3_send, + cf_msh3_recv, + cf_msh3_data_event, + cf_msh3_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_msh3_query, +}; + +CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_msh3_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + (void)ai; /* TODO: msh3 resolves itself? */ + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + +out: + *pcf = (!result)? cf : NULL; + if(result) { + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +bool Curl_conn_is_msh3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +#endif /* USE_MSH3 */ diff --git a/Utilities/cmcurl/lib/vquic/msh3.h b/Utilities/cmcurl/lib/vquic/curl_msh3.h index ce884d9..33931f5 100644 --- a/Utilities/cmcurl/lib/vquic/msh3.h +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_MSH3_H -#define HEADER_CURL_VQUIC_MSH3_H +#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H +#define HEADER_CURL_VQUIC_CURL_MSH3_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -30,11 +30,17 @@ #include <msh3.h> -struct quicsocket { - MSH3_API* api; - MSH3_CONNECTION* conn; -}; +void Curl_msh3_ver(char *p, size_t len); + +CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_msh3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); #endif /* USE_MSQUIC */ -#endif /* HEADER_CURL_VQUIC_MSH3_H */ +#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */ diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c new file mode 100644 index 0000000..ffdaead --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c @@ -0,0 +1,2515 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_NGTCP2 +#include <ngtcp2/ngtcp2.h> +#include <nghttp3/nghttp3.h> + +#ifdef USE_OPENSSL +#include <openssl/err.h> +#ifdef OPENSSL_IS_BORINGSSL +#include <ngtcp2/ngtcp2_crypto_boringssl.h> +#else +#include <ngtcp2/ngtcp2_crypto_openssl.h> +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include <ngtcp2/ngtcp2_crypto_gnutls.h> +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include <ngtcp2/ngtcp2_crypto_wolfssl.h> +#include "vtls/wolfssl.h" +#endif + +#include "urldata.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "multiif.h" +#include "strcase.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "connect.h" +#include "progress.h" +#include "strerror.h" +#include "dynbuf.h" +#include "select.h" +#include "vquic.h" +#include "vquic_int.h" +#include "h2h3.h" +#include "vtls/keylog.h" +#include "vtls/vtls.h" +#include "curl_ngtcp2.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#define H3_ALPN_H3_29 "\x5h3-29" +#define H3_ALPN_H3 "\x2h3" + +/* + * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. + * It is used as a circular buffer. Add new bytes at the end until it reaches + * the far end, then start over at index 0 again. + */ + +#define H3_SEND_SIZE (256*1024) +struct h3out { + uint8_t buf[H3_SEND_SIZE]; + size_t used; /* number of bytes used in the buffer */ + size_t windex; /* index in the buffer where to start writing the next + data block */ +}; + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) +#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) + +#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) +#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 + + +/* + * Store ngtcp2 version info in this buffer. + */ +void Curl_ngtcp2_ver(char *p, size_t len) +{ + const ngtcp2_info *ng2 = ngtcp2_version(0); + const nghttp3_info *ht3 = nghttp3_version(0); + (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", + ng2->version_str, ht3->version_str); +} + +struct cf_ngtcp2_ctx { + struct cf_quic_ctx q; + ngtcp2_path connected_path; + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; + ngtcp2_transport_params transport_params; + ngtcp2_connection_close_error last_error; + ngtcp2_crypto_conn_ref conn_ref; +#ifdef USE_OPENSSL + SSL_CTX *sslctx; + SSL *ssl; +#elif defined(USE_GNUTLS) + struct gtls_instance *gtls; +#elif defined(USE_WOLFSSL) + WOLFSSL_CTX *sslctx; + WOLFSSL *ssl; +#endif + struct cf_call_data call_data; + nghttp3_conn *h3conn; + nghttp3_settings h3settings; + int qlogfd; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct curltime first_byte_at; /* when first byte was recvd */ + struct curltime reconnect_at; /* time the next attempt should start */ + BIT(got_first_byte); /* if first byte was received */ +}; + +/* How to access `call_data` from a cf_ngtcp2 filter */ +#define CF_CTX_CALL_DATA(cf) \ + ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data + + +/* ngtcp2 default congestion controller does not perform pacing. Limit + the maximum packet burst to MAX_PKT_BURST packets. */ +#define MAX_PKT_BURST 10 + +static CURLcode cf_process_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data); +static CURLcode cf_flush_egress(struct Curl_cfilter *cf, + struct Curl_easy *data); +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data); + +static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) +{ + struct Curl_cfilter *cf = conn_ref->user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + return ctx->qconn; +} + +static ngtcp2_tstamp timestamp(void) +{ + struct curltime ct = Curl_now(); + return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; +} + +#ifdef DEBUG_NGTCP2 +static void quic_printf(void *user_data, const char *fmt, ...) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + (void)ctx; /* TODO: need an easy handle to infof() message */ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} +#endif + +static void qlog_callback(void *user_data, uint32_t flags, + const void *data, size_t datalen) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + (void)flags; + if(ctx->qlogfd != -1) { + ssize_t rc = write(ctx->qlogfd, data, datalen); + if(rc == -1) { + /* on write error, stop further write attempts */ + close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } + +} + +static void quic_settings(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data) +{ + ngtcp2_settings *s = &ctx->settings; + ngtcp2_transport_params *t = &ctx->transport_params; + size_t stream_win_size = CURL_MAX_READ_SIZE; + + ngtcp2_settings_default(s); + ngtcp2_transport_params_default(t); +#ifdef DEBUG_NGTCP2 + s->log_printf = quic_printf; +#else + s->log_printf = NULL; +#endif + + (void)data; + s->initial_ts = timestamp(); + s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; + s->max_window = 100 * stream_win_size; + s->max_stream_window = stream_win_size; + + t->initial_max_data = 10 * stream_win_size; + t->initial_max_stream_data_bidi_local = stream_win_size; + t->initial_max_stream_data_bidi_remote = stream_win_size; + t->initial_max_stream_data_uni = stream_win_size; + t->initial_max_streams_bidi = QUIC_MAX_STREAMS; + t->initial_max_streams_uni = QUIC_MAX_STREAMS; + t->max_idle_timeout = QUIC_IDLE_TIMEOUT; + if(ctx->qlogfd != -1) { + s->qlog.write = qlog_callback; + } +} + +#ifdef USE_OPENSSL +static void keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} +#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; +} +#elif defined(USE_WOLFSSL) +#if defined(HAVE_SECRET_CALLBACK) +static void keylog_callback(const WOLFSSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} +#endif +#endif + +static int init_ngh3_conn(struct Curl_cfilter *cf); + +#ifdef USE_OPENSSL +static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, + struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + CURLcode result = CURLE_FAILED_INIT; + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + + if(!ssl_ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + +#ifdef OPENSSL_IS_BORINGSSL + if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + goto out; + } +#else + if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_openssl_configure_client_context failed"); + goto out; + } +#endif + + SSL_CTX_set_default_verify_paths(ssl_ctx); + +#ifdef OPENSSL_IS_BORINGSSL + if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_curves_list failed"); + goto out; + } +#else + if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { + char error_buffer[256]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + goto out; + } + + if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_groups_list failed"); + goto out; + } +#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(ssl_ctx, keylog_callback); + } + + result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); + if(result) + goto out; + + /* OpenSSL always tries to verify the peer, this only says whether it should + * fail to connect if the verification fails, or if it should continue + * anyway. In the latter case the result of the verification is checked with + * SSL_get_verify_result() below. */ + SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ? + SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + Curl_set_in_callback(data, true); + result = (*data->set.ssl.fsslctx)(data, 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: + *pssl_ctx = result? NULL : ssl_ctx; + if(result && ssl_ctx) + SSL_CTX_free(ssl_ctx); + return result; +} + +static CURLcode quic_set_client_cert(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + SSL_CTX *ssl_ctx = ctx->sslctx; + const struct ssl_config_data *ssl_config; + + ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET); + 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 quic_init_ssl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const uint8_t *alpn = NULL; + size_t alpnlen = 0; + + (void)data; + DEBUGASSERT(!ctx->ssl); + ctx->ssl = SSL_new(ctx->sslctx); + + SSL_set_app_data(ctx->ssl, &ctx->conn_ref); + SSL_set_connect_state(ctx->ssl); + SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); + + alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; + alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; + if(alpn) + SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); + + /* set SNI */ + SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); + return CURLE_OK; +} +#elif defined(USE_GNUTLS) +static CURLcode quic_init_ssl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result; + gnutls_datum_t alpn[2]; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = cf->conn->host.name; + long * const pverifyresult = &data->set.ssl.certverifyresult; + int rc; + + DEBUGASSERT(ctx->gtls == NULL); + ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); + if(!ctx->gtls) + return CURLE_OUT_OF_MEMORY; + + result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl, + hostname, ctx->gtls, pverifyresult); + if(result) + return result; + + gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref); + + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { + DEBUGF(LOG_CF(data, cf, + "ngtcp2_crypto_gnutls_configure_client_session failed\n")); + return CURLE_QUIC_CONNECT_ERROR; + } + + rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); + if(rc < 0) { + DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", + gnutls_strerror(rc))); + 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); + } + + /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ + alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; + alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; + alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; + alpn[1].size = sizeof(H3_ALPN_H3) - 2; + + gnutls_alpn_set_protocols(ctx->gtls->session, + alpn, 2, GNUTLS_ALPN_MANDATORY); + return CURLE_OK; +} +#elif defined(USE_WOLFSSL) + +static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, + struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + CURLcode result = CURLE_FAILED_INIT; + WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + + if(!ssl_ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + goto out; + } + + wolfSSL_CTX_set_default_verify_paths(ssl_ctx); + + if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { + char error_buffer[256]; + ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); + failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + goto out; + } + + if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) { + failf(data, "SSL_CTX_set1_groups_list failed"); + goto out; + } + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { +#if defined(HAVE_SECRET_CALLBACK) + wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); +#else + failf(data, "wolfSSL was built without keylog callback"); + goto out; +#endif + } + + if(conn->ssl_config.verifypeer) { + const char * const ssl_cafile = conn->ssl_config.CAfile; + const char * const ssl_capath = conn->ssl_config.CApath; + + wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + if(conn->ssl_config.CAfile || conn->ssl_config.CApath) { + /* tell wolfSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + goto out; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* verifying the peer without any CA certificates won't work so + use wolfssl's built-in default as fallback */ + wolfSSL_CTX_set_default_verify_paths(ssl_ctx); + } +#endif + } + else { + wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); + } + + /* give application a chance to interfere with SSL set up. */ + if(data->set.ssl.fsslctx) { + Curl_set_in_callback(data, true); + result = (*data->set.ssl.fsslctx)(data, 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: + *pssl_ctx = result? NULL : ssl_ctx; + if(result && ssl_ctx) + SSL_CTX_free(ssl_ctx); + return result; +} + +/** SSL callbacks ***/ + +static CURLcode quic_init_ssl(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const uint8_t *alpn = NULL; + size_t alpnlen = 0; + /* this will need some attention when HTTPS proxy over QUIC get fixed */ + const char * const hostname = cf->conn->host.name; + + (void)data; + DEBUGASSERT(!ctx->ssl); + ctx->ssl = wolfSSL_new(ctx->sslctx); + + wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref); + wolfSSL_set_connect_state(ctx->ssl); + wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); + + alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; + alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; + if(alpn) + wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); + + /* set SNI */ + wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME, + hostname, (unsigned short)strlen(hostname)); + + return CURLE_OK; +} +#endif /* defined(USE_WOLFSSL) */ + +static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) +{ + (void)user_data; + (void)tconn; + return 0; +} + +static void report_consumed_data(struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t consumed) +{ + struct HTTP *stream = data->req.p.http; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + /* 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; + } + else { + stream->recv_buf_nonflow -= consumed; + consumed = 0; + } + } + if(consumed > 0) { + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes", + stream->stream3_id, consumed)); + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->stream3_id, + consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + } + if(!stream->closed && data->state.drain + && !stream->memlen + && !Curl_dyn_len(&stream->overflow)) { + /* nothing buffered any more */ + data->state.drain = 0; + } +} + +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, 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; + nghttp3_ssize nconsumed; + int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; + struct Curl_easy *data = stream_user_data; + (void)offset; + (void)data; + + nconsumed = + nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd", + stream_id, buflen, nconsumed)); + if(nconsumed < 0) { + ngtcp2_connection_close_error_set_application_error( + &ctx->last_error, + nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* number of bytes inside buflen which consists of framing overhead + * including QPACK HEADERS. In other words, it does not consume payload of + * DATA frame. */ + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); + ngtcp2_conn_extend_max_offset(tconn, nconsumed); + + return 0; +} + +static int +cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t offset, uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)stream_id; + (void)tconn; + (void)offset; + (void)datalen; + (void)stream_user_data; + + rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream3_id, 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; + int rv; + + (void)tconn; + (void)data; + /* stream is closed... */ + + 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); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%" + PRIu64 ") -> %d", stream3_id, app_error_code, rv)); + if(rv) { + ngtcp2_connection_close_error_set_application_error( + &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, + 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; + struct Curl_easy *data = stream_user_data; + int rv; + (void)tconn; + (void)final_size; + (void)app_error_code; + (void)data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, + 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; + int rv; + (void)tconn; + (void)app_error_code; + (void)stream_user_data; + + rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, + uint64_t max_streams, + void *user_data) +{ + (void)tconn; + (void)max_streams; + (void)user_data; + + return 0; +} + +static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)tconn; + (void)max_data; + (void)stream_user_data; + + rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static void cb_rand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) +{ + CURLcode result; + (void)rand_ctx; + + result = Curl_rand(NULL, dest, destlen); + if(result) { + /* cb_rand is only used for non-cryptographic context. If Curl_rand + failed, just fill 0 and call it *random*. */ + memset(dest, 0, destlen); + } +} + +static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} + +static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level, + void *user_data) +{ + struct Curl_cfilter *cf = user_data; + (void)tconn; + + if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) { + return 0; + } + + if(init_ngh3_conn(cf) != CURLE_OK) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static ngtcp2_callbacks ng_callbacks = { + ngtcp2_crypto_client_initial_cb, + NULL, /* recv_client_initial */ + ngtcp2_crypto_recv_crypto_data_cb, + cb_handshake_completed, + NULL, /* recv_version_negotiation */ + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + cb_recv_stream_data, + cb_acked_stream_data_offset, + NULL, /* stream_open */ + cb_stream_close, + NULL, /* recv_stateless_reset */ + ngtcp2_crypto_recv_retry_cb, + cb_extend_max_local_streams_bidi, + NULL, /* extend_max_local_streams_uni */ + cb_rand, + cb_get_new_connection_id, + NULL, /* remove_connection_id */ + ngtcp2_crypto_update_key_cb, /* update_key */ + NULL, /* path_validation */ + NULL, /* select_preferred_addr */ + cb_stream_reset, + NULL, /* extend_max_remote_streams_bidi */ + NULL, /* extend_max_remote_streams_uni */ + cb_extend_max_stream_data, + NULL, /* dcid_status */ + NULL, /* handshake_confirmed */ + NULL, /* recv_new_token */ + ngtcp2_crypto_delete_crypto_aead_ctx_cb, + ngtcp2_crypto_delete_crypto_cipher_ctx_cb, + NULL, /* recv_datagram */ + NULL, /* ack_datagram */ + NULL, /* lost_datagram */ + ngtcp2_crypto_get_path_challenge_data_cb, + cb_stream_stop_sending, + NULL, /* version_negotiation */ + cb_recv_rx_key, + NULL, /* recv_tx_key */ + NULL, /* early_data_rejected */ +}; + +static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct SingleRequest *k = &data->req; + int rv = GETSOCK_BLANK; + struct HTTP *stream = data->req.p.http; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + socks[0] = ctx->q.sockfd; + + /* in an HTTP/3 connection we can basically always get a frame so we should + always be ready for one */ + rv |= GETSOCK_READSOCK(0); + + /* we're still uploading or the HTTP/2 layer wants to send data */ + if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND && + (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) && + ngtcp2_conn_get_cwnd_left(ctx->qconn) && + ngtcp2_conn_get_max_data_left(ctx->qconn) && + nghttp3_conn_is_stream_writable(ctx->h3conn, stream->stream3_id)) + rv |= GETSOCK_WRITESOCK(0); + + DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)", + rv, (int)socks[0])); + CF_DATA_RESTORE(cf, save); + return rv; +} + +static void notify_drain(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)cf; + if(!data->state.drain) { + data->state.drain = 1; + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } +} + + +static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + (void)conn; + (void)stream_id; + (void)app_error_code; + (void)cf; + + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRIx64 ")", + stream_id, app_error_code)); + stream->closed = TRUE; + stream->error3 = app_error_code; + if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) { + /* TODO: we do not get a specific error when the remote end closed + * the response before it was complete. */ + stream->reset = TRUE; + } + notify_drain(cf, data); + return 0; +} + +/* + * write_resp_raw() copies resonse data in raw format to the `data`'s + * receive buffer. If not enough space is available, it appends to the + * `data`'s overflow buffer. + */ +static CURLcode write_resp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, size_t memlen, + bool flow) +{ + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + const char *buf = mem; + size_t ncopy = memlen; + /* copy as much as possible to the receive buffer */ + if(stream->len) { + size_t len = CURLMIN(ncopy, stream->len); + memcpy(stream->mem + stream->memlen, buf, len); + stream->len -= len; + stream->memlen += len; + buf += len; + ncopy -= len; + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes" + " to data buffer", stream->stream3_id, len)); + } + /* copy the rest to the overflow buffer */ + if(ncopy) { + result = Curl_dyn_addn(&stream->overflow, buf, ncopy); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes" + " to overflow buffer -> %d", + stream->stream3_id, ncopy, result)); + notify_drain(cf, data); + } + + if(!flow) + stream->recv_buf_nonflow += memlen; + if(CF_DATA_CURRENT(cf) != data) { + notify_drain(cf, data); + } + return result; +} + +static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct Curl_easy *data = stream_user_data; + CURLcode result; + + (void)conn; + (void)stream3_id; + + result = write_resp_raw(cf, data, buf, buflen, TRUE); + return result? -1 : 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, + size_t consumed, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + (void)conn; + (void)stream_user_data; + + /* nghttp3 has consumed bytes on the QUIC stream and we need to + * tell the QUIC connection to increase its flow control */ + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + return 0; +} + +/* Decode HTTP status code. Returns -1 if no valid status code was + decoded. (duplicate from http2.c) */ +static int decode_status_code(const uint8_t *value, size_t len) +{ + int i; + int res; + + if(len != 3) { + return -1; + } + + res = 0; + + for(i = 0; i < 3; ++i) { + char c = value[i]; + + if(c < '0' || c > '9') { + return -1; + } + + res *= 10; + res += c - '0'; + } + + return res; +} + +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, + int fin, void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)fin; + (void)cf; + + /* add a CRLF only if we've received some headers */ + if(stream->firstheader) { + result = write_resp_raw(cf, data, "\r\n", 2, FALSE); + if(result) { + return -1; + } + } + + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d", + stream_id, stream->status_code)); + if(stream->status_code / 100 != 1) { + stream->bodystarted = TRUE; + } + return 0; +} + +static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *user_data, void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)cf; + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + char line[14]; /* status line is always 13 characters long */ + size_t ncopy; + + DEBUGASSERT(!stream->firstheader); + stream->status_code = decode_status_code(h3val.base, h3val.len); + DEBUGASSERT(stream->status_code != -1); + ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", + stream->status_code); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s", + stream_id, line)); + result = write_resp_raw(cf, data, line, ncopy, FALSE); + if(result) { + return -1; + } + stream->firstheader = TRUE; + } + else { + /* store as an HTTP1-style header */ + DEBUGASSERT(stream->firstheader); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s", + stream_id, (int)h3name.len, h3name.base, + (int)h3val.len, h3val.base)); + result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); + if(result) { + return -1; + } + result = write_resp_raw(cf, data, ": ", 2, FALSE); + if(result) { + return -1; + } + result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); + if(result) { + return -1; + } + result = write_resp_raw(cf, data, "\r\n", 2, FALSE); + if(result) { + return -1; + } + } + return 0; +} + +static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)conn; + (void)stream_user_data; + + rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, stream_id, app_error_code); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + int rv; + (void)conn; + (void)data; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id, + app_error_code); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static nghttp3_callbacks ngh3_callbacks = { + cb_h3_acked_stream_data, /* acked_stream_data */ + cb_h3_stream_close, + cb_h3_recv_data, + cb_h3_deferred_consume, + NULL, /* begin_headers */ + cb_h3_recv_header, + cb_h3_end_headers, + NULL, /* begin_trailers */ + cb_h3_recv_header, + NULL, /* end_trailers */ + cb_h3_stop_sending, + NULL, /* end_stream */ + cb_h3_reset_stream, + NULL /* shutdown */ +}; + +static int init_ngh3_conn(struct Curl_cfilter *cf) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result; + int rc; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + + if(ngtcp2_conn_get_max_local_streams_uni(ctx->qconn) < 3) { + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_callbacks, + &ctx->h3settings, + nghttp3_mem_default(), + cf); + if(rc) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + result = CURLE_QUIC_CONNECT_ERROR; + goto fail; + } + + return CURLE_OK; + fail: + + return result; +} + +static void drain_overflow_buffer(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + size_t overlen = Curl_dyn_len(&stream->overflow); + size_t ncopy = CURLMIN(overlen, stream->len); + + (void)cf; + if(ncopy > 0) { + memcpy(stream->mem + stream->memlen, + Curl_dyn_ptr(&stream->overflow), ncopy); + stream->len -= ncopy; + stream->memlen += ncopy; + if(ncopy != overlen) + /* make the buffer only keep the tail */ + (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy); + else { + Curl_dyn_reset(&stream->overflow); + } + } +} + +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + ssize_t nread = -1; + + (void)cf; + + if(stream->reset) { + failf(data, + "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id); + *err = CURLE_PARTIAL_FILE; + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", + stream->stream3_id, *err)); + goto out; + } + else if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + failf(data, + "HTTP/3 stream %" PRId64 " was not closed cleanly: (err 0x%" PRIx64 + ")", + stream->stream3_id, stream->error3); + *err = CURLE_HTTP3; + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly" + " -> %d", stream->stream3_id, *err)); + goto out; + } + + if(!stream->bodystarted) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" + " all response header fields, treated as error", + stream->stream3_id); + *err = CURLE_HTTP3; + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" + " -> %d", stream->stream3_id, *err)); + goto out; + } + else { + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" + " -> %d", stream->stream3_id, *err)); + } + *err = CURLE_OK; + nread = 0; + +out: + data->state.drain = 0; + return nread; +} + +/* 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) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + ssize_t nread = -1; + struct cf_call_data save; + + (void)ctx; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *err = CURLE_OK; + + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) start", + stream->stream3_id, len)); + /* TODO: this implementation of response DATA buffering is fragile. + * It makes the following assumptions: + * - the `buf` passed here has the same lifetime as the easy handle + * - data returned in `buf` from this call is immediately used and `buf` + * can be overwritten during any handling of other transfers at + * this connection. + */ + if(!stream->memlen) { + /* `buf` was not known before or is currently not used by stream, + * assign it (again). */ + stream->mem = buf; + stream->len = len; + } + + /* if there's data in the overflow buffer, move as much + as possible to the receive buffer now */ + drain_overflow_buffer(cf, data); + + if(cf_process_ingress(cf, data)) { + *err = CURLE_RECV_ERROR; + nread = -1; + goto out; + } + + if(stream->memlen) { + nread = stream->memlen; + /* reset to allow more data to come */ + /* TODO: very brittle buffer use design: + * - stream->mem has now `nread` bytes of response data + * - we assume that the caller will use those immediately and + * we can overwrite that with new data on our next invocation from + * anywhere. + */ + stream->mem = buf; + stream->memlen = 0; + stream->len = len; + /* extend the stream window with the data we're consuming and send out + any additional packets to tell the server that we can receive more */ + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> %zd bytes", + stream->stream3_id, nread)); + report_consumed_data(cf, data, nread); + if(cf_flush_egress(cf, data)) { + *err = CURLE_SEND_ERROR; + nread = -1; + } + goto out; + } + + if(stream->closed) { + nread = recv_closed_stream(cf, data, err); + goto out; + } + + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> EAGAIN", + stream->stream3_id)); + *err = CURLE_AGAIN; + nread = -1; +out: + if(cf_flush_egress(cf, data)) { + *err = CURLE_SEND_ERROR; + nread = -1; + goto out; + } + + CF_DATA_RESTORE(cf, save); + return nread; +} + +/* this amount of data has now been acked on this stream */ +static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.p.http; + (void)user_data; + + (void)cf; + if(!data->set.postfields) { + stream->h3out->used -= datalen; + DEBUGF(LOG_CF(data, cf, "cb_h3_acked_stream_data, %"PRIu64" bytes, " + "%zd left unacked", datalen, stream->h3out->used)); + DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); + + if(stream->h3out->used == 0) { + int rv = nghttp3_conn_resume_stream(conn, stream_id); + if(rv) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + } + return 0; +} + +static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, + nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct Curl_easy *data = stream_user_data; + size_t nread; + struct HTTP *stream = data->req.p.http; + (void)cf; + (void)conn; + (void)stream_id; + (void)user_data; + (void)veccnt; + + if(data->set.postfields) { + vec[0].base = data->set.postfields; + vec[0].len = data->state.infilesize; + *pflags = NGHTTP3_DATA_FLAG_EOF; + return 1; + } + + if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) { + return NGHTTP3_ERR_WOULDBLOCK; + } + + nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); + if(nread > 0) { + /* nghttp3 wants us to hold on to the data until it tells us it is okay to + delete it. Append the data at the end of the h3out buffer. Since we can + only return consecutive data, copy the amount that fits and the next + part comes in next invoke. */ + struct h3out *out = stream->h3out; + if(nread + out->windex > H3_SEND_SIZE) + nread = H3_SEND_SIZE - out->windex; + + memcpy(&out->buf[out->windex], stream->upload_mem, nread); + + /* that's the chunk we return to nghttp3 */ + vec[0].base = &out->buf[out->windex]; + vec[0].len = nread; + + out->windex += nread; + out->used += nread; + + if(out->windex == H3_SEND_SIZE) + out->windex = 0; /* wrap */ + stream->upload_mem += nread; + stream->upload_len -= nread; + if(data->state.infilesize != -1) { + stream->upload_left -= nread; + if(!stream->upload_left) + *pflags = NGHTTP3_DATA_FLAG_EOF; + } + DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction %zd bytes%s (at %zd unacked)", + nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + out->used)); + } + if(stream->upload_done && !stream->upload_len && + (stream->upload_left <= 0)) { + DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction sets EOF")); + *pflags = NGHTTP3_DATA_FLAG_EOF; + return nread ? 1 : 0; + } + else if(!nread) { + return NGHTTP3_ERR_WOULDBLOCK; + } + return 1; +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static CURLcode h3_stream_open(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + size_t nheader; + CURLcode result = CURLE_OK; + nghttp3_nv *nva = NULL; + int64_t stream3_id; + int rc = 0; + struct h3out *h3out = NULL; + struct h2h3req *hreq = NULL; + + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream3_id, NULL); + if(rc) { + failf(data, "can get bidi streams"); + goto fail; + } + + stream->stream3_id = stream3_id; + stream->h3req = TRUE; + Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE); + stream->recv_buf_nonflow = 0; + + result = Curl_pseudo_headers(data, mem, len, NULL, &hreq); + if(result) + goto fail; + nheader = hreq->entries; + + nva = malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].namelen = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].valuelen = hreq->header[i].valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: { + nghttp3_data_reader data_reader; + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown, but not zero */ + + data_reader.read_data = cb_h3_readfunction; + + h3out = calloc(sizeof(struct h3out), 1); + if(!h3out) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + stream->h3out = h3out; + + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id, + nva, nheader, &data_reader, data); + if(rc) + goto fail; + break; + } + default: + stream->upload_left = 0; /* nothing left to send */ + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id, + nva, nheader, NULL, data); + if(rc) + goto fail; + break; + } + + Curl_safefree(nva); + + infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)", + stream3_id, (void *)data); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", + stream3_id, data->state.url)); + + Curl_pseudo_free(hreq); + return CURLE_OK; + +fail: + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, " + "connection is closing", stream->stream3_id)); + result = CURLE_RECV_ERROR; + break; + default: + DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", + stream->stream3_id, rc, ngtcp2_strerror(rc))); + result = CURLE_SEND_ERROR; + break; + } + } + free(nva); + Curl_pseudo_free(hreq); + return result; +} + +static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + ssize_t sent = 0; + struct HTTP *stream = data->req.p.http; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *err = CURLE_OK; + + if(stream->closed) { + *err = CURLE_HTTP3; + sent = -1; + goto out; + } + + if(!stream->h3req) { + CURLcode result = h3_stream_open(cf, data, buf, len); + if(result) { + DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", result)); + sent = -1; + goto out; + } + /* Assume that mem of length len only includes HTTP/1.1 style + header fields. In other words, it does not contain request + body. */ + sent = len; + } + else { + DEBUGF(LOG_CF(data, cf, "ngh3_stream_send() wants to send %zd bytes", + len)); + if(!stream->upload_len) { + stream->upload_mem = buf; + stream->upload_len = len; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id); + } + else { + *err = CURLE_AGAIN; + sent = -1; + goto out; + } + } + + if(cf_flush_egress(cf, data)) { + *err = CURLE_SEND_ERROR; + sent = -1; + goto out; + } + + /* Reset post upload buffer after resumed. */ + if(stream->upload_mem) { + if(data->set.postfields) { + sent = len; + } + else { + sent = len - stream->upload_len; + } + + stream->upload_mem = NULL; + stream->upload_len = 0; + + if(sent == 0) { + *err = CURLE_AGAIN; + sent = -1; + goto out; + } + } +out: + CF_DATA_RESTORE(cf, save); + return sent; +} + +static CURLcode qng_verify_peer(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + const char *hostname, *disp_hostname; + int port; + char *snihost; + + Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port); + snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost) + return CURLE_PEER_FAILED_VERIFICATION; + + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + if(cf->conn->ssl_config.verifyhost) { +#ifdef USE_OPENSSL + X509 *server_cert; + server_cert = SSL_get_peer_certificate(ctx->ssl); + if(!server_cert) { + return CURLE_PEER_FAILED_VERIFICATION; + } + result = Curl_ossl_verifyhost(data, cf->conn, server_cert); + X509_free(server_cert); + if(result) + return result; +#elif defined(USE_GNUTLS) + result = Curl_gtls_verifyserver(data, ctx->gtls->session, + &cf->conn->ssl_config, &data->set.ssl, + hostname, disp_hostname, + data->set.str[STRING_SSL_PINNEDPUBLICKEY]); + if(result) + return result; +#elif defined(USE_WOLFSSL) + if(wolfSSL_check_domain_name(ctx->ssl, snihost) == 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); +#endif + return result; +} + +static CURLcode cf_process_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + ssize_t recvd; + int rv; + uint8_t buf[65536]; + size_t bufsize = sizeof(buf); + size_t pktcount = 0, total_recvd = 0; + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + ngtcp2_path path; + ngtcp2_tstamp ts = timestamp(); + ngtcp2_pkt_info pi = { 0 }; + + for(;;) { + remote_addrlen = sizeof(remote_addr); + while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + SOCKERRNO == EINTR) + ; + if(recvd == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN")); + goto out; + } + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + const char *r_ip; + int r_port; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + failf(data, "ngtcp2: connection to %s port %u refused", + r_ip, r_port); + return CURLE_COULDNT_CONNECT; + } + failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd (errno=%d)", + recvd, SOCKERRNO); + return CURLE_RECV_ERROR; + } + + if(recvd > 0 && !ctx->got_first_byte) { + ctx->first_byte_at = Curl_now(); + ctx->got_first_byte = TRUE; + } + + ++pktcount; + total_recvd += recvd; + + ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr, + remote_addrlen); + + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, buf, recvd, ts); + if(rv) { + DEBUGF(LOG_CF(data, cf, "ingress, read_pkt -> %s", + ngtcp2_strerror(rv))); + if(!ctx->last_error.error_code) { + if(rv == NGTCP2_ERR_CRYPTO) { + ngtcp2_connection_close_error_set_transport_error_tls_alert( + &ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), NULL, 0); + } + else { + ngtcp2_connection_close_error_set_transport_error_liberr( + &ctx->last_error, rv, NULL, 0); + } + } + + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } + } + +out: + (void)pktcount; + (void)total_recvd; + DEBUGF(LOG_CF(data, cf, "ingress, recvd %zu packets with %zd bytes", + pktcount, total_recvd)); + return CURLE_OK; +} + +static CURLcode cf_flush_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + size_t sent; + ngtcp2_ssize outlen; + uint8_t *outpos = ctx->q.pktbuf; + size_t max_udp_payload_size = + ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); + size_t path_max_udp_payload_size = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + size_t max_pktcnt = + CURLMIN(MAX_PKT_BURST, ctx->q.pktbuflen / max_udp_payload_size); + size_t pktcnt = 0; + size_t gsolen = 0; /* this disables gso until we have a clue */ + ngtcp2_path_storage ps; + ngtcp2_tstamp ts = timestamp(); + ngtcp2_tstamp expiry; + ngtcp2_duration timeout; + int64_t stream_id; + nghttp3_ssize veccnt; + int fin; + nghttp3_vec vec[16]; + ngtcp2_ssize ndatalen; + uint32_t flags; + CURLcode curlcode; + + rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + ngtcp2_connection_close_error_set_transport_error_liberr(&ctx->last_error, + rv, NULL, 0); + return CURLE_SEND_ERROR; + } + + if(ctx->q.num_blocked_pkt) { + curlcode = vquic_send_blocked_pkt(cf, data, &ctx->q); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + } + + ngtcp2_path_storage_zero(&ps); + + for(;;) { + veccnt = 0; + stream_id = -1; + fin = 0; + + if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { + veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, + sizeof(vec) / sizeof(vec[0])); + if(veccnt < 0) { + failf(data, "nghttp3_conn_writev_stream returned error: %s", + nghttp3_strerror((int)veccnt)); + ngtcp2_connection_close_error_set_application_error( + &ctx->last_error, + nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); + return CURLE_SEND_ERROR; + } + } + + flags = NGTCP2_WRITE_STREAM_FLAG_MORE | + (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); + outlen = ngtcp2_conn_writev_stream(ctx->qconn, &ps.path, NULL, outpos, + max_udp_payload_size, + &ndatalen, flags, stream_id, + (const ngtcp2_vec *)vec, veccnt, ts); + if(outlen == 0) { + /* ngtcp2 does not want to send more packets, if the buffer is + * not empty, send that now */ + if(outpos != ctx->q.pktbuf) { + curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf, + outpos - ctx->q.pktbuf, gsolen, &sent); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent, + outpos - ctx->q.pktbuf - sent, + gsolen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + } + /* done for now */ + goto out; + } + if(outlen < 0) { + switch(outlen) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + assert(ndatalen == -1); + nghttp3_conn_block_stream(ctx->h3conn, stream_id); + continue; + case NGTCP2_ERR_STREAM_SHUT_WR: + assert(ndatalen == -1); + nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); + continue; + case NGTCP2_ERR_WRITE_MORE: + /* ngtcp2 wants to send more. update the flow of the stream whose data + * is in the buffer and continue */ + assert(ndatalen >= 0); + rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); + if(rv) { + failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + continue; + default: + assert(ndatalen == -1); + failf(data, "ngtcp2_conn_writev_stream returned error: %s", + ngtcp2_strerror((int)outlen)); + ngtcp2_connection_close_error_set_transport_error_liberr( + &ctx->last_error, (int)outlen, NULL, 0); + return CURLE_SEND_ERROR; + } + } + else if(ndatalen >= 0) { + /* ngtcp2 thinks it has added all it wants. Update the stream */ + rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); + if(rv) { + failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", + nghttp3_strerror(rv)); + return CURLE_SEND_ERROR; + } + } + + /* advance to the end of the buffered packet data */ + outpos += outlen; + + if(pktcnt == 0) { + /* first packet buffer chunk. use this as gsolen. It's how ngtcp2 + * indicates the intended segment size. */ + gsolen = outlen; + } + else if((size_t)outlen > gsolen || + (gsolen > path_max_udp_payload_size && (size_t)outlen != gsolen)) { + /* Packet larger than path_max_udp_payload_size is PMTUD probe + packet and it might not be sent because of EMSGSIZE. Send + them separately to minimize the loss. */ + /* send the pktbuf *before* the last addition */ + curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf, + outpos - outlen - ctx->q.pktbuf, gsolen, &sent); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + /* blocked, add the pktbuf *before* and *at* the last addition + * separately to the blocked packages */ + vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent, + outpos - outlen - ctx->q.pktbuf - sent, gsolen); + vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + /* send the pktbuf *at* the last addition */ + curlcode = vquic_send_packet(cf, data, &ctx->q, outpos - outlen, outlen, + outlen, &sent); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + assert(0 == sent); + vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + /* pktbuf has been completely sent */ + pktcnt = 0; + outpos = ctx->q.pktbuf; + continue; + } + + if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) { + /* enough packets or last one is shorter than the intended + * segment size, indicating that it is time to send. */ + curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf, + outpos - ctx->q.pktbuf, gsolen, &sent); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent, + outpos - ctx->q.pktbuf - sent, gsolen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + /* pktbuf has been completely sent */ + pktcnt = 0; + outpos = ctx->q.pktbuf; + } + } + +out: + /* non-errored exit. check when we should run again. */ + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= ts) { + timeout = 0; + } + else { + timeout = expiry - ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + } + Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); + } + + return CURLE_OK; +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + /* We may have received more data than we're able to hold in the receive + buffer and allocated an overflow buffer. Since it's possible that + there's no more data coming on the socket, we need to keep reading + until the overflow buffer is empty. */ + const struct HTTP *stream = data->req.p.http; + (void)cf; + return Curl_dyn_len(&stream->overflow) > 0; +} + +static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_DONE: { + struct HTTP *stream = data->req.p.http; + Curl_dyn_free(&stream->overflow); + free(stream->h3out); + break; + } + case CF_CTRL_DATA_DONE_SEND: { + struct HTTP *stream = data->req.p.http; + stream->upload_done = TRUE; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id); + break; + } + case CF_CTRL_DATA_IDLE: + if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) { + if(cf_flush_egress(cf, data)) { + result = CURLE_SEND_ERROR; + } + } + break; + case CF_CTRL_CONN_REPORT_STATS: + if(cf->sockindex == FIRSTSOCKET) { + if(ctx->got_first_byte) + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at); + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + } + break; + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(ctx->qlogfd != -1) { + close(ctx->qlogfd); + ctx->qlogfd = -1; + } +#ifdef USE_OPENSSL + if(ctx->ssl) + SSL_free(ctx->ssl); + if(ctx->sslctx) + SSL_CTX_free(ctx->sslctx); +#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); + } +#elif defined(USE_WOLFSSL) + if(ctx->ssl) + wolfSSL_free(ctx->ssl); + if(ctx->sslctx) + wolfSSL_CTX_free(ctx->sslctx); +#endif + vquic_ctx_free(&ctx->q); + if(ctx->h3conn) + nghttp3_conn_del(ctx->h3conn); + if(ctx->qconn) + ngtcp2_conn_del(ctx->qconn); + + memset(ctx, 0, sizeof(*ctx)); + ctx->call_data = save; +} + +static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + if(ctx && ctx->qconn) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_tstamp ts; + ngtcp2_ssize rc; + + DEBUGF(LOG_CF(data, cf, "close")); + ts = timestamp(); + rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, ts); + if(rc > 0) { + while((send(ctx->q.sockfd, buffer, rc, 0) == -1) && + SOCKERRNO == EINTR); + } + + cf_ngtcp2_ctx_clear(ctx); + } + + cf->connected = FALSE; + CF_DATA_RESTORE(cf, save); +} + +static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + DEBUGF(LOG_CF(data, cf, "destroy")); + if(ctx) { + cf_ngtcp2_ctx_clear(ctx); + free(ctx); + } + cf->ctx = NULL; + /* No CF_DATA_RESTORE(cf, save) possible */ +} + +/* + * Might be called twice for happy eyeballs. + */ +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rc; + int rv; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr; + int qfd; + + ctx->version = NGTCP2_PROTO_VER_MAX; +#ifdef USE_OPENSSL + result = quic_ssl_ctx(&ctx->sslctx, cf, data); + if(result) + return result; + + result = quic_set_client_cert(cf, data); + if(result) + return result; +#elif defined(USE_WOLFSSL) + result = quic_ssl_ctx(&ctx->sslctx, cf, data); + if(result) + return result; +#endif + + result = quic_init_ssl(cf, data); + if(result) + return result; + + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + ctx->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; + + (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + ctx->qlogfd = qfd; /* -1 if failure above */ + quic_settings(ctx, data); + + result = vquic_ctx_init(&ctx->q, + NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST); + if(result) + return result; + + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, + &sockaddr, NULL, NULL, NULL, 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); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->sa_addr, sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + NULL, cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + +#ifdef USE_GNUTLS + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session); +#else + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl); +#endif + + ngtcp2_connection_close_error_default(&ctx->last_error); + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; + + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct cf_call_data save; + struct curltime now; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the UDP filter first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + now = Curl_now(); + + CF_DATA_SAVE(save, cf, data); + + if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { + /* Not time yet to attempt the next connect */ + DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); + goto out; + } + + if(!ctx->qconn) { + ctx->started_at = now; + result = cf_connect_start(cf, data); + if(result) + goto out; + result = cf_flush_egress(cf, data); + /* we do not expect to be able to recv anything yet */ + goto out; + } + + result = cf_process_ingress(cf, data); + if(result) + goto out; + + result = cf_flush_egress(cf, data); + if(result) + goto out; + + if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { + ctx->handshake_at = now; + DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at))); + result = qng_verify_peer(cf, data); + if(!result) { + DEBUGF(LOG_CF(data, cf, "peer verified")); + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + } + } + +out: + if(result == CURLE_RECV_ERROR && ctx->qconn && + ngtcp2_conn_is_in_draining_period(ctx->qconn)) { + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE right away. Our connection then enters the DRAINING + * state. + * This may be a stopping of the service or it may be that the server + * is reloading and a new instance will start serving soon. + * In any case, we tear down our socket and start over with a new one. + * We re-open the underlying UDP cf right now, but do not start + * connecting until called again. + */ + int reconn_delay_ms = 200; + + DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", + reconn_delay_ms)); + Curl_conn_cf_close(cf->next, data); + cf_ngtcp2_ctx_clear(ctx); + result = Curl_conn_cf_connect(cf->next, data, FALSE, done); + if(!result && *done) { + *done = FALSE; + ctx->reconnect_at = now; + ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; + Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); + result = CURLE_OK; + } + } + +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(result) { + const char *r_ip; + int r_port; + + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + infof(data, "QUIC connect to %s port %u failed: %s", + r_ip, r_port, curl_easy_strerror(result)); + } +#endif + DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + 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? */ + *pres1 = Curl_multi_max_concurrent_streams(data->multi); + DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1)); + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->got_first_byte) { + timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_ngtcp2_destroy, + cf_ngtcp2_connect, + cf_ngtcp2_close, + Curl_cf_def_get_host, + cf_ngtcp2_get_select_socks, + cf_ngtcp2_data_pending, + cf_ngtcp2_send, + cf_ngtcp2_recv, + cf_ngtcp2_data_event, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_ngtcp2_query, +}; + +CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + CURLcode result; + + (void)data; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + cf_ngtcp2_ctx_clear(ctx); + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + + result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + if(result) + goto out; + + cf->conn = conn; + udp_cf->conn = cf->conn; + udp_cf->sockindex = cf->sockindex; + cf->next = udp_cf; + +out: + *pcf = (!result)? cf : NULL; + if(result) { + if(udp_cf) + Curl_conn_cf_discard(udp_cf, data); + Curl_safefree(cf); + Curl_safefree(ctx); + } + return result; +} + +bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +#endif diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h new file mode 100644 index 0000000..8813ec9 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h @@ -0,0 +1,61 @@ +#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H +#define HEADER_CURL_VQUIC_CURL_NGTCP2_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_NGTCP2 + +#ifdef HAVE_NETINET_UDP_H +#include <netinet/udp.h> +#endif + +#include <ngtcp2/ngtcp2_crypto.h> +#include <nghttp3/nghttp3.h> +#ifdef USE_OPENSSL +#include <openssl/ssl.h> +#elif defined(USE_WOLFSSL) +#include <wolfssl/options.h> +#include <wolfssl/ssl.h> +#include <wolfssl/quic.h> +#endif + +struct Curl_cfilter; + +#include "urldata.h" + +void Curl_ngtcp2_ver(char *p, size_t len); + +CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); +#endif + +#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c new file mode 100644 index 0000000..54408d7 --- /dev/null +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c @@ -0,0 +1,1433 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_QUICHE +#include <quiche.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include "urldata.h" +#include "cfilters.h" +#include "cf-socket.h" +#include "sendf.h" +#include "strdup.h" +#include "rand.h" +#include "strcase.h" +#include "multiif.h" +#include "connect.h" +#include "progress.h" +#include "strerror.h" +#include "vquic.h" +#include "vquic_int.h" +#include "curl_quiche.h" +#include "transfer.h" +#include "h2h3.h" +#include "vtls/openssl.h" +#include "vtls/keylog.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#define QUIC_MAX_STREAMS (256*1024) +#define QUIC_MAX_DATA (1*1024*1024) +#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ + +/* how many UDP packets to send max in one call */ +#define MAX_PKT_BURST 10 +#define MAX_UDP_PAYLOAD_SIZE 1452 + +/* + * Store quiche version info in this buffer. + */ +void Curl_quiche_ver(char *p, size_t len) +{ + (void)msnprintf(p, len, "quiche/%s", quiche_version()); +} + +static void keylog_callback(const SSL *ssl, const char *line) +{ + (void)ssl; + Curl_tls_keylog_write_line(line); +} + +static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) +{ + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + + SSL_CTX_set_alpn_protos(ssl_ctx, + (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); + + SSL_CTX_set_default_verify_paths(ssl_ctx); + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); + } + + { + struct connectdata *conn = data->conn; + if(conn->ssl_config.verifypeer) { + const char * const ssl_cafile = conn->ssl_config.CAfile; + const char * const ssl_capath = conn->ssl_config.CApath; + if(ssl_cafile || ssl_capath) { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + /* tell OpenSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return NULL; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* verifying the peer without any CA certificates won't work so + use openssl's built-in default as fallback */ + SSL_CTX_set_default_verify_paths(ssl_ctx); + } +#endif + } + } + return ssl_ctx; +} + +struct quic_handshake { + char *buf; /* pointer to the buffer */ + size_t alloclen; /* size of allocation */ + size_t len; /* size of content in buffer */ + size_t nread; /* how many bytes have been read */ +}; + +struct h3_event_node { + struct h3_event_node *next; + quiche_h3_event *ev; +}; + +struct cf_quiche_ctx { + struct cf_quic_ctx q; + quiche_conn *qconn; + quiche_config *cfg; + quiche_h3_conn *h3c; + quiche_h3_config *h3config; + uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; + SSL_CTX *sslctx; + SSL *ssl; + struct curltime started_at; /* time the current attempt started */ + struct curltime handshake_at; /* time connect handshake finished */ + struct curltime first_byte_at; /* when first byte was recvd */ + struct curltime reconnect_at; /* time the next attempt should start */ + BIT(goaway); /* got GOAWAY from server */ + BIT(got_first_byte); /* if first byte was received */ +}; + + +#ifdef DEBUG_QUICHE +static void quiche_debug_log(const char *line, void *argp) +{ + (void)argp; + fprintf(stderr, "%s\n", line); +} +#endif + +static void h3_clear_pending(struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + + if(stream->pending) { + struct h3_event_node *node, *next; + for(node = stream->pending; node; node = next) { + next = node->next; + quiche_h3_event_free(node->ev); + free(node); + } + stream->pending = NULL; + } +} + +static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) +{ + if(ctx) { + vquic_ctx_free(&ctx->q); + if(ctx->qconn) + quiche_conn_free(ctx->qconn); + if(ctx->h3config) + quiche_h3_config_free(ctx->h3config); + if(ctx->h3c) + quiche_h3_conn_free(ctx->h3c); + if(ctx->cfg) + quiche_config_free(ctx->cfg); + memset(ctx, 0, sizeof(*ctx)); + } +} + +static void notify_drain(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)cf; + data->state.drain = 1; + Curl_expire(data, 0, EXPIRE_RUN_NOW); +} + +static CURLcode h3_add_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int64_t stream3_id, quiche_h3_event *ev) +{ + struct Curl_easy *mdata; + struct h3_event_node *node, **pnext; + + DEBUGASSERT(data->multi); + for(mdata = data->multi->easyp; mdata; mdata = mdata->next) { + if(mdata->req.p.http && mdata->req.p.http->stream3_id == stream3_id) { + break; + } + } + + if(!mdata) { + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] event discarded, easy handle " + "not found", stream3_id)); + quiche_h3_event_free(ev); + return CURLE_OK; + } + + node = calloc(sizeof(*node), 1); + if(!node) { + quiche_h3_event_free(ev); + return CURLE_OUT_OF_MEMORY; + } + node->ev = ev; + /* append to process them in order of arrival */ + pnext = &mdata->req.p.http->pending; + while(*pnext) { + pnext = &((*pnext)->next); + } + *pnext = node; + notify_drain(cf, mdata); + return CURLE_OK; +} + +struct h3h1header { + char *dest; + size_t destlen; /* left to use */ + size_t nlen; /* used */ +}; + +static int cb_each_header(uint8_t *name, size_t name_len, + uint8_t *value, size_t value_len, + void *argp) +{ + struct h3h1header *headers = (struct h3h1header *)argp; + size_t olen = 0; + + if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) { + msnprintf(headers->dest, + headers->destlen, "HTTP/3 %.*s \r\n", + (int) value_len, value); + } + else if(!headers->nlen) { + return CURLE_HTTP3; + } + else { + msnprintf(headers->dest, + headers->destlen, "%.*s: %.*s\r\n", + (int)name_len, name, (int) value_len, value); + } + olen = strlen(headers->dest); + headers->destlen -= olen; + headers->nlen += olen; + headers->dest += olen; + return 0; +} + +static ssize_t cf_recv_body(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + ssize_t nread; + size_t offset = 0; + + if(!stream->firstbody) { + /* add a header-body separator CRLF */ + offset = 2; + } + nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->stream3_id, + (unsigned char *)buf + offset, len - offset); + if(nread >= 0) { + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][DATA] len=%zd", + stream->stream3_id, nread)); + if(!stream->firstbody) { + stream->firstbody = TRUE; + buf[0] = '\r'; + buf[1] = '\n'; + nread += offset; + } + } + else if(nread == -1) { + *err = CURLE_AGAIN; + stream->h3_recving_data = FALSE; + } + else { + failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]", + nread, stream->stream3_id); + stream->closed = TRUE; + stream->reset = TRUE; + streamclose(cf->conn, "Reset of stream"); + stream->h3_recving_data = FALSE; + nread = -1; + *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + } + return nread; +} + +#ifdef DEBUGBUILD +static const char *cf_ev_name(quiche_h3_event *ev) +{ + switch(quiche_h3_event_type(ev)) { + case QUICHE_H3_EVENT_HEADERS: + return "HEADERS"; + case QUICHE_H3_EVENT_DATA: + return "DATA"; + case QUICHE_H3_EVENT_RESET: + return "RESET"; + case QUICHE_H3_EVENT_FINISHED: + return "FINISHED"; + case QUICHE_H3_EVENT_GOAWAY: + return "GOAWAY"; + default: + return "Unknown"; + } +} +#else +#define cf_ev_name(x) "" +#endif + +static ssize_t h3_process_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + int64_t stream3_id, + quiche_h3_event *ev, + CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + ssize_t recvd = 0; + int rc; + struct h3h1header headers; + + DEBUGASSERT(stream3_id == stream->stream3_id); + + *err = CURLE_OK; + switch(quiche_h3_event_type(ev)) { + case QUICHE_H3_EVENT_HEADERS: + stream->h3_got_header = TRUE; + headers.dest = buf; + headers.destlen = len; + headers.nlen = 0; + rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers); + if(rc) { + failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]", + rc, stream3_id); + *err = CURLE_RECV_ERROR; + recvd = -1; + break; + } + recvd = headers.nlen; + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS] len=%zd", + stream3_id, recvd)); + break; + + case QUICHE_H3_EVENT_DATA: + DEBUGASSERT(!stream->closed); + stream->h3_recving_data = TRUE; + recvd = cf_recv_body(cf, data, buf, len, err); + if(recvd < 0) { + if(*err != CURLE_AGAIN) + return -1; + recvd = 0; + } + break; + + case QUICHE_H3_EVENT_RESET: + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id)); + stream->closed = TRUE; + stream->reset = TRUE; + /* streamclose(cf->conn, "Reset of stream");*/ + stream->h3_recving_data = FALSE; + break; + + case QUICHE_H3_EVENT_FINISHED: + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id)); + stream->closed = TRUE; + /* streamclose(cf->conn, "End of stream");*/ + stream->h3_recving_data = FALSE; + break; + + case QUICHE_H3_EVENT_GOAWAY: + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id)); + break; + + default: + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d", + stream3_id, quiche_h3_event_type(ev))); + break; + } + return recvd; +} + +static ssize_t h3_process_pending(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + struct h3_event_node *node = stream->pending, **pnext = &stream->pending; + ssize_t recvd = 0, erecvd; + + *err = CURLE_OK; + DEBUGASSERT(stream); + while(node && len) { + erecvd = h3_process_event(cf, data, buf, len, + stream->stream3_id, node->ev, err); + quiche_h3_event_free(node->ev); + *pnext = node->next; + free(node); + node = *pnext; + if(erecvd < 0) { + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] process event -> %d", + stream->stream3_id, *err)); + return erecvd; + } + recvd += erecvd; + *err = CURLE_OK; + buf += erecvd; + len -= erecvd; + } + return recvd; +} + +static CURLcode cf_process_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1; + uint8_t buf[65536]; + size_t bufsize = sizeof(buf); + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + quiche_recv_info recv_info; + ssize_t recvd, nread; + ssize_t total = 0, pkts = 0; + + DEBUGASSERT(ctx->qconn); + + /* in case the timeout expired */ + quiche_conn_on_timeout(ctx->qconn); + + do { + remote_addrlen = sizeof(remote_addr); + while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + SOCKERRNO == EINTR) + ; + if(recvd < 0) { + if((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)) { + break; + } + if(SOCKERRNO == ECONNREFUSED) { + const char *r_ip; + int r_port; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + failf(data, "quiche: connection to %s:%u refused", + r_ip, r_port); + return CURLE_COULDNT_CONNECT; + } + failf(data, "quiche: recvfrom() unexpectedly returned %zd " + "(errno: %d, socket %d)", recvd, SOCKERRNO, ctx->q.sockfd); + return CURLE_RECV_ERROR; + } + + total += recvd; + ++pkts; + if(recvd > 0 && !ctx->got_first_byte) { + ctx->first_byte_at = Curl_now(); + ctx->got_first_byte = TRUE; + } + recv_info.from = (struct sockaddr *) &remote_addr; + recv_info.from_len = remote_addrlen; + recv_info.to = (struct sockaddr *) &ctx->q.local_addr; + recv_info.to_len = ctx->q.local_addrlen; + + nread = quiche_conn_recv(ctx->qconn, buf, recvd, &recv_info); + if(nread < 0) { + if(QUICHE_ERR_DONE == nread) { + DEBUGF(LOG_CF(data, cf, "ingress, quiche is DONE")); + return CURLE_OK; + } + else if(QUICHE_ERR_TLS_FAIL == nread) { + long verify_ok = SSL_get_verify_result(ctx->ssl); + if(verify_ok != X509_V_OK) { + failf(data, "SSL certificate problem: %s", + X509_verify_cert_error_string(verify_ok)); + return CURLE_PEER_FAILED_VERIFICATION; + } + } + else { + failf(data, "quiche_conn_recv() == %zd", nread); + return CURLE_RECV_ERROR; + } + } + else if(nread < recvd) { + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, quiche only " + "accepted %zd/%zd bytes", + stream3_id, nread, recvd)); + } + + } while(pkts < 1000); /* arbitrary */ + + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, recvd %zd bytes " + "in %zd packets", stream3_id, total, pkts)); + return CURLE_OK; +} + +/* + * flush_egress drains the buffers and sends off data. + * Calls failf() on errors. + */ +static CURLcode cf_flush_egress(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1; + quiche_send_info send_info; + ssize_t outlen, total_len = 0; + size_t max_udp_payload_size = + quiche_conn_max_send_udp_payload_size(ctx->qconn); + size_t gsolen = max_udp_payload_size; + size_t sent, pktcnt = 0; + CURLcode result; + int64_t timeout_ns; + + ctx->q.no_gso = TRUE; + if(ctx->q.num_blocked_pkt) { + result = vquic_send_blocked_pkt(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, still not " + "able to send blocked packet", stream3_id)); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + goto out; + } + } + + for(;;) { + outlen = quiche_conn_send(ctx->qconn, ctx->q.pktbuf, max_udp_payload_size, + &send_info); + if(outlen == QUICHE_ERR_DONE) { + result = CURLE_OK; + goto out; + } + + if(outlen < 0) { + failf(data, "quiche_conn_send returned %zd", outlen); + result = CURLE_SEND_ERROR; + goto out; + } + + /* send the pktbuf *before* the last addition */ + result = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf, + outlen, gsolen, &sent); + ++pktcnt; + total_len += outlen; + if(result) { + if(result == CURLE_AGAIN) { + /* blocked, add the pktbuf *before* and *at* the last addition + * separately to the blocked packages */ + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, pushing blocked " + "packet with %zd bytes", stream3_id, outlen)); + vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf, outlen, gsolen); + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + goto out; + } + } + +out: + timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn); + if(timeout_ns % 1000000) + timeout_ns += 1000000; + /* expire resolution is milliseconds */ + Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC); + if(pktcnt) + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, sent %zd packets " + "with %zd bytes", stream3_id, pktcnt, total_len)); + return result; +} + +static ssize_t recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + ssize_t nread = -1; + + if(stream->reset) { + failf(data, + "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id); + *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", + stream->stream3_id, *err)); + goto out; + } + + if(!stream->h3_got_header) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" + " all response header fields, treated as error", + stream->stream3_id); + /* *err = CURLE_PARTIAL_FILE; */ + *err = CURLE_RECV_ERROR; + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" + " -> %d", stream->stream3_id, *err)); + goto out; + } + else { + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" + " -> %d", stream->stream3_id, *err)); + } + *err = CURLE_OK; + nread = 0; + +out: + return nread; +} + +static CURLcode cf_poll_events(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + quiche_h3_event *ev; + + /* Take in the events and distribute them to the transfers. */ + while(1) { + int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); + if(stream3_id < 0) { + /* nothing more to do */ + break; + } + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, queue event %s " + "for [h3sid=%"PRId64"]", + stream? stream->stream3_id : -1, cf_ev_name(ev), + stream3_id)); + if(h3_add_event(cf, data, stream3_id, ev) != CURLE_OK) { + return CURLE_OUT_OF_MEMORY; + } + } + return CURLE_OK; +} + +static ssize_t cf_recv_transfer_data(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + ssize_t recvd = -1; + size_t offset = 0; + + if(stream->h3_recving_data) { + /* try receiving body first */ + recvd = cf_recv_body(cf, data, buf, len, err); + if(recvd < 0) { + if(*err != CURLE_AGAIN) + return -1; + recvd = 0; + } + if(recvd > 0) { + offset = recvd; + } + } + + if(offset < len && stream->pending) { + /* process any pending events for `data` first. if there are, + * return so the transfer can handle those. We do not want to + * progress ingress while events are pending here. */ + recvd = h3_process_pending(cf, data, buf + offset, len - offset, err); + if(recvd < 0) { + if(*err != CURLE_AGAIN) + return -1; + recvd = 0; + } + if(recvd > 0) { + offset += recvd; + } + } + + if(offset) { + *err = CURLE_OK; + return offset; + } + *err = CURLE_AGAIN; + return 0; +} + +static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, CURLcode *err) +{ + struct HTTP *stream = data->req.p.http; + ssize_t recvd = -1; + + *err = CURLE_AGAIN; + + recvd = cf_recv_transfer_data(cf, data, buf, len, err); + if(recvd) + goto out; + if(stream->closed) { + recvd = recv_closed_stream(cf, data, err); + goto out; + } + + /* we did get nothing from the quiche buffers or pending events. + * Take in more data from the connection, any error is fatal */ + if(cf_process_ingress(cf, data)) { + DEBUGF(LOG_CF(data, cf, "h3_stream_recv returns on ingress")); + *err = CURLE_RECV_ERROR; + recvd = -1; + goto out; + } + /* poll quiche and distribute the events to the transfers */ + *err = cf_poll_events(cf, data); + if(*err) { + recvd = -1; + goto out; + } + + /* try to receive again for this transfer */ + recvd = cf_recv_transfer_data(cf, data, buf, len, err); + if(recvd) + goto out; + if(stream->closed) { + recvd = recv_closed_stream(cf, data, err); + goto out; + } + recvd = -1; + *err = CURLE_AGAIN; + data->state.drain = 0; + +out: + if(cf_flush_egress(cf, data)) { + DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed")); + *err = CURLE_SEND_ERROR; + return -1; + } + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv -> %zd, err=%d", + stream->stream3_id, recvd, *err)); + if(recvd > 0) + notify_drain(cf, data); + return recvd; +} + +/* Index where :authority header field will appear in request header + field list. */ +#define AUTHORITY_DST_IDX 3 + +static CURLcode cf_http_request(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + size_t nheader; + int64_t stream3_id; + quiche_h3_header *nva = NULL; + CURLcode result = CURLE_OK; + struct h2h3req *hreq = NULL; + + stream->h3req = TRUE; /* send off! */ + stream->closed = FALSE; + stream->reset = FALSE; + + result = Curl_pseudo_headers(data, mem, len, NULL, &hreq); + if(result) + goto fail; + nheader = hreq->entries; + + nva = malloc(sizeof(quiche_h3_header) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + else { + unsigned int i; + for(i = 0; i < nheader; i++) { + nva[i].name = (unsigned char *)hreq->header[i].name; + nva[i].name_len = hreq->header[i].namelen; + nva[i].value = (unsigned char *)hreq->header[i].value; + nva[i].value_len = hreq->header[i].valuelen; + } + } + + switch(data->state.httpreq) { + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + case HTTPREQ_PUT: + if(data->state.infilesize != -1) + stream->upload_left = data->state.infilesize; + else + /* data sending without specifying the data amount up front */ + stream->upload_left = -1; /* unknown, but not zero */ + + stream->upload_done = !stream->upload_left; + stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, + stream->upload_done); + break; + default: + stream->upload_left = 0; + stream->upload_done = TRUE; + stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, + TRUE); + break; + } + + Curl_safefree(nva); + + if(stream3_id < 0) { + if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { + DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) rejected " + "with H3_ERR_STREAM_BLOCKED", + data->state.url, (long)stream->upload_left)); + result = CURLE_AGAIN; + goto fail; + } + else { + DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) -> %" PRId64, + data->state.url, (long)stream->upload_left, stream3_id)); + } + result = CURLE_SEND_ERROR; + goto fail; + } + + stream->stream3_id = stream3_id; + infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)", + stream3_id, (void *)data); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", + stream3_id, data->state.url)); + + Curl_pseudo_free(hreq); + return CURLE_OK; + +fail: + free(nva); + Curl_pseudo_free(hreq); + return result; +} + +static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + ssize_t nwritten; + + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) start", + stream->h3req? stream->stream3_id : -1, len)); + *err = cf_process_ingress(cf, data); + if(*err) + return -1; + + if(!stream->h3req) { + CURLcode result = cf_http_request(cf, data, buf, len); + if(result) { + *err = result; + return -1; + } + nwritten = len; + } + else { + nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id, + (uint8_t *)buf, len, FALSE); + DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu) -> %zd", + stream->stream3_id, len, nwritten)); + if(nwritten == QUICHE_H3_ERR_DONE) { + /* no error, nothing to do (flow control?) */ + *err = CURLE_AGAIN; + nwritten = -1; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { + DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> exceeds size", len)); + *err = CURLE_SEND_ERROR; + nwritten = -1; + } + else if(nwritten < 0) { + DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> SEND_ERROR", len)); + *err = CURLE_SEND_ERROR; + nwritten = -1; + } + else { + *err = CURLE_OK; + } + } + + if(cf_flush_egress(cf, data)) { + *err = CURLE_SEND_ERROR; + return -1; + } + + return nwritten; +} + +static bool stream_is_writeable(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct HTTP *stream = data->req.p.http; + + /* surely, there must be a better way */ + quiche_stream_iter *qiter = quiche_conn_writable(ctx->qconn); + if(qiter) { + uint64_t stream_id; + while(quiche_stream_iter_next(qiter, &stream_id)) { + if(stream_id == (uint64_t)stream->stream3_id) + return TRUE; + } + quiche_stream_iter_free(qiter); + } + return FALSE; +} + +static int cf_quiche_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + struct SingleRequest *k = &data->req; + int rv = GETSOCK_BLANK; + + socks[0] = ctx->q.sockfd; + + /* in an HTTP/3 connection we can basically always get a frame so we should + always be ready for one */ + rv |= GETSOCK_READSOCK(0); + + /* we're still uploading or the HTTP/3 layer wants to send data */ + if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) + && stream_is_writeable(cf, data)) + rv |= GETSOCK_WRITESOCK(0); + + return rv; +} + +/* + * Called from transfer.c:data_pending to know if we should keep looping + * to receive more data from the connection. + */ +static bool cf_quiche_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct HTTP *stream = data->req.p.http; + + if(stream->pending) { + DEBUGF(LOG_CF((struct Curl_easy *)data, cf, + "[h3sid=%"PRId64"] has event pending", stream->stream3_id)); + return TRUE; + } + if(stream->h3_recving_data) { + DEBUGF(LOG_CF((struct Curl_easy *)data, cf, + "[h3sid=%"PRId64"] is receiving DATA", stream->stream3_id)); + return TRUE; + } + if(data->state.drain) { + DEBUGF(LOG_CF((struct Curl_easy *)data, cf, + "[h3sid=%"PRId64"] is draining", stream->stream3_id)); + return TRUE; + } + return FALSE; +} + +static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_DONE: { + struct HTTP *stream = data->req.p.http; + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is %s", + stream->stream3_id, arg1? "cancelled" : "done")); + h3_clear_pending(data); + break; + } + case CF_CTRL_DATA_DONE_SEND: { + struct HTTP *stream = data->req.p.http; + ssize_t sent; + stream->upload_done = TRUE; + sent = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id, + NULL, 0, TRUE); + DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] send_body FINISHED", + stream->stream3_id)); + if(sent < 0) + return CURLE_SEND_ERROR; + break; + } + case CF_CTRL_DATA_IDLE: + /* anything to do? */ + break; + case CF_CTRL_CONN_REPORT_STATS: + if(cf->sockindex == FIRSTSOCKET) { + if(ctx->got_first_byte) + Curl_pgrsTimeWas(data, TIMER_CONNECT, ctx->first_byte_at); + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at); + } + break; + default: + break; + } + return result; +} + +static CURLcode cf_verify_peer(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + cf->conn->httpversion = 30; + cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; + + if(cf->conn->ssl_config.verifyhost) { + X509 *server_cert; + server_cert = SSL_get_peer_certificate(ctx->ssl); + if(!server_cert) { + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; + } + result = Curl_ossl_verifyhost(data, cf->conn, server_cert); + X509_free(server_cert); + if(result) + goto out; + } + else + DEBUGF(LOG_CF(data, cf, "Skipped certificate verification")); + + ctx->h3config = quiche_h3_config_new(); + if(!ctx->h3config) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + /* Create a new HTTP/3 connection on the QUIC connection. */ + ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config); + if(!ctx->h3c) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(data->set.ssl.certinfo) + /* asked to gather certificate info */ + (void)Curl_ossl_certchain(data, ctx->ssl); + +out: + if(result) { + if(ctx->h3config) { + quiche_h3_config_free(ctx->h3config); + ctx->h3config = NULL; + } + if(ctx->h3c) { + quiche_h3_conn_free(ctx->h3c); + ctx->h3c = NULL; + } + } + return result; +} + +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + int rv; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr; + + DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); + +#ifdef DEBUG_QUICHE + /* initialize debug log callback only once */ + static int debug_log_init = 0; + if(!debug_log_init) { + quiche_enable_debug_logging(quiche_debug_log, NULL); + debug_log_init = 1; + } +#endif + + result = vquic_ctx_init(&ctx->q, MAX_UDP_PAYLOAD_SIZE * MAX_PKT_BURST); + if(result) + return result; + + ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); + if(!ctx->cfg) { + failf(data, "can't create quiche config"); + return CURLE_FAILED_INIT; + } + quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT); + quiche_config_set_initial_max_data(ctx->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_bidi_local( + ctx->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_bidi_remote( + ctx->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_stream_data_uni(ctx->cfg, QUIC_MAX_DATA); + quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); + quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS); + quiche_config_set_application_protos(ctx->cfg, + (uint8_t *) + QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) + - 1); + + DEBUGASSERT(!ctx->ssl); + DEBUGASSERT(!ctx->sslctx); + ctx->sslctx = quic_ssl_ctx(data); + if(!ctx->sslctx) + return CURLE_QUIC_CONNECT_ERROR; + ctx->ssl = SSL_new(ctx->sslctx); + if(!ctx->ssl) + return CURLE_QUIC_CONNECT_ERROR; + + SSL_set_app_data(ctx->ssl, cf); + SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); + + result = Curl_rand(data, ctx->scid, sizeof(ctx->scid)); + if(result) + return result; + + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, + &sockaddr, NULL, NULL, NULL, 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); + if(rv == -1) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid, + sizeof(ctx->scid), NULL, 0, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen, + &sockaddr->sa_addr, sockaddr->addrlen, + ctx->cfg, ctx->ssl, false); + if(!ctx->qconn) { + failf(data, "can't create quiche connection"); + return CURLE_OUT_OF_MEMORY; + } + + /* Known to not work on Windows */ +#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) + { + int qfd; + (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); + if(qfd != -1) + quiche_conn_set_qlog_fd(ctx->qconn, qfd, + "qlog title", "curl qlog"); + } +#endif + + result = cf_flush_egress(cf, data); + if(result) + return result; + + { + unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; + unsigned alpn_len, offset = 0; + + /* Replace each ALPN length prefix by a comma. */ + while(offset < sizeof(alpn_protocols) - 1) { + alpn_len = alpn_protocols[offset]; + alpn_protocols[offset] = ','; + offset += 1 + alpn_len; + } + + DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", + alpn_protocols + 1)); + } + + return CURLE_OK; +} + +static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool blocking, bool *done) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + struct curltime now; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* Connect the UDP filter first */ + if(!cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + + *done = FALSE; + now = Curl_now(); + + if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { + /* Not time yet to attempt the next connect */ + DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); + goto out; + } + + if(!ctx->qconn) { + result = cf_connect_start(cf, data); + if(result) + goto out; + ctx->started_at = now; + result = cf_flush_egress(cf, data); + /* we do not expect to be able to recv anything yet */ + goto out; + } + + result = cf_process_ingress(cf, data); + if(result) + goto out; + + result = cf_flush_egress(cf, data); + if(result) + goto out; + + if(quiche_conn_is_established(ctx->qconn)) { + DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at))); + ctx->handshake_at = now; + result = cf_verify_peer(cf, data); + if(!result) { + DEBUGF(LOG_CF(data, cf, "peer verified")); + cf->connected = TRUE; + cf->conn->alpn = CURL_HTTP_VERSION_3; + *done = TRUE; + connkeep(cf->conn, "HTTP/3 default"); + } + } + else if(quiche_conn_is_draining(ctx->qconn)) { + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE right away. Our connection then enters the DRAINING + * state. + * This may be a stopping of the service or it may be that the server + * is reloading and a new instance will start serving soon. + * In any case, we tear down our socket and start over with a new one. + * We re-open the underlying UDP cf right now, but do not start + * connecting until called again. + */ + int reconn_delay_ms = 200; + + DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", + reconn_delay_ms)); + Curl_conn_cf_close(cf->next, data); + cf_quiche_ctx_clear(ctx); + result = Curl_conn_cf_connect(cf->next, data, FALSE, done); + if(!result && *done) { + *done = FALSE; + ctx->reconnect_at = Curl_now(); + ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; + Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); + result = CURLE_OK; + } + } + +out: +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(result && result != CURLE_AGAIN) { + const char *r_ip; + int r_port; + + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + infof(data, "connect to %s port %u failed: %s", + r_ip, r_port, curl_easy_strerror(result)); + } +#endif + return result; +} + +static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + (void)data; + if(ctx) { + if(ctx->qconn) { + (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); + /* flushing the egress is not a failsafe way to deliver all the + outstanding packets, but we also don't want to get stuck here... */ + (void)cf_flush_egress(cf, data); + } + cf_quiche_ctx_clear(ctx); + } +} + +static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + (void)data; + cf_quiche_ctx_clear(ctx); + free(ctx); + cf->ctx = NULL; +} + +static CURLcode cf_quiche_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + 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; + DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1)); + return CURLE_OK; + } + case CF_QUERY_CONNECT_REPLY_MS: + if(ctx->got_first_byte) { + timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); + *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; + } + else + *pres1 = -1; + return CURLE_OK; + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + + +struct Curl_cftype Curl_cft_http3 = { + "HTTP/3", + CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, + 0, + cf_quiche_destroy, + cf_quiche_connect, + cf_quiche_close, + Curl_cf_def_get_host, + cf_quiche_get_select_socks, + cf_quiche_data_pending, + cf_quiche_send, + cf_quiche_recv, + cf_quiche_data_event, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_quiche_query, +}; + +CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai) +{ + struct cf_quiche_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + CURLcode result; + + (void)data; + (void)conn; + ctx = calloc(sizeof(*ctx), 1); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + + result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + if(result) + goto out; + + udp_cf->conn = cf->conn; + udp_cf->sockindex = cf->sockindex; + cf->next = udp_cf; + +out: + *pcf = (!result)? cf : NULL; + if(result) { + if(udp_cf) + Curl_conn_cf_discard(udp_cf, data); + Curl_safefree(cf); + Curl_safefree(ctx); + } + + return result; +} + +bool Curl_conn_is_quiche(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_http3) + return TRUE; + if(cf->cft->flags & CF_TYPE_IP_CONNECT) + return FALSE; + } + return FALSE; +} + +#endif diff --git a/Utilities/cmcurl/lib/vquic/quiche.h b/Utilities/cmcurl/lib/vquic/curl_quiche.h index 2da65f5..bce781c 100644 --- a/Utilities/cmcurl/lib/vquic/quiche.h +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_QUICHE_H -#define HEADER_CURL_VQUIC_QUICHE_H +#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H +#define HEADER_CURL_VQUIC_CURL_QUICHE_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -31,28 +31,20 @@ #include <quiche.h> #include <openssl/ssl.h> -struct quic_handshake { - char *buf; /* pointer to the buffer */ - size_t alloclen; /* size of allocation */ - size_t len; /* size of content in buffer */ - size_t nread; /* how many bytes have been read */ -}; - -struct quicsocket { - quiche_config *cfg; - quiche_conn *conn; - quiche_h3_conn *h3c; - quiche_h3_config *h3config; - uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; - curl_socket_t sockfd; - uint32_t version; - SSL_CTX *sslctx; - SSL *ssl; - bool h3_recving; /* TRUE when in h3-body-reading state */ - struct sockaddr_storage local_addr; - socklen_t local_addrlen; -}; +struct Curl_cfilter; +struct Curl_easy; + +void Curl_quiche_ver(char *p, size_t len); + +CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); + +bool Curl_conn_is_quiche(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); #endif -#endif /* HEADER_CURL_VQUIC_QUICHE_H */ +#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ diff --git a/Utilities/cmcurl/lib/vquic/msh3.c b/Utilities/cmcurl/lib/vquic/msh3.c deleted file mode 100644 index c3e58e7..0000000 --- a/Utilities/cmcurl/lib/vquic/msh3.c +++ /dev/null @@ -1,527 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_MSH3 - -#include "urldata.h" -#include "timeval.h" -#include "multiif.h" -#include "sendf.h" -#include "connect.h" -#include "h2h3.h" -#include "msh3.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* #define DEBUG_HTTP3 1 */ -#ifdef DEBUG_HTTP3 -#define H3BUGF(x) x -#else -#define H3BUGF(x) do { } while(0) -#endif - -#define MSH3_REQ_INIT_BUF_LEN 8192 - -static CURLcode msh3_do_it(struct Curl_easy *data, bool *done); -static int msh3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); -static CURLcode msh3_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -static unsigned int msh3_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform); -static Curl_recv msh3_stream_recv; -static Curl_send msh3_stream_send; -static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, - void *IfContext, - const MSH3_HEADER *Header); -static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t Length, - const uint8_t *Data); -static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, - bool Aborted, uint64_t AbortError); -static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext); - -static const struct Curl_handler msh3_curl_handler_http3 = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - msh3_do_it, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - msh3_getsock, /* proto_getsock */ - msh3_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - msh3_getsock, /* perform_getsock */ - msh3_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - msh3_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -static const MSH3_REQUEST_IF msh3_request_if = { - msh3_header_received, - msh3_data_received, - msh3_complete, - msh3_shutdown -}; - -void Curl_quic_ver(char *p, size_t len) -{ - uint32_t v[4]; - MsH3Version(v); - (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); -} - -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct quicsocket *qs = &conn->hequic[sockindex]; - bool insecure = !conn->ssl_config.verifypeer; - memset(qs, 0, sizeof(*qs)); - - (void)sockfd; - (void)addr; /* TODO - Pass address along */ - (void)addrlen; - - H3BUGF(infof(data, "creating new api/connection")); - - qs->api = MsH3ApiOpen(); - if(!qs->api) { - failf(data, "can't create msh3 api"); - return CURLE_FAILED_INIT; - } - - qs->conn = MsH3ConnectionOpen(qs->api, - conn->host.name, - (uint16_t)conn->remote_port, - insecure); - if(!qs->conn) { - failf(data, "can't create msh3 connection"); - if(qs->api) { - MsH3ApiClose(qs->api); - } - return CURLE_FAILED_INIT; - } - - return CURLE_OK; -} - -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected) -{ - struct quicsocket *qs = &conn->hequic[sockindex]; - MSH3_CONNECTION_STATE state; - - state = MsH3ConnectionGetState(qs->conn, false); - if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) { - failf(data, "failed to connect, state=%u", (uint32_t)state); - return CURLE_COULDNT_CONNECT; - } - - if(state == MSH3_CONN_CONNECTED) { - H3BUGF(infof(data, "connection connected")); - *connected = true; - conn->quic = qs; - conn->recv[sockindex] = msh3_stream_recv; - conn->send[sockindex] = msh3_stream_send; - conn->handler = &msh3_curl_handler_http3; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 30; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - /* TODO - Clean up other happy-eyeballs connection(s)? */ - } - - return CURLE_OK; -} - -static int msh3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - struct HTTP *stream = data->req.p.http; - int bitmap = GETSOCK_BLANK; - - socks[0] = conn->sock[FIRSTSOCKET]; - - if(stream->recv_error) { - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - data->state.drain++; - } - else if(stream->recv_header_len || stream->recv_data_len) { - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - data->state.drain++; - } - - H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain)); - - return bitmap; -} - -static CURLcode msh3_do_it(struct Curl_easy *data, bool *done) -{ - struct HTTP *stream = data->req.p.http; - H3BUGF(infof(data, "msh3_do_it")); - stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN); - if(!stream->recv_buf) { - return CURLE_OUT_OF_MEMORY; - } - stream->req = ZERO_NULL; - msh3_lock_initialize(&stream->recv_lock); - stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN; - stream->recv_header_len = 0; - stream->recv_header_complete = false; - stream->recv_data_len = 0; - stream->recv_data_complete = false; - stream->recv_error = CURLE_OK; - return Curl_http(data, done); -} - -static unsigned int msh3_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - (void)data; - (void)conn; - (void)checks_to_perform; - H3BUGF(infof(data, "msh3_conncheck")); - return CONNRESULT_NONE; -} - -static void disconnect(struct quicsocket *qs) -{ - if(qs->conn) { - MsH3ConnectionClose(qs->conn); - qs->conn = ZERO_NULL; - } - if(qs->api) { - MsH3ApiClose(qs->api); - qs->api = ZERO_NULL; - } -} - -static CURLcode msh3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - (void)data; - (void)dead_connection; - H3BUGF(infof(data, "disconnecting (msh3)")); - disconnect(conn->quic); - return CURLE_OK; -} - -void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn, - int tempindex) -{ - (void)data; - if(conn->transport == TRNSPRT_QUIC) { - H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex)); - disconnect(&conn->hequic[tempindex]); - } -} - -/* Requires stream->recv_lock to be held */ -static bool msh3request_ensure_room(struct HTTP *stream, size_t len) -{ - uint8_t *new_recv_buf; - const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; - if(cur_recv_len + len > stream->recv_buf_alloc) { - size_t new_recv_buf_alloc_len = stream->recv_buf_alloc; - do { - new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */ - } while(cur_recv_len + len > new_recv_buf_alloc_len); - new_recv_buf = malloc(new_recv_buf_alloc_len); - if(!new_recv_buf) { - return false; - } - if(cur_recv_len) { - memcpy(new_recv_buf, stream->recv_buf, cur_recv_len); - } - stream->recv_buf_alloc = new_recv_buf_alloc_len; - free(stream->recv_buf); - stream->recv_buf = new_recv_buf; - } - return true; -} - -static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, - void *IfContext, - const MSH3_HEADER *Header) -{ - struct HTTP *stream = IfContext; - size_t total_len; - (void)Request; - - if(stream->recv_header_complete) { - H3BUGF(printf("* ignoring header after data\n")); - return; - } - - msh3_lock_acquire(&stream->recv_lock); - - if((Header->NameLength == 7) && - !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) { - total_len = 9 + Header->ValueLength; - if(!msh3request_ensure_room(stream, total_len)) { - /* TODO - handle error */ - goto release_lock; - } - msnprintf((char *)stream->recv_buf + stream->recv_header_len, - stream->recv_buf_alloc - stream->recv_header_len, - "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value); - } - else { - total_len = Header->NameLength + 4 + Header->ValueLength; - if(!msh3request_ensure_room(stream, total_len)) { - /* TODO - handle error */ - goto release_lock; - } - msnprintf((char *)stream->recv_buf + stream->recv_header_len, - stream->recv_buf_alloc - stream->recv_header_len, - "%.*s: %.*s\n", - (int)Header->NameLength, Header->Name, - (int)Header->ValueLength, Header->Value); - } - - stream->recv_header_len += total_len - 1; /* don't include null-terminator */ - -release_lock: - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t Length, - const uint8_t *Data) -{ - struct HTTP *stream = IfContext; - size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; - (void)Request; - H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n", - Length, cur_recv_len, stream->recv_buf_alloc)); - msh3_lock_acquire(&stream->recv_lock); - if(!stream->recv_header_complete) { - H3BUGF(printf("* Headers complete!\n")); - if(!msh3request_ensure_room(stream, 2)) { - /* TODO - handle error */ - goto release_lock; - } - stream->recv_buf[stream->recv_header_len++] = '\r'; - stream->recv_buf[stream->recv_header_len++] = '\n'; - stream->recv_header_complete = true; - cur_recv_len += 2; - } - if(!msh3request_ensure_room(stream, Length)) { - /* TODO - handle error */ - goto release_lock; - } - memcpy(stream->recv_buf + cur_recv_len, Data, Length); - stream->recv_data_len += (size_t)Length; -release_lock: - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, - bool Aborted, uint64_t AbortError) -{ - struct HTTP *stream = IfContext; - (void)Request; - (void)AbortError; - H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false")); - msh3_lock_acquire(&stream->recv_lock); - if(Aborted) { - stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */ - } - stream->recv_header_complete = true; - stream->recv_data_complete = true; - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext) -{ - struct HTTP *stream = IfContext; - (void)Request; - (void)stream; -} - -static ssize_t msh3_stream_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - struct h2h3req *hreq; - - (void)sockindex; - /* Sizes must match for cast below to work" */ - DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo)); - - H3BUGF(infof(data, "msh3_stream_send %zu", len)); - - if(!stream->req) { - *curlcode = Curl_pseudo_headers(data, mem, len, &hreq); - if(*curlcode) { - failf(data, "Curl_pseudo_headers failed"); - return -1; - } - H3BUGF(infof(data, "starting request with %zu headers", hreq->entries)); - stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream, - (MSH3_HEADER*)hreq->header, hreq->entries); - Curl_pseudo_free(hreq); - if(!stream->req) { - failf(data, "request open failed"); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - *curlcode = CURLE_OK; - return len; - } - H3BUGF(infof(data, "send %zd body bytes on request %p", len, - (void *)stream->req)); - *curlcode = CURLE_SEND_ERROR; - return -1; -} - -static ssize_t msh3_stream_recv(struct Curl_easy *data, - int sockindex, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct HTTP *stream = data->req.p.http; - size_t outsize = 0; - (void)sockindex; - H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize)); - - if(stream->recv_error) { - failf(data, "request aborted"); - *curlcode = stream->recv_error; - return -1; - } - - msh3_lock_acquire(&stream->recv_lock); - - if(stream->recv_header_len) { - outsize = buffersize; - if(stream->recv_header_len < outsize) { - outsize = stream->recv_header_len; - } - memcpy(buf, stream->recv_buf, outsize); - if(outsize < stream->recv_header_len + stream->recv_data_len) { - memmove(stream->recv_buf, stream->recv_buf + outsize, - stream->recv_header_len + stream->recv_data_len - outsize); - } - stream->recv_header_len -= outsize; - H3BUGF(infof(data, "returned %zu bytes of headers", outsize)); - } - else if(stream->recv_data_len) { - outsize = buffersize; - if(stream->recv_data_len < outsize) { - outsize = stream->recv_data_len; - } - memcpy(buf, stream->recv_buf, outsize); - if(outsize < stream->recv_data_len) { - memmove(stream->recv_buf, stream->recv_buf + outsize, - stream->recv_data_len - outsize); - } - stream->recv_data_len -= outsize; - H3BUGF(infof(data, "returned %zu bytes of data", outsize)); - } - else if(stream->recv_data_complete) { - H3BUGF(infof(data, "receive complete")); - } - - msh3_lock_release(&stream->recv_lock); - - return (ssize_t)outsize; -} - -CURLcode Curl_quic_done_sending(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - H3BUGF(infof(data, "Curl_quic_done_sending")); - if(conn->handler == &msh3_curl_handler_http3) { - struct HTTP *stream = data->req.p.http; - stream->upload_done = TRUE; - } - - return CURLE_OK; -} - -void Curl_quic_done(struct Curl_easy *data, bool premature) -{ - struct HTTP *stream = data->req.p.http; - (void)premature; - H3BUGF(infof(data, "Curl_quic_done")); - if(stream) { - if(stream->recv_buf) { - Curl_safefree(stream->recv_buf); - msh3_lock_uninitialize(&stream->recv_lock); - } - if(stream->req) { - MsH3RequestClose(stream->req); - stream->req = ZERO_NULL; - } - } -} - -bool Curl_quic_data_pending(const struct Curl_easy *data) -{ - struct HTTP *stream = data->req.p.http; - H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending")); - return stream->recv_header_len || stream->recv_data_len; -} - -/* - * Called from transfer.c:Curl_readwrite when neither HTTP level read - * nor write is performed. It is a good place to handle timer expiry - * for QUIC transport. - */ -CURLcode Curl_quic_idle(struct Curl_easy *data) -{ - (void)data; - H3BUGF(infof(data, "Curl_quic_idle")); - return CURLE_OK; -} - -#endif /* USE_MSH3 */ diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.c b/Utilities/cmcurl/lib/vquic/ngtcp2.c deleted file mode 100644 index f16b469..0000000 --- a/Utilities/cmcurl/lib/vquic/ngtcp2.c +++ /dev/null @@ -1,2266 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_NGTCP2 -#include <ngtcp2/ngtcp2.h> -#include <nghttp3/nghttp3.h> - -#ifdef USE_OPENSSL -#include <openssl/err.h> -#ifdef OPENSSL_IS_BORINGSSL -#include <ngtcp2/ngtcp2_crypto_boringssl.h> -#else -#include <ngtcp2/ngtcp2_crypto_openssl.h> -#endif -#include "vtls/openssl.h" -#elif defined(USE_GNUTLS) -#include <ngtcp2/ngtcp2_crypto_gnutls.h> -#include "vtls/gtls.h" -#elif defined(USE_WOLFSSL) -#include <ngtcp2/ngtcp2_crypto_wolfssl.h> -#include "vtls/wolfssl.h" -#endif - -#include "urldata.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "ngtcp2.h" -#include "multiif.h" -#include "strcase.h" -#include "cfilters.h" -#include "connect.h" -#include "strerror.h" -#include "dynbuf.h" -#include "vquic.h" -#include "h2h3.h" -#include "vtls/keylog.h" -#include "vtls/vtls.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* #define DEBUG_NGTCP2 */ -#ifdef CURLDEBUG -#define DEBUG_HTTP3 -#endif -#ifdef DEBUG_HTTP3 -#define H3BUGF(x) x -#else -#define H3BUGF(x) do { } while(0) -#endif - -#define H3_ALPN_H3_29 "\x5h3-29" -#define H3_ALPN_H3 "\x2h3" - -/* - * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. - * It is used as a circular buffer. Add new bytes at the end until it reaches - * the far end, then start over at index 0 again. - */ - -#define H3_SEND_SIZE (256*1024) -struct h3out { - uint8_t buf[H3_SEND_SIZE]; - size_t used; /* number of bytes used in the buffer */ - size_t windex; /* index in the buffer where to start writing the next - data block */ -}; - -#define QUIC_MAX_STREAMS (256*1024) -#define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) - -#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) -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:P-384:P-521" -#endif - -/* ngtcp2 default congestion controller does not perform pacing. Limit - the maximum packet burst to MAX_PKT_BURST packets. */ -#define MAX_PKT_BURST 10 - -static CURLcode ng_process_ingress(struct Curl_easy *data, - curl_socket_t sockfd, - struct quicsocket *qs); -static CURLcode ng_flush_egress(struct Curl_easy *data, int sockfd, - struct quicsocket *qs); -static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data); - -static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) -{ - struct quicsocket *qs = conn_ref->user_data; - return qs->qconn; -} - -static ngtcp2_tstamp timestamp(void) -{ - struct curltime ct = Curl_now(); - return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; -} - -#ifdef DEBUG_NGTCP2 -static void quic_printf(void *user_data, const char *fmt, ...) -{ - va_list ap; - (void)user_data; /* TODO, use this to do infof() instead long-term */ - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); -} -#endif - -static void qlog_callback(void *user_data, uint32_t flags, - const void *data, size_t datalen) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - (void)flags; - if(qs->qlogfd != -1) { - ssize_t rc = write(qs->qlogfd, data, datalen); - if(rc == -1) { - /* on write error, stop further write attempts */ - close(qs->qlogfd); - qs->qlogfd = -1; - } - } - -} - -static void quic_settings(struct quicsocket *qs, - uint64_t stream_buffer_size) -{ - ngtcp2_settings *s = &qs->settings; - ngtcp2_transport_params *t = &qs->transport_params; - ngtcp2_settings_default(s); - ngtcp2_transport_params_default(t); -#ifdef DEBUG_NGTCP2 - s->log_printf = quic_printf; -#else - s->log_printf = NULL; -#endif - s->initial_ts = timestamp(); - t->initial_max_stream_data_bidi_local = stream_buffer_size; - t->initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS; - t->initial_max_stream_data_uni = QUIC_MAX_STREAMS; - t->initial_max_data = QUIC_MAX_DATA; - t->initial_max_streams_bidi = 1; - t->initial_max_streams_uni = 3; - t->max_idle_timeout = QUIC_IDLE_TIMEOUT; - if(qs->qlogfd != -1) { - s->qlog.write = qlog_callback; - } -} - -#ifdef USE_OPENSSL -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} -#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; -} -#elif defined(USE_WOLFSSL) -#if defined(HAVE_SECRET_CALLBACK) -static void keylog_callback(const WOLFSSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} -#endif -#endif - -static int init_ngh3_conn(struct quicsocket *qs); - -#ifdef USE_OPENSSL -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - -#ifdef OPENSSL_IS_BORINGSSL - if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - return NULL; - } -#else - if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_openssl_configure_client_context failed"); - return NULL; - } -#endif - - SSL_CTX_set_default_verify_paths(ssl_ctx); - -#ifdef OPENSSL_IS_BORINGSSL - if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_curves_list failed"); - return NULL; - } -#else - if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { - char error_buffer[256]; - ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); - return NULL; - } - - if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_groups_list failed"); - return NULL; - } -#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(ssl_ctx, keylog_callback); - } - - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - return ssl_ctx; -} - -static CURLcode quic_set_client_cert(struct Curl_easy *data, - struct quicsocket *qs) -{ - SSL_CTX *ssl_ctx = qs->sslctx; - const struct ssl_config_data *ssl_config; - - ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET); - 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 quic_init_ssl(struct quicsocket *qs, - struct Curl_easy *data, - struct connectdata *conn) -{ - const uint8_t *alpn = NULL; - size_t alpnlen = 0; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = qs->conn->host.name; - - (void)data; - (void)conn; - DEBUGASSERT(!qs->ssl); - qs->ssl = SSL_new(qs->sslctx); - - SSL_set_app_data(qs->ssl, &qs->conn_ref); - SSL_set_connect_state(qs->ssl); - SSL_set_quic_use_legacy_codepoint(qs->ssl, 0); - - alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; - alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; - if(alpn) - SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); - - /* set SNI */ - SSL_set_tlsext_host_name(qs->ssl, hostname); - return CURLE_OK; -} -#elif defined(USE_GNUTLS) -static CURLcode quic_init_ssl(struct quicsocket *qs, - struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - gnutls_datum_t alpn[2]; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = qs->conn->host.name; - long * const pverifyresult = &data->set.ssl.certverifyresult; - int rc; - - DEBUGASSERT(qs->gtls == NULL); - qs->gtls = calloc(1, sizeof(*(qs->gtls))); - if(!qs->gtls) - return CURLE_OUT_OF_MEMORY; - - result = gtls_client_init(data, &conn->ssl_config, &data->set.ssl, - hostname, qs->gtls, pverifyresult); - if(result) - return result; - - gnutls_session_set_ptr(qs->gtls->session, &qs->conn_ref); - - if(ngtcp2_crypto_gnutls_configure_client_session(qs->gtls->session) != 0) { - H3BUGF(fprintf(stderr, - "ngtcp2_crypto_gnutls_configure_client_session failed\n")); - return CURLE_QUIC_CONNECT_ERROR; - } - - rc = gnutls_priority_set_direct(qs->gtls->session, QUIC_PRIORITY, NULL); - if(rc < 0) { - H3BUGF(fprintf(stderr, "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(qs->gtls->session, keylog_callback); - } - - /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ - alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; - alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; - alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; - alpn[1].size = sizeof(H3_ALPN_H3) - 2; - - gnutls_alpn_set_protocols(qs->gtls->session, alpn, 2, GNUTLS_ALPN_MANDATORY); - - return CURLE_OK; -} -#elif defined(USE_WOLFSSL) - -static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); - - if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); - return NULL; - } - - wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - - if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { - char error_buffer[256]; - ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); - return NULL; - } - - if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_groups_list failed"); - return NULL; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { -#if defined(HAVE_SECRET_CALLBACK) - wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); -#else - failf(data, "wolfSSL was built without keylog callback"); - return NULL; -#endif - } - - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - - if(ssl_cafile || ssl_capath) { - wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell wolfSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use wolfssl's built-in default as fallback */ - wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - else { - wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); - } - - return ssl_ctx; -} - -/** SSL callbacks ***/ - -static CURLcode quic_init_ssl(struct quicsocket *qs, - struct Curl_easy *data, - struct connectdata *conn) -{ - const uint8_t *alpn = NULL; - size_t alpnlen = 0; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = qs->conn->host.name; - - (void)data; - (void)conn; - DEBUGASSERT(!qs->ssl); - qs->ssl = SSL_new(qs->sslctx); - - wolfSSL_set_app_data(qs->ssl, &qs->conn_ref); - wolfSSL_set_connect_state(qs->ssl); - wolfSSL_set_quic_use_legacy_codepoint(qs->ssl, 0); - - alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; - alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; - if(alpn) - wolfSSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); - - /* set SNI */ - wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME, - hostname, (unsigned short)strlen(hostname)); - - return CURLE_OK; -} -#endif /* defined(USE_WOLFSSL) */ - -static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) -{ - (void)user_data; - (void)tconn; - return 0; -} - -static void extend_stream_window(ngtcp2_conn *tconn, - struct HTTP *stream) -{ - size_t thismuch = stream->unacked_window; - ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch); - ngtcp2_conn_extend_max_offset(tconn, thismuch); - stream->unacked_window = 0; -} - - -static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - nghttp3_ssize nconsumed; - int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; - (void)offset; - (void)stream_user_data; - - nconsumed = - nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin); - if(nconsumed < 0) { - ngtcp2_connection_close_error_set_application_error( - &qs->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed), - NULL, 0); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); - ngtcp2_conn_extend_max_offset(tconn, nconsumed); - - return 0; -} - -static int -cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t offset, uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)stream_id; - (void)tconn; - (void)offset; - (void)datalen; - (void)stream_user_data; - - rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)stream_user_data; - /* stream is closed... */ - - if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { - app_error_code = NGHTTP3_H3_NO_ERROR; - } - - rv = nghttp3_conn_close_stream(qs->h3conn, stream_id, - app_error_code); - if(rv) { - ngtcp2_connection_close_error_set_application_error( - &qs->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)final_size; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, - uint64_t max_streams, - void *user_data) -{ - (void)tconn; - (void)max_streams; - (void)user_data; - - return 0; -} - -static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t max_data, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)max_data; - (void)stream_user_data; - - rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static void cb_rand(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx) -{ - CURLcode result; - (void)rand_ctx; - - result = Curl_rand(NULL, dest, destlen); - if(result) { - /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, just fill 0 and call it *random*. */ - memset(dest, 0, destlen); - } -} - -static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, - uint8_t *token, size_t cidlen, - void *user_data) -{ - CURLcode result; - (void)tconn; - (void)user_data; - - result = Curl_rand(NULL, cid->data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} - -static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level, - void *user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - (void)tconn; - - if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) { - return 0; - } - - if(init_ngh3_conn(qs) != CURLE_OK) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static ngtcp2_callbacks ng_callbacks = { - ngtcp2_crypto_client_initial_cb, - NULL, /* recv_client_initial */ - ngtcp2_crypto_recv_crypto_data_cb, - cb_handshake_completed, - NULL, /* recv_version_negotiation */ - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - cb_recv_stream_data, - cb_acked_stream_data_offset, - NULL, /* stream_open */ - cb_stream_close, - NULL, /* recv_stateless_reset */ - ngtcp2_crypto_recv_retry_cb, - cb_extend_max_local_streams_bidi, - NULL, /* extend_max_local_streams_uni */ - cb_rand, - cb_get_new_connection_id, - NULL, /* remove_connection_id */ - ngtcp2_crypto_update_key_cb, /* update_key */ - NULL, /* path_validation */ - NULL, /* select_preferred_addr */ - cb_stream_reset, - NULL, /* extend_max_remote_streams_bidi */ - NULL, /* extend_max_remote_streams_uni */ - cb_extend_max_stream_data, - NULL, /* dcid_status */ - NULL, /* handshake_confirmed */ - NULL, /* recv_new_token */ - ngtcp2_crypto_delete_crypto_aead_ctx_cb, - ngtcp2_crypto_delete_crypto_cipher_ctx_cb, - NULL, /* recv_datagram */ - NULL, /* ack_datagram */ - NULL, /* lost_datagram */ - ngtcp2_crypto_get_path_challenge_data_cb, - cb_stream_stop_sending, - NULL, /* version_negotiation */ - cb_recv_rx_key, - NULL, /* recv_tx_key */ - NULL, /* early_data_rejected */ -}; - -/* - * Might be called twice for happy eyeballs. - */ -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, - socklen_t addrlen) -{ - int rc; - int rv; - CURLcode result; - ngtcp2_path path; /* TODO: this must be initialized properly */ - struct quicsocket *qs = &conn->hequic[sockindex]; - char ipbuf[40]; - int port; - int qfd; - - if(qs->conn) - Curl_quic_disconnect(data, conn, sockindex); - qs->conn = conn; - - /* extract the used address as a string */ - if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { - char buffer[STRERROR_LEN]; - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - infof(data, "Connect socket %d over QUIC to %s:%d", - sockfd, ipbuf, port); - - qs->version = NGTCP2_PROTO_VER_MAX; -#ifdef USE_OPENSSL - qs->sslctx = quic_ssl_ctx(data); - if(!qs->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - - result = quic_set_client_cert(data, qs); - if(result) - return result; -#elif defined(USE_WOLFSSL) - qs->sslctx = quic_ssl_ctx(data); - if(!qs->sslctx) - return CURLE_QUIC_CONNECT_ERROR; -#endif - - result = quic_init_ssl(qs, data, conn); - if(result) - return result; - - qs->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - qs->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - qs->qlogfd = qfd; /* -1 if failure above */ - quic_settings(qs, data->set.buffer_size); - - qs->local_addrlen = sizeof(qs->local_addr); - rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, - &qs->local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr, - qs->local_addrlen); - ngtcp2_addr_init(&path.remote, addr, addrlen); - - rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &qs->settings, &qs->transport_params, NULL, qs); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - -#ifdef USE_GNUTLS - ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->gtls->session); -#else - ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl); -#endif - - ngtcp2_connection_close_error_default(&qs->last_error); - -#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) - qs->no_gso = FALSE; -#else - qs->no_gso = TRUE; -#endif - - qs->num_blocked_pkt = 0; - qs->num_blocked_pkt_sent = 0; - memset(&qs->blocked_pkt, 0, sizeof(qs->blocked_pkt)); - - qs->pktbuflen = NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST; - qs->pktbuf = malloc(qs->pktbuflen); - if(!qs->pktbuf) { - ngtcp2_conn_del(qs->qconn); - qs->qconn = NULL; - return CURLE_OUT_OF_MEMORY; - } - - qs->conn_ref.get_conn = get_conn; - qs->conn_ref.user_data = qs; - - return CURLE_OK; -} - -/* - * Store ngtcp2 version info in this buffer. - */ -void Curl_quic_ver(char *p, size_t len) -{ - const ngtcp2_info *ng2 = ngtcp2_version(0); - const nghttp3_info *ht3 = nghttp3_version(0); - (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", - ng2->version_str, ht3->version_str); -} - -static int ng_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks) -{ - struct SingleRequest *k = &data->req; - int bitmap = GETSOCK_BLANK; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - - socks[0] = conn->sock[FIRSTSOCKET]; - - /* in an HTTP/2 connection we can basically always get a frame so we should - always be ready for one */ - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND && - (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) && - ngtcp2_conn_get_cwnd_left(qs->qconn) && - ngtcp2_conn_get_max_data_left(qs->qconn) && - nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id)) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -static void qs_disconnect(struct quicsocket *qs) -{ - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_tstamp ts; - ngtcp2_ssize rc; - - if(!qs->conn) /* already closed */ - return; - ts = timestamp(); - rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &qs->last_error, ts); - if(rc > 0) { - while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) && - SOCKERRNO == EINTR); - } - - qs->conn = NULL; - if(qs->qlogfd != -1) { - close(qs->qlogfd); - qs->qlogfd = -1; - } -#ifdef USE_OPENSSL - if(qs->ssl) - SSL_free(qs->ssl); - qs->ssl = NULL; - SSL_CTX_free(qs->sslctx); -#elif defined(USE_GNUTLS) - if(qs->gtls) { - if(qs->gtls->cred) - gnutls_certificate_free_credentials(qs->gtls->cred); - if(qs->gtls->session) - gnutls_deinit(qs->gtls->session); - free(qs->gtls); - qs->gtls = NULL; - } -#elif defined(USE_WOLFSSL) - if(qs->ssl) - wolfSSL_free(qs->ssl); - qs->ssl = NULL; - wolfSSL_CTX_free(qs->sslctx); -#endif - free(qs->pktbuf); - nghttp3_conn_del(qs->h3conn); - ngtcp2_conn_del(qs->qconn); -} - -void Curl_quic_disconnect(struct Curl_easy *data, - struct connectdata *conn, - int tempindex) -{ - (void)data; - if(conn->transport == TRNSPRT_QUIC) - qs_disconnect(&conn->hequic[tempindex]); -} - -static CURLcode ng_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - (void)dead_connection; - Curl_quic_disconnect(data, conn, 0); - Curl_quic_disconnect(data, conn, 1); - return CURLE_OK; -} - -static unsigned int ng_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - (void)data; - (void)conn; - (void)checks_to_perform; - return CONNRESULT_NONE; -} - -static const struct Curl_handler Curl_handler_http3 = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ng_getsock, /* proto_getsock */ - ng_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ng_getsock, /* perform_getsock */ - ng_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ng_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - (void)conn; - (void)stream_id; - (void)app_error_code; - (void)user_data; - H3BUGF(infof(data, "cb_h3_stream_close CALLED")); - - stream->closed = TRUE; - stream->error3 = app_error_code; - Curl_expire(data, 0, EXPIRE_QUIC); - /* make sure that ngh3_stream_recv is called again to complete the transfer - even if there are no more packets to be received from the server. */ - data->state.drain = 1; - return 0; -} - -/* - * write_data() copies data to the stream's receive buffer. If not enough - * space is available in the receive buffer, it copies the rest to the - * stream's overflow buffer. - */ -static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen) -{ - CURLcode result = CURLE_OK; - const char *buf = mem; - size_t ncopy = memlen; - /* copy as much as possible to the receive buffer */ - if(stream->len) { - size_t len = CURLMIN(ncopy, stream->len); - memcpy(stream->mem, buf, len); - stream->len -= len; - stream->memlen += len; - stream->mem += len; - buf += len; - ncopy -= len; - } - /* copy the rest to the overflow buffer */ - if(ncopy) - result = Curl_dyn_addn(&stream->overflow, buf, ncopy); - return result; -} - -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - CURLcode result = CURLE_OK; - (void)conn; - - result = write_data(stream, buf, buflen); - if(result) { - return -1; - } - stream->unacked_window += buflen; - (void)stream_id; - (void)user_data; - return 0; -} - -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, - size_t consumed, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = user_data; - (void)conn; - (void)stream_user_data; - (void)stream_id; - - ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed); - ngtcp2_conn_extend_max_offset(qs->qconn, consumed); - return 0; -} - -/* Decode HTTP status code. Returns -1 if no valid status code was - decoded. (duplicate from http2.c) */ -static int decode_status_code(const uint8_t *value, size_t len) -{ - int i; - int res; - - if(len != 3) { - return -1; - } - - res = 0; - - for(i = 0; i < 3; ++i) { - char c = value[i]; - - if(c < '0' || c > '9') { - return -1; - } - - res *= 10; - res += c - '0'; - } - - return res; -} - -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - int fin, void *user_data, void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)user_data; - (void)fin; - - /* add a CRLF only if we've received some headers */ - if(stream->firstheader) { - result = write_data(stream, "\r\n", 2); - if(result) { - return -1; - } - } - - if(stream->status_code / 100 != 1) { - stream->bodystarted = TRUE; - } - return 0; -} - -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *user_data, void *stream_user_data) -{ - nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); - nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)token; - (void)flags; - (void)user_data; - - if(token == NGHTTP3_QPACK_TOKEN__STATUS) { - char line[14]; /* status line is always 13 characters long */ - size_t ncopy; - stream->status_code = decode_status_code(h3val.base, h3val.len); - DEBUGASSERT(stream->status_code != -1); - ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - result = write_data(stream, line, ncopy); - if(result) { - return -1; - } - } - else { - /* store as an HTTP1-style header */ - result = write_data(stream, h3name.base, h3name.len); - if(result) { - return -1; - } - result = write_data(stream, ": ", 2); - if(result) { - return -1; - } - result = write_data(stream, h3val.base, h3val.len); - if(result) { - return -1; - } - result = write_data(stream, "\r\n", 2); - if(result) { - return -1; - } - } - - stream->firstheader = TRUE; - return 0; -} - -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = user_data; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_read(qs->qconn, stream_id, app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) { - struct quicsocket *qs = user_data; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_write(qs->qconn, stream_id, app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_stream_data, /* acked_stream_data */ - cb_h3_stream_close, - cb_h3_recv_data, - cb_h3_deferred_consume, - NULL, /* begin_headers */ - cb_h3_recv_header, - cb_h3_end_headers, - NULL, /* begin_trailers */ - cb_h3_recv_header, - NULL, /* end_trailers */ - cb_h3_stop_sending, - NULL, /* end_stream */ - cb_h3_reset_stream, - NULL /* shutdown */ -}; - -static int init_ngh3_conn(struct quicsocket *qs) -{ - CURLcode result; - int rc; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; - - if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) { - return CURLE_QUIC_CONNECT_ERROR; - } - - nghttp3_settings_default(&qs->h3settings); - - rc = nghttp3_conn_client_new(&qs->h3conn, - &ngh3_callbacks, - &qs->h3settings, - nghttp3_mem_default(), - qs); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - return CURLE_OK; - fail: - - return result; -} - -static Curl_recv ngh3_stream_recv; -static Curl_send ngh3_stream_send; - -static size_t drain_overflow_buffer(struct HTTP *stream) -{ - size_t overlen = Curl_dyn_len(&stream->overflow); - size_t ncopy = CURLMIN(overlen, stream->len); - if(ncopy > 0) { - memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy); - stream->len -= ncopy; - stream->mem += ncopy; - stream->memlen += ncopy; - if(ncopy != overlen) - /* make the buffer only keep the tail */ - (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy); - else - Curl_dyn_reset(&stream->overflow); - } - return ncopy; -} - -/* incoming data frames on the h3 stream */ -static ssize_t ngh3_stream_recv(struct Curl_easy *data, - int sockindex, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[sockindex]; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - - if(!stream->memlen) { - /* remember where to store incoming data for this stream and how big the - buffer is */ - stream->mem = buf; - stream->len = buffersize; - } - /* else, there's data in the buffer already */ - - /* if there's data in the overflow buffer from a previous call, copy as much - as possible to the receive buffer before receiving more */ - drain_overflow_buffer(stream); - - if(ng_process_ingress(data, sockfd, qs)) { - *curlcode = CURLE_RECV_ERROR; - return -1; - } - if(ng_flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - if(stream->memlen) { - ssize_t memlen = stream->memlen; - /* data arrived */ - *curlcode = CURLE_OK; - /* reset to allow more data to come */ - stream->memlen = 0; - stream->mem = buf; - stream->len = buffersize; - /* extend the stream window with the data we're consuming and send out - any additional packets to tell the server that we can receive more */ - extend_stream_window(qs->qconn, stream); - if(ng_flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - return memlen; - } - - if(stream->closed) { - if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - failf(data, - "HTTP/3 stream %" PRId64 " was not closed cleanly: (err %" PRIu64 - ")", - stream->stream3_id, stream->error3); - *curlcode = CURLE_HTTP3; - return -1; - } - - if(!stream->bodystarted) { - failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" - " all response header fields, treated as error", - stream->stream3_id); - *curlcode = CURLE_HTTP3; - return -1; - } - - *curlcode = CURLE_OK; - return 0; - } - - infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN"); - *curlcode = CURLE_AGAIN; - return -1; -} - -/* this amount of data has now been acked on this stream */ -static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - (void)user_data; - - if(!data->set.postfields) { - stream->h3out->used -= datalen; - H3BUGF(infof(data, - "cb_h3_acked_stream_data, %zd bytes, %zd left unacked", - datalen, stream->h3out->used)); - DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); - - if(stream->h3out->used == 0) { - int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - } - return 0; -} - -static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, - nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *user_data, - void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - size_t nread; - struct HTTP *stream = data->req.p.http; - (void)conn; - (void)stream_id; - (void)user_data; - (void)veccnt; - - if(data->set.postfields) { - vec[0].base = data->set.postfields; - vec[0].len = data->state.infilesize; - *pflags = NGHTTP3_DATA_FLAG_EOF; - return 1; - } - - if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) { - return NGHTTP3_ERR_WOULDBLOCK; - } - - nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); - if(nread > 0) { - /* nghttp3 wants us to hold on to the data until it tells us it is okay to - delete it. Append the data at the end of the h3out buffer. Since we can - only return consecutive data, copy the amount that fits and the next - part comes in next invoke. */ - struct h3out *out = stream->h3out; - if(nread + out->windex > H3_SEND_SIZE) - nread = H3_SEND_SIZE - out->windex; - - memcpy(&out->buf[out->windex], stream->upload_mem, nread); - - /* that's the chunk we return to nghttp3 */ - vec[0].base = &out->buf[out->windex]; - vec[0].len = nread; - - out->windex += nread; - out->used += nread; - - if(out->windex == H3_SEND_SIZE) - out->windex = 0; /* wrap */ - stream->upload_mem += nread; - stream->upload_len -= nread; - if(data->state.infilesize != -1) { - stream->upload_left -= nread; - if(!stream->upload_left) - *pflags = NGHTTP3_DATA_FLAG_EOF; - } - H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)", - nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", - out->used)); - } - if(stream->upload_done && !stream->upload_len && - (stream->upload_left <= 0)) { - H3BUGF(infof(data, "cb_h3_readfunction sets EOF")); - *pflags = NGHTTP3_DATA_FLAG_EOF; - return nread ? 1 : 0; - } - else if(!nread) { - return NGHTTP3_ERR_WOULDBLOCK; - } - return 1; -} - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -static CURLcode http_request(struct Curl_easy *data, const void *mem, - size_t len) -{ - struct connectdata *conn = data->conn; - struct HTTP *stream = data->req.p.http; - size_t nheader; - struct quicsocket *qs = conn->quic; - CURLcode result = CURLE_OK; - nghttp3_nv *nva = NULL; - int64_t stream3_id; - int rc; - struct h3out *h3out = NULL; - struct h2h3req *hreq = NULL; - - rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); - if(rc) { - failf(data, "can get bidi streams"); - result = CURLE_SEND_ERROR; - goto fail; - } - - stream->stream3_id = stream3_id; - stream->h3req = TRUE; /* senf off! */ - Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE); - - result = Curl_pseudo_headers(data, mem, len, &hreq); - if(result) - goto fail; - nheader = hreq->entries; - - nva = malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - else { - unsigned int i; - for(i = 0; i < nheader; i++) { - nva[i].name = (unsigned char *)hreq->header[i].name; - nva[i].namelen = hreq->header[i].namelen; - nva[i].value = (unsigned char *)hreq->header[i].value; - nva[i].valuelen = hreq->header[i].valuelen; - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - } - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: { - nghttp3_data_reader data_reader; - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown, but not zero */ - - data_reader.read_data = cb_h3_readfunction; - - h3out = calloc(sizeof(struct h3out), 1); - if(!h3out) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - stream->h3out = h3out; - - rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, - nva, nheader, &data_reader, data); - if(rc) { - result = CURLE_SEND_ERROR; - goto fail; - } - break; - } - default: - stream->upload_left = 0; /* nothing left to send */ - rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, - nva, nheader, NULL, data); - if(rc) { - result = CURLE_SEND_ERROR; - goto fail; - } - break; - } - - Curl_safefree(nva); - - infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", - stream3_id, (void *)data); - - Curl_pseudo_free(hreq); - return CURLE_OK; - -fail: - free(nva); - Curl_pseudo_free(hreq); - return result; -} -static ssize_t ngh3_stream_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - ssize_t sent = 0; - struct connectdata *conn = data->conn; - struct quicsocket *qs = conn->quic; - curl_socket_t sockfd = conn->sock[sockindex]; - struct HTTP *stream = data->req.p.http; - - if(stream->closed) { - *curlcode = CURLE_HTTP3; - return -1; - } - - if(!stream->h3req) { - CURLcode result = http_request(data, mem, len); - if(result) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - /* Assume that mem of length len only includes HTTP/1.1 style - header fields. In other words, it does not contain request - body. */ - sent = len; - } - else { - H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes", - len)); - if(!stream->upload_len) { - stream->upload_mem = mem; - stream->upload_len = len; - (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); - } - else { - *curlcode = CURLE_AGAIN; - return -1; - } - } - - if(ng_flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - /* Reset post upload buffer after resumed. */ - if(stream->upload_mem) { - if(data->set.postfields) { - sent = len; - } - else { - sent = len - stream->upload_len; - } - - stream->upload_mem = NULL; - stream->upload_len = 0; - - if(sent == 0) { - *curlcode = CURLE_AGAIN; - return -1; - } - } - - *curlcode = CURLE_OK; - return sent; -} - -static CURLcode ng_has_connected(struct Curl_easy *data, - struct connectdata *conn, int tempindex) -{ - CURLcode result = CURLE_OK; - const char *hostname, *disp_hostname; - int port; - char *snihost; - - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) - return CURLE_PEER_FAILED_VERIFICATION; - - conn->recv[FIRSTSOCKET] = ngh3_stream_recv; - conn->send[FIRSTSOCKET] = ngh3_stream_send; - conn->handler = &Curl_handler_http3; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 30; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - conn->quic = &conn->hequic[tempindex]; - - if(conn->ssl_config.verifyhost) { -#ifdef USE_OPENSSL - X509 *server_cert; - server_cert = SSL_get_peer_certificate(conn->quic->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, conn, server_cert); - X509_free(server_cert); - if(result) - return result; -#elif defined(USE_GNUTLS) - result = Curl_gtls_verifyserver(data, conn->quic->gtls->session, - &conn->ssl_config, &data->set.ssl, - hostname, disp_hostname, - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) - return result; -#elif defined(USE_WOLFSSL) - if(wolfSSL_check_domain_name(conn->quic->ssl, snihost) == 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, conn->quic->ssl); -#endif - return result; -} - -/* - * There can be multiple connection attempts going on in parallel. - */ -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *done) -{ - CURLcode result; - struct quicsocket *qs = &conn->hequic[sockindex]; - curl_socket_t sockfd = conn->tempsock[sockindex]; - - result = ng_process_ingress(data, sockfd, qs); - if(result) - goto error; - - result = ng_flush_egress(data, sockfd, qs); - if(result) - goto error; - - if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { - result = ng_has_connected(data, conn, sockindex); - if(!result) - *done = TRUE; - } - - return result; - error: - (void)qs_disconnect(qs); - return result; - -} - -static CURLcode ng_process_ingress(struct Curl_easy *data, - curl_socket_t sockfd, - struct quicsocket *qs) -{ - ssize_t recvd; - int rv; - uint8_t buf[65536]; - size_t bufsize = sizeof(buf); - struct sockaddr_storage remote_addr; - socklen_t remote_addrlen; - ngtcp2_path path; - ngtcp2_tstamp ts = timestamp(); - ngtcp2_pkt_info pi = { 0 }; - - for(;;) { - remote_addrlen = sizeof(remote_addr); - while((recvd = recvfrom(sockfd, (char *)buf, bufsize, 0, - (struct sockaddr *)&remote_addr, - &remote_addrlen)) == -1 && - SOCKERRNO == EINTR) - ; - if(recvd == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) - break; - - failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd", recvd); - return CURLE_RECV_ERROR; - } - - ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr, - qs->local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr, - remote_addrlen); - - rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts); - if(rv) { - if(!qs->last_error.error_code) { - if(rv == NGTCP2_ERR_CRYPTO) { - ngtcp2_connection_close_error_set_transport_error_tls_alert( - &qs->last_error, ngtcp2_conn_get_tls_alert(qs->qconn), NULL, 0); - } - else { - ngtcp2_connection_close_error_set_transport_error_liberr( - &qs->last_error, rv, NULL, 0); - } - } - - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode do_sendmsg(size_t *sent, struct Curl_easy *data, int sockfd, - struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen); - -static CURLcode send_packet_no_gso(size_t *psent, struct Curl_easy *data, - int sockfd, struct quicsocket *qs, - const uint8_t *pkt, size_t pktlen, - size_t gsolen) -{ - const uint8_t *p, *end = pkt + pktlen; - size_t sent; - - *psent = 0; - - for(p = pkt; p < end; p += gsolen) { - size_t len = CURLMIN(gsolen, (size_t)(end - p)); - CURLcode curlcode = do_sendmsg(&sent, data, sockfd, qs, p, len, len); - if(curlcode != CURLE_OK) { - return curlcode; - } - *psent += sent; - } - - return CURLE_OK; -} - -static CURLcode do_sendmsg(size_t *psent, struct Curl_easy *data, int sockfd, - struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen) -{ -#ifdef HAVE_SENDMSG - struct iovec msg_iov; - struct msghdr msg = {0}; - ssize_t sent; -#if defined(__linux__) && defined(UDP_SEGMENT) - uint8_t msg_ctrl[32]; - struct cmsghdr *cm; -#endif - - *psent = 0; - msg_iov.iov_base = (uint8_t *)pkt; - msg_iov.iov_len = pktlen; - msg.msg_iov = &msg_iov; - msg.msg_iovlen = 1; - -#if defined(__linux__) && defined(UDP_SEGMENT) - if(pktlen > gsolen) { - /* Only set this, when we need it. macOS, for example, - * does not seem to like a msg_control of length 0. */ - msg.msg_control = msg_ctrl; - assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); - msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); - cm = CMSG_FIRSTHDR(&msg); - cm->cmsg_level = SOL_UDP; - cm->cmsg_type = UDP_SEGMENT; - cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); - *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; - } -#endif - - - while((sent = sendmsg(sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) - ; - - if(sent == -1) { - switch(SOCKERRNO) { - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - return CURLE_AGAIN; - case EMSGSIZE: - /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ - break; - case EIO: - if(pktlen > gsolen) { - /* GSO failure */ - failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, - SOCKERRNO); - qs->no_gso = TRUE; - return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, - gsolen); - } - /* FALLTHROUGH */ - default: - failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); - return CURLE_SEND_ERROR; - } - } - else { - assert(pktlen == (size_t)sent); - } -#else - ssize_t sent; - (void)qs; - (void)gsolen; - - *psent = 0; - - while((sent = send(sockfd, (const char *)pkt, pktlen, 0)) == -1 && - SOCKERRNO == EINTR) - ; - - if(sent == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - return CURLE_AGAIN; - } - else { - failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); - if(SOCKERRNO != EMSGSIZE) { - return CURLE_SEND_ERROR; - } - /* UDP datagram is too large; caused by PMTUD. Just let it be - lost. */ - } - } -#endif - - *psent = pktlen; - - return CURLE_OK; -} - -static CURLcode send_packet(size_t *psent, struct Curl_easy *data, int sockfd, - struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen) -{ - if(qs->no_gso && pktlen > gsolen) { - return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, gsolen); - } - - return do_sendmsg(psent, data, sockfd, qs, pkt, pktlen, gsolen); -} - -static void push_blocked_pkt(struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen) -{ - struct blocked_pkt *blkpkt; - - assert(qs->num_blocked_pkt < - sizeof(qs->blocked_pkt) / sizeof(qs->blocked_pkt[0])); - - blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt++]; - - blkpkt->pkt = pkt; - blkpkt->pktlen = pktlen; - blkpkt->gsolen = gsolen; -} - -static CURLcode send_blocked_pkt(struct Curl_easy *data, int sockfd, - struct quicsocket *qs) -{ - size_t sent; - CURLcode curlcode; - struct blocked_pkt *blkpkt; - - for(; qs->num_blocked_pkt_sent < qs->num_blocked_pkt; - ++qs->num_blocked_pkt_sent) { - blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt_sent]; - curlcode = send_packet(&sent, data, sockfd, qs, blkpkt->pkt, - blkpkt->pktlen, blkpkt->gsolen); - - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - blkpkt->pkt += sent; - blkpkt->pktlen -= sent; - } - return curlcode; - } - } - - qs->num_blocked_pkt = 0; - qs->num_blocked_pkt_sent = 0; - - return CURLE_OK; -} - -static CURLcode ng_flush_egress(struct Curl_easy *data, - int sockfd, - struct quicsocket *qs) -{ - int rv; - size_t sent; - ngtcp2_ssize outlen; - uint8_t *outpos = qs->pktbuf; - size_t max_udp_payload_size = - ngtcp2_conn_get_max_tx_udp_payload_size(qs->qconn); - size_t path_max_udp_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(qs->qconn); - size_t max_pktcnt = - CURLMIN(MAX_PKT_BURST, qs->pktbuflen / max_udp_payload_size); - size_t pktcnt = 0; - size_t gsolen; - ngtcp2_path_storage ps; - ngtcp2_tstamp ts = timestamp(); - ngtcp2_tstamp expiry; - ngtcp2_duration timeout; - int64_t stream_id; - nghttp3_ssize veccnt; - int fin; - nghttp3_vec vec[16]; - ngtcp2_ssize ndatalen; - uint32_t flags; - CURLcode curlcode; - - rv = ngtcp2_conn_handle_expiry(qs->qconn, ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - ngtcp2_connection_close_error_set_transport_error_liberr(&qs->last_error, - rv, NULL, 0); - return CURLE_SEND_ERROR; - } - - if(qs->num_blocked_pkt) { - curlcode = send_blocked_pkt(data, sockfd, qs); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - } - - ngtcp2_path_storage_zero(&ps); - - for(;;) { - veccnt = 0; - stream_id = -1; - fin = 0; - - if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) { - veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec, - sizeof(vec) / sizeof(vec[0])); - if(veccnt < 0) { - failf(data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)veccnt)); - ngtcp2_connection_close_error_set_application_error( - &qs->last_error, - nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); - return CURLE_SEND_ERROR; - } - } - - flags = NGTCP2_WRITE_STREAM_FLAG_MORE | - (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); - outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, outpos, - max_udp_payload_size, - &ndatalen, flags, stream_id, - (const ngtcp2_vec *)vec, veccnt, ts); - if(outlen == 0) { - if(outpos != qs->pktbuf) { - curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, - outpos - qs->pktbuf, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent, - gsolen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - } - - break; - } - if(outlen < 0) { - switch(outlen) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - assert(ndatalen == -1); - nghttp3_conn_block_stream(qs->h3conn, stream_id); - continue; - case NGTCP2_ERR_STREAM_SHUT_WR: - assert(ndatalen == -1); - nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id); - continue; - case NGTCP2_ERR_WRITE_MORE: - assert(ndatalen >= 0); - rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); - if(rv) { - failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - continue; - default: - assert(ndatalen == -1); - failf(data, "ngtcp2_conn_writev_stream returned error: %s", - ngtcp2_strerror((int)outlen)); - ngtcp2_connection_close_error_set_transport_error_liberr( - &qs->last_error, (int)outlen, NULL, 0); - return CURLE_SEND_ERROR; - } - } - else if(ndatalen >= 0) { - rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); - if(rv) { - failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - } - - outpos += outlen; - - if(pktcnt == 0) { - gsolen = outlen; - } - else if((size_t)outlen > gsolen || - (gsolen > path_max_udp_payload_size && - (size_t)outlen != gsolen)) { - /* Packet larger than path_max_udp_payload_size is PMTUD probe - packet and it might not be sent because of EMSGSIZE. Send - them separately to minimize the loss. */ - curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, - outpos - outlen - qs->pktbuf, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - push_blocked_pkt(qs, qs->pktbuf + sent, - outpos - outlen - qs->pktbuf - sent, gsolen); - push_blocked_pkt(qs, outpos - outlen, outlen, outlen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - curlcode = send_packet(&sent, data, sockfd, qs, outpos - outlen, outlen, - outlen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - assert(0 == sent); - push_blocked_pkt(qs, outpos - outlen, outlen, outlen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - - pktcnt = 0; - outpos = qs->pktbuf; - continue; - } - - if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) { - curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, - outpos - qs->pktbuf, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent, - gsolen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - - pktcnt = 0; - outpos = qs->pktbuf; - } - } - - expiry = ngtcp2_conn_get_expiry(qs->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= ts) { - timeout = 0; - } - else { - timeout = expiry - ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - } - Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); - } - - return CURLE_OK; -} - -/* - * Called from transfer.c:done_sending when we stop HTTP/3 uploading. - */ -CURLcode Curl_quic_done_sending(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - DEBUGASSERT(conn); - if(conn->handler == &Curl_handler_http3) { - /* only for HTTP/3 transfers */ - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - stream->upload_done = TRUE; - (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); - } - - return CURLE_OK; -} - -/* - * Called from http.c:Curl_http_done when a request completes. - */ -void Curl_quic_done(struct Curl_easy *data, bool premature) -{ - (void)premature; - if(data->conn->handler == &Curl_handler_http3) { - /* only for HTTP/3 transfers */ - struct HTTP *stream = data->req.p.http; - Curl_dyn_free(&stream->overflow); - free(stream->h3out); - } -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -bool Curl_quic_data_pending(const struct Curl_easy *data) -{ - /* We may have received more data than we're able to hold in the receive - buffer and allocated an overflow buffer. Since it's possible that - there's no more data coming on the socket, we need to keep reading - until the overflow buffer is empty. */ - const struct HTTP *stream = data->req.p.http; - return Curl_dyn_len(&stream->overflow) > 0; -} - -/* - * Called from transfer.c:Curl_readwrite when neither HTTP level read - * nor write is performed. It is a good place to handle timer expiry - * for QUIC transport. - */ -CURLcode Curl_quic_idle(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct quicsocket *qs = conn->quic; - - if(ngtcp2_conn_get_expiry(qs->qconn) > timestamp()) { - return CURLE_OK; - } - - if(ng_flush_egress(data, sockfd, qs)) { - return CURLE_SEND_ERROR; - } - - return CURLE_OK; -} - -#endif diff --git a/Utilities/cmcurl/lib/vquic/quiche.c b/Utilities/cmcurl/lib/vquic/quiche.c deleted file mode 100644 index 2b9a041..0000000 --- a/Utilities/cmcurl/lib/vquic/quiche.c +++ /dev/null @@ -1,892 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_QUICHE -#include <quiche.h> -#include <openssl/err.h> -#include <openssl/ssl.h> -#include "urldata.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "quic.h" -#include "strcase.h" -#include "multiif.h" -#include "connect.h" -#include "strerror.h" -#include "vquic.h" -#include "transfer.h" -#include "h2h3.h" -#include "vtls/openssl.h" -#include "vtls/keylog.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define DEBUG_HTTP3 -/* #define DEBUG_QUICHE */ -#ifdef DEBUG_HTTP3 -#define H3BUGF(x) x -#else -#define H3BUGF(x) do { } while(0) -#endif - -#define QUIC_MAX_STREAMS (256*1024) -#define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ - -static CURLcode process_ingress(struct Curl_easy *data, - curl_socket_t sockfd, - struct quicsocket *qs); - -static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd, - struct quicsocket *qs); - -static CURLcode http_request(struct Curl_easy *data, const void *mem, - size_t len); -static Curl_recv h3_stream_recv; -static Curl_send h3_stream_send; - -static int quiche_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - struct SingleRequest *k = &data->req; - int bitmap = GETSOCK_BLANK; - - socks[0] = conn->sock[FIRSTSOCKET]; - - /* in an HTTP/2 connection we can basically always get a frame so we should - always be ready for one */ - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -static CURLcode qs_disconnect(struct Curl_easy *data, - struct quicsocket *qs) -{ - DEBUGASSERT(qs); - if(qs->conn) { - (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0); - /* flushing the egress is not a failsafe way to deliver all the - outstanding packets, but we also don't want to get stuck here... */ - (void)flush_egress(data, qs->sockfd, qs); - quiche_conn_free(qs->conn); - qs->conn = NULL; - } - if(qs->h3config) - quiche_h3_config_free(qs->h3config); - if(qs->h3c) - quiche_h3_conn_free(qs->h3c); - if(qs->cfg) { - quiche_config_free(qs->cfg); - qs->cfg = NULL; - } - return CURLE_OK; -} - -static CURLcode quiche_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct quicsocket *qs = conn->quic; - (void)dead_connection; - return qs_disconnect(data, qs); -} - -void Curl_quic_disconnect(struct Curl_easy *data, - struct connectdata *conn, - int tempindex) -{ - if(conn->transport == TRNSPRT_QUIC) - qs_disconnect(data, &conn->hequic[tempindex]); -} - -static unsigned int quiche_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - (void)data; - (void)conn; - (void)checks_to_perform; - return CONNRESULT_NONE; -} - -static CURLcode quiche_do(struct Curl_easy *data, bool *done) -{ - struct HTTP *stream = data->req.p.http; - stream->h3req = FALSE; /* not sent */ - return Curl_http(data, done); -} - -static const struct Curl_handler Curl_handler_http3 = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - quiche_do, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - quiche_getsock, /* proto_getsock */ - quiche_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - quiche_getsock, /* perform_getsock */ - quiche_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - quiche_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -#ifdef DEBUG_QUICHE -static void quiche_debug_log(const char *line, void *argp) -{ - (void)argp; - fprintf(stderr, "%s\n", line); -} -#endif - -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} - -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - - SSL_CTX_set_alpn_protos(ssl_ctx, - (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - - SSL_CTX_set_default_verify_paths(ssl_ctx); - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - { - struct connectdata *conn = data->conn; - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - } - return ssl_ctx; -} - -static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn) -{ - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = conn->host.name; - - DEBUGASSERT(!qs->ssl); - qs->ssl = SSL_new(qs->sslctx); - - SSL_set_app_data(qs->ssl, qs); - - /* set SNI */ - SSL_set_tlsext_host_name(qs->ssl, hostname); - return 0; -} - - -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, socklen_t addrlen) -{ - CURLcode result; - struct quicsocket *qs = &conn->hequic[sockindex]; - char ipbuf[40]; - int port; - int rv; - -#ifdef DEBUG_QUICHE - /* initialize debug log callback only once */ - static int debug_log_init = 0; - if(!debug_log_init) { - quiche_enable_debug_logging(quiche_debug_log, NULL); - debug_log_init = 1; - } -#endif - - (void)addr; - (void)addrlen; - - qs->sockfd = sockfd; - qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); - if(!qs->cfg) { - failf(data, "can't create quiche config"); - return CURLE_FAILED_INIT; - } - - quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT); - quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA); - quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA); - quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg, - QUIC_MAX_DATA); - quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA); - quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS); - quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS); - quiche_config_set_application_protos(qs->cfg, - (uint8_t *) - QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - - 1); - - qs->sslctx = quic_ssl_ctx(data); - if(!qs->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - - if(quic_init_ssl(qs, conn)) - return CURLE_QUIC_CONNECT_ERROR; - - result = Curl_rand(data, qs->scid, sizeof(qs->scid)); - if(result) - return result; - - qs->local_addrlen = sizeof(qs->local_addr); - rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, - &qs->local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid, - sizeof(qs->scid), NULL, 0, - (struct sockaddr *)&qs->local_addr, - qs->local_addrlen, addr, addrlen, - qs->cfg, qs->ssl, false); - if(!qs->conn) { - failf(data, "can't create quiche connection"); - return CURLE_OUT_OF_MEMORY; - } - - /* Known to not work on Windows */ -#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) - { - int qfd; - (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd); - if(qfd != -1) - quiche_conn_set_qlog_fd(qs->conn, qfd, - "qlog title", "curl qlog"); - } -#endif - - result = flush_egress(data, sockfd, qs); - if(result) - return result; - - /* extract the used address as a string */ - if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { - char buffer[STRERROR_LEN]; - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - infof(data, "Connect socket %d over QUIC to %s:%ld", - sockfd, ipbuf, port); - - Curl_persistconninfo(data, conn, NULL, -1); - - { - unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; - unsigned alpn_len, offset = 0; - - /* Replace each ALPN length prefix by a comma. */ - while(offset < sizeof(alpn_protocols) - 1) { - alpn_len = alpn_protocols[offset]; - alpn_protocols[offset] = ','; - offset += 1 + alpn_len; - } - - infof(data, "Sent QUIC client Initial, ALPN: %s", - alpn_protocols + 1); - } - - return CURLE_OK; -} - -static CURLcode quiche_has_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - int tempindex) -{ - CURLcode result; - struct quicsocket *qs = conn->quic = &conn->hequic[tempindex]; - - conn->recv[sockindex] = h3_stream_recv; - conn->send[sockindex] = h3_stream_send; - conn->handler = &Curl_handler_http3; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 30; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - - if(conn->ssl_config.verifyhost) { - X509 *server_cert; - server_cert = SSL_get_peer_certificate(qs->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, conn, server_cert); - X509_free(server_cert); - if(result) - return result; - infof(data, "Verified certificate just fine"); - } - else - infof(data, "Skipped certificate verification"); - - qs->h3config = quiche_h3_config_new(); - if(!qs->h3config) - return CURLE_OUT_OF_MEMORY; - - /* Create a new HTTP/3 connection on the QUIC connection. */ - qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config); - if(!qs->h3c) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - if(conn->hequic[1-tempindex].cfg) { - qs = &conn->hequic[1-tempindex]; - quiche_config_free(qs->cfg); - quiche_conn_free(qs->conn); - qs->cfg = NULL; - qs->conn = NULL; - } - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, qs->ssl); - - return CURLE_OK; - fail: - quiche_h3_config_free(qs->h3config); - quiche_h3_conn_free(qs->h3c); - return result; -} - -/* - * This function gets polled to check if this QUIC connection has connected. - */ -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *done) -{ - CURLcode result; - struct quicsocket *qs = &conn->hequic[sockindex]; - curl_socket_t sockfd = conn->tempsock[sockindex]; - - result = process_ingress(data, sockfd, qs); - if(result) - goto error; - - result = flush_egress(data, sockfd, qs); - if(result) - goto error; - - if(quiche_conn_is_established(qs->conn)) { - *done = TRUE; - result = quiche_has_connected(data, conn, 0, sockindex); - DEBUGF(infof(data, "quiche established connection")); - } - - return result; - error: - qs_disconnect(data, qs); - return result; -} - -static CURLcode process_ingress(struct Curl_easy *data, int sockfd, - struct quicsocket *qs) -{ - ssize_t recvd; - uint8_t *buf = (uint8_t *)data->state.buffer; - size_t bufsize = data->set.buffer_size; - struct sockaddr_storage from; - socklen_t from_len; - quiche_recv_info recv_info; - - DEBUGASSERT(qs->conn); - - /* in case the timeout expired */ - quiche_conn_on_timeout(qs->conn); - - do { - from_len = sizeof(from); - - recvd = recvfrom(sockfd, buf, bufsize, 0, - (struct sockaddr *)&from, &from_len); - - if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK))) - break; - - if(recvd < 0) { - failf(data, "quiche: recvfrom() unexpectedly returned %zd " - "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd); - return CURLE_RECV_ERROR; - } - - recv_info.from = (struct sockaddr *) &from; - recv_info.from_len = from_len; - recv_info.to = (struct sockaddr *) &qs->local_addr; - recv_info.to_len = qs->local_addrlen; - - recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info); - if(recvd == QUICHE_ERR_DONE) - break; - - if(recvd < 0) { - if(QUICHE_ERR_TLS_FAIL == recvd) { - long verify_ok = SSL_get_verify_result(qs->ssl); - if(verify_ok != X509_V_OK) { - failf(data, "SSL certificate problem: %s", - X509_verify_cert_error_string(verify_ok)); - - return CURLE_PEER_FAILED_VERIFICATION; - } - } - - failf(data, "quiche_conn_recv() == %zd", recvd); - - return CURLE_RECV_ERROR; - } - } while(1); - - return CURLE_OK; -} - -/* - * flush_egress drains the buffers and sends off data. - * Calls failf() on errors. - */ -static CURLcode flush_egress(struct Curl_easy *data, int sockfd, - struct quicsocket *qs) -{ - ssize_t sent; - uint8_t out[1200]; - int64_t timeout_ns; - quiche_send_info send_info; - - do { - sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info); - if(sent == QUICHE_ERR_DONE) - break; - - if(sent < 0) { - failf(data, "quiche_conn_send returned %zd", sent); - return CURLE_SEND_ERROR; - } - - sent = send(sockfd, out, sent, 0); - if(sent < 0) { - failf(data, "send() returned %zd", sent); - return CURLE_SEND_ERROR; - } - } while(1); - - /* time until the next timeout event, as nanoseconds. */ - timeout_ns = quiche_conn_timeout_as_nanos(qs->conn); - if(timeout_ns) - /* expire uses milliseconds */ - Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC); - - return CURLE_OK; -} - -struct h3h1header { - char *dest; - size_t destlen; /* left to use */ - size_t nlen; /* used */ -}; - -static int cb_each_header(uint8_t *name, size_t name_len, - uint8_t *value, size_t value_len, - void *argp) -{ - struct h3h1header *headers = (struct h3h1header *)argp; - size_t olen = 0; - - if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) { - msnprintf(headers->dest, - headers->destlen, "HTTP/3 %.*s\n", - (int) value_len, value); - } - else if(!headers->nlen) { - return CURLE_HTTP3; - } - else { - msnprintf(headers->dest, - headers->destlen, "%.*s: %.*s\n", - (int)name_len, name, (int) value_len, value); - } - olen = strlen(headers->dest); - headers->destlen -= olen; - headers->nlen += olen; - headers->dest += olen; - return 0; -} - -static ssize_t h3_stream_recv(struct Curl_easy *data, - int sockindex, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - ssize_t recvd = -1; - ssize_t rcode; - struct connectdata *conn = data->conn; - struct quicsocket *qs = conn->quic; - curl_socket_t sockfd = conn->sock[sockindex]; - quiche_h3_event *ev; - int rc; - struct h3h1header headers; - struct HTTP *stream = data->req.p.http; - headers.dest = buf; - headers.destlen = buffersize; - headers.nlen = 0; - - if(process_ingress(data, sockfd, qs)) { - infof(data, "h3_stream_recv returns on ingress"); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - - if(qs->h3_recving) { - /* body receiving state */ - rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id, - (unsigned char *)buf, buffersize); - if(rcode <= 0) { - recvd = -1; - qs->h3_recving = FALSE; - /* fall through into the while loop below */ - } - else - recvd = rcode; - } - - while(recvd < 0) { - int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev); - if(s < 0) - /* nothing more to do */ - break; - - if(s != stream->stream3_id) { - /* another transfer, ignore for now */ - infof(data, "Got h3 for stream %u, expects %u", - s, stream->stream3_id); - continue; - } - - switch(quiche_h3_event_type(ev)) { - case QUICHE_H3_EVENT_HEADERS: - rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers); - if(rc) { - *curlcode = rc; - failf(data, "Error in HTTP/3 response header"); - break; - } - recvd = headers.nlen; - break; - case QUICHE_H3_EVENT_DATA: - if(!stream->firstbody) { - /* add a header-body separator CRLF */ - buf[0] = '\r'; - buf[1] = '\n'; - buf += 2; - buffersize -= 2; - stream->firstbody = TRUE; - recvd = 2; /* two bytes already */ - } - else - recvd = 0; - rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf, - buffersize); - if(rcode <= 0) { - recvd = -1; - break; - } - qs->h3_recving = TRUE; - recvd += rcode; - break; - - case QUICHE_H3_EVENT_RESET: - streamclose(conn, "Stream reset"); - *curlcode = CURLE_PARTIAL_FILE; - return -1; - - case QUICHE_H3_EVENT_FINISHED: - streamclose(conn, "End of stream"); - recvd = 0; /* end of stream */ - break; - default: - break; - } - - quiche_h3_event_free(ev); - } - if(flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK; - if(recvd >= 0) - /* Get this called again to drain the event queue */ - Curl_expire(data, 0, EXPIRE_QUIC); - - data->state.drain = (recvd >= 0) ? 1 : 0; - return recvd; -} - -static ssize_t h3_stream_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - ssize_t sent; - struct connectdata *conn = data->conn; - struct quicsocket *qs = conn->quic; - curl_socket_t sockfd = conn->sock[sockindex]; - struct HTTP *stream = data->req.p.http; - - if(!stream->h3req) { - CURLcode result = http_request(data, mem, len); - if(result) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - sent = len; - } - else { - sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, - (uint8_t *)mem, len, FALSE); - if(sent == QUICHE_H3_ERR_DONE) { - sent = 0; - } - else if(sent < 0) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - } - - if(flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - *curlcode = CURLE_OK; - return sent; -} - -/* - * Store quiche version info in this buffer. - */ -void Curl_quic_ver(char *p, size_t len) -{ - (void)msnprintf(p, len, "quiche/%s", quiche_version()); -} - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -static CURLcode http_request(struct Curl_easy *data, const void *mem, - size_t len) -{ - struct connectdata *conn = data->conn; - struct HTTP *stream = data->req.p.http; - size_t nheader; - int64_t stream3_id; - quiche_h3_header *nva = NULL; - struct quicsocket *qs = conn->quic; - CURLcode result = CURLE_OK; - struct h2h3req *hreq = NULL; - - stream->h3req = TRUE; /* senf off! */ - - result = Curl_pseudo_headers(data, mem, len, &hreq); - if(result) - goto fail; - nheader = hreq->entries; - - nva = malloc(sizeof(quiche_h3_header) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - else { - unsigned int i; - for(i = 0; i < nheader; i++) { - nva[i].name = (unsigned char *)hreq->header[i].name; - nva[i].name_len = hreq->header[i].namelen; - nva[i].value = (unsigned char *)hreq->header[i].value; - nva[i].value_len = hreq->header[i].valuelen; - } - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown, but not zero */ - - stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, - stream->upload_left ? FALSE: TRUE); - if((stream3_id >= 0) && data->set.postfields) { - ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id, - (uint8_t *)data->set.postfields, - stream->upload_left, TRUE); - if(sent <= 0) { - failf(data, "quiche_h3_send_body failed"); - result = CURLE_SEND_ERROR; - } - stream->upload_left = 0; /* nothing left to send */ - } - break; - default: - stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, - TRUE); - break; - } - - Curl_safefree(nva); - - if(stream3_id < 0) { - H3BUGF(infof(data, "quiche_h3_send_request returned %d", - stream3_id)); - result = CURLE_SEND_ERROR; - goto fail; - } - - infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", - stream3_id, (void *)data); - stream->stream3_id = stream3_id; - - Curl_pseudo_free(hreq); - return CURLE_OK; - -fail: - free(nva); - Curl_pseudo_free(hreq); - return result; -} - -/* - * Called from transfer.c:done_sending when we stop HTTP/3 uploading. - */ -CURLcode Curl_quic_done_sending(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - DEBUGASSERT(conn); - if(conn->handler == &Curl_handler_http3) { - /* only for HTTP/3 transfers */ - ssize_t sent; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - stream->upload_done = TRUE; - sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, - NULL, 0, TRUE); - if(sent < 0) - return CURLE_SEND_ERROR; - } - - return CURLE_OK; -} - -/* - * Called from http.c:Curl_http_done when a request completes. - */ -void Curl_quic_done(struct Curl_easy *data, bool premature) -{ - (void)data; - (void)premature; -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -bool Curl_quic_data_pending(const struct Curl_easy *data) -{ - (void)data; - return FALSE; -} - -/* - * Called from transfer.c:Curl_readwrite when neither HTTP level read - * nor write is performed. It is a good place to handle timer expiry - * for QUIC transport. - */ -CURLcode Curl_quic_idle(struct Curl_easy *data) -{ - (void)data; - return CURLE_OK; -} - -#endif diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index e52a4f3..5f4f30d 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -24,15 +24,26 @@ #include "curl_setup.h" -#ifdef ENABLE_QUIC - #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif #include "urldata.h" #include "dynbuf.h" -#include "curl_printf.h" +#include "cfilters.h" +#include "curl_log.h" +#include "curl_msh3.h" +#include "curl_ngtcp2.h" +#include "curl_quiche.h" #include "vquic.h" +#include "vquic_int.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + + +#ifdef ENABLE_QUIC #ifdef O_BINARY #define QLOGMODE O_WRONLY|O_CREAT|O_BINARY @@ -40,6 +51,231 @@ #define QLOGMODE O_WRONLY|O_CREAT #endif +void Curl_quic_ver(char *p, size_t len) +{ +#ifdef USE_NGTCP2 + Curl_ngtcp2_ver(p, len); +#elif defined(USE_QUICHE) + Curl_quiche_ver(p, len); +#elif defined(USE_MSH3) + Curl_msh3_ver(p, len); +#endif +} + +CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen) +{ + qctx->num_blocked_pkt = 0; + qctx->num_blocked_pkt_sent = 0; + memset(&qctx->blocked_pkt, 0, sizeof(qctx->blocked_pkt)); + + qctx->pktbuflen = pktbuflen; + qctx->pktbuf = malloc(qctx->pktbuflen); + if(!qctx->pktbuf) + return CURLE_OUT_OF_MEMORY; + +#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) + qctx->no_gso = FALSE; +#else + qctx->no_gso = TRUE; +#endif + + return CURLE_OK; +} + +void vquic_ctx_free(struct cf_quic_ctx *qctx) +{ + free(qctx->pktbuf); + qctx->pktbuf = NULL; +} + +static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent); + +static CURLcode do_sendmsg(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen, + size_t *psent) +{ +#ifdef HAVE_SENDMSG + struct iovec msg_iov; + struct msghdr msg = {0}; + ssize_t sent; +#if defined(__linux__) && defined(UDP_SEGMENT) + uint8_t msg_ctrl[32]; + struct cmsghdr *cm; +#endif + + *psent = 0; + msg_iov.iov_base = (uint8_t *)pkt; + msg_iov.iov_len = pktlen; + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + +#if defined(__linux__) && defined(UDP_SEGMENT) + if(pktlen > gsolen) { + /* Only set this, when we need it. macOS, for example, + * does not seem to like a msg_control of length 0. */ + msg.msg_control = msg_ctrl; + assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); + msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + cm = CMSG_FIRSTHDR(&msg); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; + } +#endif + + + while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) + ; + + if(sent == -1) { + switch(SOCKERRNO) { + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return CURLE_AGAIN; + case EMSGSIZE: + /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ + break; + case EIO: + if(pktlen > gsolen) { + /* GSO failure */ + failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, + SOCKERRNO); + qctx->no_gso = TRUE; + return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + /* FALLTHROUGH */ + default: + failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); + return CURLE_SEND_ERROR; + } + } + else { + assert(pktlen == (size_t)sent); + } +#else + ssize_t sent; + (void)gsolen; + + *psent = 0; + + while((sent = send(qctx->sockfd, (const char *)pkt, pktlen, 0)) == -1 && + SOCKERRNO == EINTR) + ; + + if(sent == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + return CURLE_AGAIN; + } + else { + failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); + if(SOCKERRNO != EMSGSIZE) { + return CURLE_SEND_ERROR; + } + /* UDP datagram is too large; caused by PMTUD. Just let it be + lost. */ + } + } +#endif + (void)cf; + *psent = pktlen; + + return CURLE_OK; +} + +static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) +{ + const uint8_t *p, *end = pkt + pktlen; + size_t sent; + + *psent = 0; + + for(p = pkt; p < end; p += gsolen) { + size_t len = CURLMIN(gsolen, (size_t)(end - p)); + CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent); + if(curlcode != CURLE_OK) { + return curlcode; + } + *psent += sent; + } + + return CURLE_OK; +} + +CURLcode vquic_send_packet(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen, + size_t *psent) +{ + if(qctx->no_gso && pktlen > gsolen) { + return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); + } + + return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); +} + + + +void vquic_push_blocked_pkt(struct Curl_cfilter *cf, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen) +{ + struct vquic_blocked_pkt *blkpkt; + + (void)cf; + assert(qctx->num_blocked_pkt < + sizeof(qctx->blocked_pkt) / sizeof(qctx->blocked_pkt[0])); + + blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt++]; + + blkpkt->pkt = pkt; + blkpkt->pktlen = pktlen; + blkpkt->gsolen = gsolen; +} + +CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx) +{ + size_t sent; + CURLcode curlcode; + struct vquic_blocked_pkt *blkpkt; + + (void)cf; + for(; qctx->num_blocked_pkt_sent < qctx->num_blocked_pkt; + ++qctx->num_blocked_pkt_sent) { + blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt_sent]; + curlcode = vquic_send_packet(cf, data, qctx, blkpkt->pkt, + blkpkt->pktlen, blkpkt->gsolen, &sent); + + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + blkpkt->pkt += sent; + blkpkt->pktlen -= sent; + } + return curlcode; + } + } + + qctx->num_blocked_pkt = 0; + qctx->num_blocked_pkt_sent = 0; + + return CURLE_OK; +} + /* * If the QLOGDIR environment variable is set, open and return a file * descriptor to write the log to. @@ -84,4 +320,76 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, return CURLE_OK; } + +CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport) +{ + (void)transport; + DEBUGASSERT(transport == TRNSPRT_QUIC); +#ifdef USE_NGTCP2 + return Curl_cf_ngtcp2_create(pcf, data, conn, ai); +#elif defined(USE_QUICHE) + return Curl_cf_quiche_create(pcf, data, conn, ai); +#elif defined(USE_MSH3) + return Curl_cf_msh3_create(pcf, data, conn, ai); +#else + *pcf = NULL; + (void)data; + (void)conn; + (void)ai; + return CURLE_NOT_BUILT_IN; +#endif +} + +bool Curl_conn_is_http3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex) +{ +#ifdef USE_NGTCP2 + return Curl_conn_is_ngtcp2(data, conn, sockindex); +#elif defined(USE_QUICHE) + return Curl_conn_is_quiche(data, conn, sockindex); +#elif defined(USE_MSH3) + return Curl_conn_is_msh3(data, conn, sockindex); +#else + return ((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (conn->httpversion == 30)); +#endif +} + +CURLcode Curl_conn_may_http3(struct Curl_easy *data, + const struct connectdata *conn) +{ + if(!(conn->handler->flags & PROTOPT_SSL)) { + failf(data, "HTTP/3 requested for non-HTTPS URL"); + return CURLE_URL_MALFORMAT; + } +#ifndef CURL_DISABLE_PROXY + if(conn->bits.socksproxy) { + failf(data, "HTTP/3 is not supported over a SOCKS proxy"); + return CURLE_URL_MALFORMAT; + } + if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { + failf(data, "HTTP/3 is not supported over a HTTP proxy"); + return CURLE_URL_MALFORMAT; + } #endif + + return CURLE_OK; +} + +#else /* ENABLE_QUIC */ + +CURLcode Curl_conn_may_http3(struct Curl_easy *data, + const struct connectdata *conn) +{ + (void)conn; + (void)data; + DEBUGF(infof(data, "QUIC is not supported in this build")); + return CURLE_NOT_BUILT_IN; +} + +#endif /* !ENABLE_QUIC */ diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h index 8f599a8..dc73957 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.h +++ b/Utilities/cmcurl/lib/vquic/vquic.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -27,10 +27,38 @@ #include "curl_setup.h" #ifdef ENABLE_QUIC +struct Curl_cfilter; +struct Curl_easy; +struct connectdata; +struct Curl_addrinfo; + +void Curl_quic_ver(char *p, size_t len); + CURLcode Curl_qlogdir(struct Curl_easy *data, unsigned char *scid, size_t scidlen, int *qlogfdp); -#endif + + +CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai, + int transport); + +bool Curl_conn_is_http3(const struct Curl_easy *data, + const struct connectdata *conn, + int sockindex); + +extern struct Curl_cftype Curl_cft_http3; + +#else /* ENABLE_QUIC */ + +#define Curl_conn_is_http3(a,b,c) FALSE + +#endif /* !ENABLE_QUIC */ + +CURLcode Curl_conn_may_http3(struct Curl_easy *data, + const struct connectdata *conn); #endif /* HEADER_CURL_VQUIC_QUIC_H */ diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.h b/Utilities/cmcurl/lib/vquic/vquic_int.h index 2265999..42aba39 100644 --- a/Utilities/cmcurl/lib/vquic/ngtcp2.h +++ b/Utilities/cmcurl/lib/vquic/vquic_int.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_NGTCP2_H -#define HEADER_CURL_VQUIC_NGTCP2_H +#ifndef HEADER_CURL_VQUIC_QUIC_INT_H +#define HEADER_CURL_VQUIC_QUIC_INT_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -26,68 +26,47 @@ #include "curl_setup.h" -#ifdef USE_NGTCP2 +#ifdef ENABLE_QUIC -#ifdef HAVE_NETINET_UDP_H -#include <netinet/udp.h> -#endif - -#include <ngtcp2/ngtcp2_crypto.h> -#include <nghttp3/nghttp3.h> -#ifdef USE_OPENSSL -#include <openssl/ssl.h> -#elif defined(USE_WOLFSSL) -#include <wolfssl/options.h> -#include <wolfssl/ssl.h> -#include <wolfssl/quic.h> -#endif - -struct gtls_instance; - -struct blocked_pkt { +struct vquic_blocked_pkt { const uint8_t *pkt; size_t pktlen; size_t gsolen; }; -struct quicsocket { - struct connectdata *conn; /* point back to the connection */ - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_connection_close_error last_error; - ngtcp2_crypto_conn_ref conn_ref; -#ifdef USE_OPENSSL - SSL_CTX *sslctx; - SSL *ssl; -#elif defined(USE_GNUTLS) - struct gtls_instance *gtls; -#elif defined(USE_WOLFSSL) - WOLFSSL_CTX *sslctx; - WOLFSSL *ssl; -#endif +struct cf_quic_ctx { + curl_socket_t sockfd; struct sockaddr_storage local_addr; socklen_t local_addrlen; - bool no_gso; + struct vquic_blocked_pkt blocked_pkt[2]; uint8_t *pktbuf; - size_t pktbuflen; /* the number of entries in blocked_pkt */ size_t num_blocked_pkt; - /* the number of processed entries in blocked_pkt */ size_t num_blocked_pkt_sent; /* the packets blocked by sendmsg (EAGAIN or EWOULDBLOCK) */ - struct blocked_pkt blocked_pkt[2]; - - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - int qlogfd; + size_t pktbuflen; + /* the number of processed entries in blocked_pkt */ + bool no_gso; }; -#include "urldata.h" +CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen); +void vquic_ctx_free(struct cf_quic_ctx *qctx); + +CURLcode vquic_send_packet(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen, + size_t *psent); + +void vquic_push_blocked_pkt(struct Curl_cfilter *cf, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, size_t gsolen); + +CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx); + -#endif +#endif /* !ENABLE_QUIC */ -#endif /* HEADER_CURL_VQUIC_NGTCP2_H */ +#endif /* HEADER_CURL_VQUIC_QUIC_INT_H */ diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index d9fa58a..1115318 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2017 - 2022 Red Hat, Inc. + * Copyright (C) Red Hat, Inc. * * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, * Robert Kolcun, Andreas Schneider diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index ce9229f..4703eb5 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -144,7 +144,7 @@ const struct Curl_handler Curl_handler_scp = { scp_disconnect, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ - ssh_attach, + ssh_attach, /* attach */ PORT_SSH, /* defport */ CURLPROTO_SCP, /* protocol */ CURLPROTO_SCP, /* family */ @@ -173,7 +173,7 @@ const struct Curl_handler Curl_handler_sftp = { sftp_disconnect, /* disconnect */ ZERO_NULL, /* readwrite */ ZERO_NULL, /* connection_check */ - ssh_attach, + ssh_attach, /* attach */ PORT_SSH, /* defport */ CURLPROTO_SFTP, /* protocol */ CURLPROTO_SFTP, /* family */ @@ -840,6 +840,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) #endif static const char * const hostkey_method_ssh_rsa = "ssh-rsa"; + static const char * const hostkey_method_ssh_rsa_all + = "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; static const char * const hostkey_method_ssh_dss = "ssh-dss"; @@ -914,7 +916,16 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) break; #endif case LIBSSH2_KNOWNHOST_KEY_SSHRSA: - hostkey_method = hostkey_method_ssh_rsa; +#ifdef HAVE_LIBSSH2_VERSION + if(libssh2_version(0x010900)) + /* since 1.9.0 libssh2_session_method_pref() works as expected */ + hostkey_method = hostkey_method_ssh_rsa_all; + else +#endif + /* old libssh2 which cannot correctly remove unsupported methods due + * to bug in src/kex.c or does not support the new methods anyways. + */ + hostkey_method = hostkey_method_ssh_rsa; break; case LIBSSH2_KNOWNHOST_KEY_SSHDSS: hostkey_method = hostkey_method_ssh_dss; diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index 13bb8aa..7c25555 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index 6a8fb56..17d59ec 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index d9c0ce0..7e3eb79 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org> + * Copyright (C) Michael Forney, <mforney@mforney.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -58,7 +58,7 @@ struct ssl_backend_data { unsigned char buf[BR_SSL_BUFSIZE_BIDI]; br_x509_trust_anchor *anchors; size_t anchors_len; - const char *protocols[2]; + const char *protocols[ALPN_ENTRIES_MAX]; /* SSL client context is active */ bool active; /* size of pending write, yet to be flushed */ @@ -691,29 +691,17 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_unlock(data); } - if(cf->conn->bits.tls_enable_alpn) { - int cur = 0; - - /* NOTE: when adding more protocols here, increase the size of the - * protocols array in `struct ssl_backend_data`. - */ + if(connssl->alpn) { + struct alpn_proto_buf proto; + size_t i; -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy) -#endif - ) { - backend->protocols[cur++] = ALPN_H2; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + for(i = 0; i < connssl->alpn->count; ++i) { + backend->protocols[i] = connssl->alpn->entries[i]; } -#endif - - backend->protocols[cur++] = ALPN_HTTP_1_1; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - br_ssl_engine_set_protocol_names(&backend->ctx.eng, - backend->protocols, cur); + br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols, + connssl->alpn->count); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) @@ -862,26 +850,11 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(cf->conn->bits.tls_enable_alpn) { - const char *protocol; - - protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); - if(protocol) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol); + const char *proto; -#ifdef USE_HTTP2 - if(!strcmp(protocol, ALPN_H2)) - cf->conn->alpn = CURL_HTTP_VERSION_2; - else -#endif - if(!strcmp(protocol, ALPN_HTTP_1_1)) - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - else - infof(data, "ALPN, unrecognized protocol %s", protocol); - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } - else - infof(data, VTLS_INFOF_NO_ALPN); + proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); + Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, + proto? strlen(proto) : 0); } if(ssl_config->primary.sessionid) { @@ -977,7 +950,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, { CURLcode ret; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); timediff_t timeout_ms; int what; diff --git a/Utilities/cmcurl/lib/vtls/bearssl.h b/Utilities/cmcurl/lib/vtls/bearssl.h index 5125359..b3651b0 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.h +++ b/Utilities/cmcurl/lib/vtls/bearssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org> + * Copyright (C) Michael Forney, <mforney@mforney.org> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c index 2074dca..59fd27c 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.c +++ b/Utilities/cmcurl/lib/vtls/gskit.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -499,7 +499,7 @@ static void cancel_async_handshake(struct Curl_cfilter *cf, (void)data; DEBUGASSERT(BACKEND); - if(QsoCancelOperation(cf->conn->sock[cf->sockindex], 0) > 0) + if(QsoCancelOperation(Curl_conn_cf_get_socket(cf, data), 0) > 0) QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); } @@ -532,7 +532,7 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, int directions) DEBUGASSERT(connssl_next->backend); n = 1; fds[0].fd = BACKEND->remotefd; - fds[1].fd = cf->conn->sock[cf->sockindex]; + fds[1].fd = Curl_conn_cf_get_socket(cf, data); if(directions & SOS_READ) { fds[0].events |= POLLOUT; @@ -847,7 +847,7 @@ static CURLcode gskit_connect_step1(struct Curl_cfilter *cf, result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1); if(!result) result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0? - BACKEND->localfd: cf->conn->sock[cf->sockindex]); + BACKEND->localfd: Curl_conn_cf_get_socket(cf, data)); if(!result) result = set_ciphers(cf, data, BACKEND->handle, &protoflags); if(!protoflags) { @@ -1208,7 +1208,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf, close_one(cf, data); rc = 0; - what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], + what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); while(loop--) { @@ -1230,7 +1230,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf, notify alert from the server. No way to gsk_secure_soc_read() now, so use read(). */ - nread = read(cf->conn->sock[cf->sockindex], buf, sizeof(buf)); + nread = read(Curl_conn_cf_get_socket(cf, data), buf, sizeof(buf)); if(nread < 0) { char buffer[STRERROR_LEN]; @@ -1241,7 +1241,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf, if(nread <= 0) break; - what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0); + what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); } return rc; diff --git a/Utilities/cmcurl/lib/vtls/gskit.h b/Utilities/cmcurl/lib/vtls/gskit.h index cf923f6..c71e6a0 100644 --- a/Utilities/cmcurl/lib/vtls/gskit.h +++ b/Utilities/cmcurl/lib/vtls/gskit.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 104dce6..07dfaa4 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -84,7 +84,7 @@ 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 Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result; @@ -102,7 +102,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) { struct Curl_cfilter *cf = s; struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result; @@ -214,7 +214,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; gnutls_session_t session; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); DEBUGASSERT(backend); session = backend->gtls.session; @@ -434,12 +434,10 @@ CURLcode gtls_client_init(struct Curl_easy *data, } #ifdef USE_GNUTLS_SRP - if((config->authtype == CURL_TLSAUTH_SRP) && - Curl_auth_allowed_to_host(data)) { + if(config->username && Curl_auth_allowed_to_host(data)) { infof(data, "Using TLS-SRP username: %s", config->username); - rc = gnutls_srp_allocate_client_credentials( - >ls->srp_client_cred); + rc = gnutls_srp_allocate_client_credentials(>ls->srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_allocate_client_cred() failed: %s", gnutls_strerror(rc)); @@ -581,7 +579,7 @@ CURLcode gtls_client_init(struct Curl_easy *data, #ifdef USE_GNUTLS_SRP /* Only add SRP to the cipher list if SRP is requested. Otherwise * GnuTLS will disable TLS 1.3 support. */ - if(config->authtype == CURL_TLSAUTH_SRP) { + if(config->username) { size_t len = strlen(prioritylist); char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1); @@ -646,7 +644,7 @@ CURLcode gtls_client_init(struct Curl_easy *data, #ifdef USE_GNUTLS_SRP /* put the credentials to the current session */ - if(config->authtype == CURL_TLSAUTH_SRP) { + if(config->username) { rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP, gtls->srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { @@ -700,32 +698,22 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(result) return result; - if(cf->conn->bits.tls_enable_alpn) { - int cur = 0; - gnutls_datum_t protocols[2]; + if(connssl->alpn) { + struct alpn_proto_buf proto; + gnutls_datum_t alpn[ALPN_ENTRIES_MAX]; + size_t i; -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur].data = (unsigned char *)ALPN_H2; - protocols[cur].size = ALPN_H2_LENGTH; - cur++; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + 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]); } -#endif - - protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1; - protocols[cur].size = ALPN_HTTP_1_1_LENGTH; - cur++; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - if(gnutls_alpn_set_protocols(backend->gtls.session, protocols, cur, 0)) { + if(gnutls_alpn_set_protocols(backend->gtls.session, alpn, + (unsigned)connssl->alpn->count, 0)) { 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 @@ -860,10 +848,8 @@ Curl_gtls_verifyserver(struct Curl_easy *data, config->verifyhost || config->issuercert) { #ifdef USE_GNUTLS_SRP - if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP - && ssl_config->primary.username - && !config->verifypeer - && gnutls_cipher_get(session)) { + if(ssl_config->primary.username && !config->verifypeer && + gnutls_cipher_get(session)) { /* no peer cert, but auth is ok if we have SRP user and cipher and no peer verify */ } @@ -1271,28 +1257,10 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, int rc; rc = gnutls_alpn_get_selected_protocol(session, &proto); - if(rc == 0) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size, - proto.data); - -#ifdef USE_HTTP2 - if(proto.size == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, proto.data, - ALPN_H2_LENGTH)) { - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(proto.size == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - } + if(rc == 0) + Curl_alpn_set_negotiated(cf, data, proto.data, proto.size); else - infof(data, VTLS_INFOF_NO_ALPN); - - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + Curl_alpn_set_negotiated(cf, data, NULL, 0); } if(ssl_config->primary.sessionid) { @@ -1516,7 +1484,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf, char buf[120]; while(!done) { - int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], + int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); if(what > 0) { /* Something to read, let's do it and hope that it is the close @@ -1556,8 +1524,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf, gnutls_certificate_free_credentials(backend->gtls.cred); #ifdef USE_GNUTLS_SRP - if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP - && ssl_config->primary.username != NULL) + if(ssl_config->primary.username) gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred); #endif diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index 49c1c47..ac141e1 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c index 2a648f2..e827dc5 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.c +++ b/Utilities/cmcurl/lib/vtls/hostcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/hostcheck.h b/Utilities/cmcurl/lib/vtls/hostcheck.h index d3c4eab..22a1ac2 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.h +++ b/Utilities/cmcurl/lib/vtls/hostcheck.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c index 1952a69..d37bb18 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.c +++ b/Utilities/cmcurl/lib/vtls/keylog.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h index 5d3c675..eff5bf3 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.h +++ b/Utilities/cmcurl/lib/vtls/keylog.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 0b81662..7f0f4e3 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -159,15 +159,14 @@ static void mbed_debug(void *context, int level, const char *f_name, static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen) { struct Curl_cfilter *cf = bio; - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result); - /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"), - blen, (int)nwritten, result)); */ + DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d", + blen, nwritten, result)); if(nwritten < 0 && CURLE_AGAIN == result) { nwritten = MBEDTLS_ERR_SSL_WANT_WRITE; } @@ -177,8 +176,7 @@ static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen) static int bio_cf_read(void *bio, unsigned char *buf, size_t blen) { struct Curl_cfilter *cf = bio; - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result; @@ -188,8 +186,8 @@ static int bio_cf_read(void *bio, unsigned char *buf, size_t blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result); - /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"), - blen, (int)nread, result)); */ + DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d", + blen, nread, result)); if(nread < 0 && CURLE_AGAIN == result) { nread = MBEDTLS_ERR_SSL_WANT_READ; } @@ -648,14 +646,13 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifdef HAS_ALPN - if(cf->conn->bits.tls_enable_alpn) { - const char **p = &backend->protocols[0]; -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) - *p++ = ALPN_H2; -#endif - *p++ = ALPN_HTTP_1_1; - *p = NULL; + if(connssl->alpn) { + struct alpn_proto_buf proto; + size_t i; + + for(i = 0; i < connssl->alpn->count; ++i) { + backend->protocols[i] = connssl->alpn->entries[i]; + } /* this function doesn't clone the protocols array, which is why we need to keep it around */ if(mbedtls_ssl_conf_alpn_protocols(&backend->config, @@ -663,8 +660,8 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "Failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } - for(p = &backend->protocols[0]; *p; ++p) - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif @@ -844,28 +841,11 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifdef HAS_ALPN - if(cf->conn->bits.tls_enable_alpn) { - const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl); + if(connssl->alpn) { + const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl); - if(next_protocol) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol); -#ifdef USE_HTTP2 - if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) && - !next_protocol[ALPN_H2_LENGTH]) { - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) && - !next_protocol[ALPN_HTTP_1_1_LENGTH]) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - } - else { - infof(data, VTLS_INFOF_NO_ALPN); - } - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, + proto? strlen(proto) : 0); } #endif @@ -1081,7 +1061,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, { CURLcode retcode; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); timediff_t timeout_ms; int what; diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.h b/Utilities/cmcurl/lib/vtls/mbedtls.h index ec3b43b..d8a0a06 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.h +++ b/Utilities/cmcurl/lib/vtls/mbedtls.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com> * * 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/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c index 7d019ed..bcb7106 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com> * * 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/vtls/mbedtls_threadlock.h b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h index 22e8725..2b0bd41 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h +++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com> * * 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/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c index 03694d2..774cbdd 100644 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ b/Utilities/cmcurl/lib/vtls/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -873,11 +873,11 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) #endif case SSL_NEXT_PROTO_NO_SUPPORT: case SSL_NEXT_PROTO_NO_OVERLAP: - infof(data, VTLS_INFOF_NO_ALPN); + Curl_alpn_set_negotiated(cf, data, NULL, 0); return; #ifdef SSL_ENABLE_ALPN case SSL_NEXT_PROTO_SELECTED: - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf); + Curl_alpn_set_negotiated(cf, data, buf, buflen); break; #endif default: @@ -885,25 +885,6 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) break; } -#ifdef USE_HTTP2 - if(buflen == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) { - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(buflen == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - - /* This callback might get called when PR_Recv() is used within - * close_one() during a connection shutdown. At that point there might not - * be any "bundle" associated with the connection anymore. - */ - if(conn->bundle) - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } } @@ -1897,7 +1878,7 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf, PRFileDesc *nspr_io_stub = NULL; PRBool ssl_no_cache; PRBool ssl_cbc_random_iv; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -2163,27 +2144,17 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf, #endif #if defined(SSL_ENABLE_ALPN) - if(cf->conn->bits.tls_enable_alpn) { - int cur = 0; - unsigned char protocols[128]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur++] = ALPN_H2_LENGTH; - memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - } -#endif - protocols[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; + if(connssl->alpn) { + struct alpn_proto_buf proto; - if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess) + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result || SSL_SetNextProtoNego(backend->handle, proto.data, proto.len) + != SECSuccess) { + failf(data, "Error setting ALPN"); goto error; + } + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif @@ -2393,6 +2364,19 @@ static ssize_t nss_send(struct Curl_cfilter *cf, return rc; /* number of bytes */ } +static bool +nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + PRFileDesc *fd = connssl->backend->handle->lower; + char buf; + + (void) data; + + /* Returns true in case of error to force reading. */ + return PR_Recv(fd, (void *) &buf, 1, PR_MSG_PEEK, PR_INTERVAL_NO_WAIT) != 0; +} + static ssize_t nss_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* transfer */ char *buf, /* store read data here */ @@ -2543,7 +2527,7 @@ const struct Curl_ssl Curl_ssl_nss = { nss_check_cxn, /* check_cxn */ /* NSS has no shutdown function provided and thus always fail */ Curl_none_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ + nss_data_pending, /* data_pending */ nss_random, /* random */ nss_cert_status_request, /* cert_status_request */ nss_connect, /* connect */ diff --git a/Utilities/cmcurl/lib/vtls/nssg.h b/Utilities/cmcurl/lib/vtls/nssg.h index 454a38f..ad7eef5 100644 --- a/Utilities/cmcurl/lib/vtls/nssg.h +++ b/Utilities/cmcurl/lib/vtls/nssg.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index c5085be..e3a50bd 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -96,6 +96,7 @@ #include "curl_memory.h" #include "memdebug.h" + /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS renegotiations when built with BoringSSL. Renegotiating is non-compliant with HTTP/2 and "an extremely dangerous protocol feature". Beware. @@ -262,6 +263,12 @@ #define HAVE_OPENSSL_VERSION #endif +#ifdef OPENSSL_IS_BORINGSSL +typedef uint32_t sslerr_t; +#else +typedef unsigned long sslerr_t; +#endif + /* * Whether the OpenSSL version has the API needed to support sharing an * X509_STORE between connections. The API is: @@ -285,17 +292,17 @@ #endif /* !LIBRESSL_VERSION_NUMBER */ struct ssl_backend_data { - struct Curl_easy *logger; /* transfer handle to pass trace logs to, only - using sockindex 0 */ /* 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) @@ -710,14 +717,14 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_SEND_ERROR; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"), - blen, (int)nwritten, result)); */ + DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", + blen, (int)nwritten, result)); BIO_clear_retry_flags(bio); connssl->backend->io_result = result; if(nwritten < 0) { @@ -731,7 +738,7 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_RECV_ERROR; @@ -741,64 +748,75 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"), - blen, (int)nread, result)); */ + DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", + blen, (int)nread, result)); BIO_clear_retry_flags(bio); connssl->backend->io_result = result; if(nread < 0) { if(CURLE_AGAIN == result) BIO_set_retry_read(bio); } + + /* Before returning server replies to the SSL instance, we need + * to have setup the x509 store or verification will fail. */ + if(!connssl->backend->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx); + if(result) { + connssl->backend->io_result = result; + return -1; + } + connssl->backend->x509_store_setup = TRUE; + } + return (int)nread; } -static BIO_METHOD *bio_cf_method = NULL; - #if USE_PRE_1_1_API static BIO_METHOD bio_cf_meth_1_0 = { - BIO_TYPE_MEM, - "OpenSSL CF BIO", - bio_cf_out_write, - bio_cf_in_read, - NULL, /* puts is never called */ - NULL, /* gets is never called */ - bio_cf_ctrl, - bio_cf_create, - bio_cf_destroy, - NULL + BIO_TYPE_MEM, + "OpenSSL CF BIO", + bio_cf_out_write, + bio_cf_in_read, + NULL, /* puts is never called */ + NULL, /* gets is never called */ + bio_cf_ctrl, + bio_cf_create, + bio_cf_destroy, + NULL }; -static void bio_cf_init_methods(void) +static BIO_METHOD *bio_cf_method_create(void) { - bio_cf_method = &bio_cf_meth_1_0; + return &bio_cf_meth_1_0; } -#define bio_cf_free_methods() Curl_nop_stmt +#define bio_cf_method_free(m) Curl_nop_stmt #else -static void bio_cf_init_methods(void) +static BIO_METHOD *bio_cf_method_create(void) { - bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); - BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); - BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); - BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); - BIO_meth_set_create(bio_cf_method, &bio_cf_create); - BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); + if(m) { + BIO_meth_set_write(m, &bio_cf_out_write); + BIO_meth_set_read(m, &bio_cf_in_read); + BIO_meth_set_ctrl(m, &bio_cf_ctrl); + BIO_meth_set_create(m, &bio_cf_create); + BIO_meth_set_destroy(m, &bio_cf_destroy); + } + return m; } -static void bio_cf_free_methods(void) +static void bio_cf_method_free(BIO_METHOD *m) { - BIO_meth_free(bio_cf_method); + if(m) + BIO_meth_free(m); } #endif -static bool ossl_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); - /* * Number of bytes to read from the random number seed file. This must be * a finite value (because some entropy "files" like /dev/urandom have @@ -930,54 +948,6 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) return buf; } -/* Return an extra data index for the transfer data. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_data_index(void) -{ - static int ssl_ex_data_data_index = -1; - if(ssl_ex_data_data_index < 0) { - ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return ssl_ex_data_data_index; -} - -/* Return an extra data index for the associated Curl_cfilter instance. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_cf_index(void) -{ - static int ssl_ex_data_cf_index = -1; - if(ssl_ex_data_cf_index < 0) { - ssl_ex_data_cf_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return ssl_ex_data_cf_index; -} - -/* Return an extra data index for the sockindex. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_sockindex_index(void) -{ - static int sockindex_index = -1; - if(sockindex_index < 0) { - sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return sockindex_index; -} - -/* Return an extra data index for proxy boolean. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_proxy_index(void) -{ - static int proxy_index = -1; - if(proxy_index < 0) { - proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return proxy_index; -} - static int passwd_callback(char *buf, int num, int encrypting, void *global_passwd) { @@ -1254,7 +1224,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, if(ret) { X509 *ca; - unsigned long err; + sslerr_t err; if(!SSL_CTX_clear_chain_certs(ctx)) { ret = 0; @@ -1776,14 +1746,8 @@ static int ossl_init(void) OpenSSL_add_all_algorithms(); #endif - bio_cf_init_methods(); Curl_tls_keylog_open(); - /* Initialize the extra data indexes */ - if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_cf_index() < 0 || - ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0) - return 0; - return 1; } @@ -1822,7 +1786,6 @@ static void ossl_cleanup(void) #endif Curl_tls_keylog_close(); - bio_cf_free_methods(); } /* @@ -1840,7 +1803,10 @@ static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef MSG_PEEK char buf; ssize_t nread; - nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex], + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(sock == CURL_SOCKET_BAD) + return 0; /* no socket, consider closed */ + nread = recv((RECV_TYPE_ARG1)sock, (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK); if(nread == 0) @@ -1968,19 +1934,15 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list; } -#define set_logger(connssl, data) \ - connssl->backend->logger = data - static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; + (void)data; DEBUGASSERT(backend); if(backend->handle) { - set_logger(connssl, data); - if(cf->next && cf->next->connected) { char buf[32]; /* Maybe the server has already sent a close notify alert. @@ -1997,6 +1959,11 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(backend->ctx) { SSL_CTX_free(backend->ctx); backend->ctx = NULL; + backend->x509_store_setup = FALSE; + } + if(backend->bio_method) { + bio_cf_method_free(backend->bio_method); + backend->bio_method = NULL; } } @@ -2034,7 +2001,7 @@ static int ossl_shutdown(struct Curl_cfilter *cf, if(backend->handle) { buffsize = (int)sizeof(buf); while(!done && loop--) { - int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], + int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); if(what > 0) { ERR_clear_error(); @@ -2163,6 +2130,22 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, return FALSE; } +static CURLcode +ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert, const char *hostname, + const char *dispname); + +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert) +{ + const char *hostname, *dispname; + int port; + + (void)conn; + Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); + return ossl_verifyhost(data, conn, server_cert, hostname, dispname); +} + /* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST @@ -2185,8 +2168,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, This function is now used from ngtcp2 (QUIC) as well. */ -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) +static CURLcode +ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert, const char *hostname, + const char *dispname) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -2200,12 +2185,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, 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 */ - const char *hostname, *dispname; - int port; size_t hostlen; (void)conn; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); hostlen = strlen(hostname); #ifndef ENABLE_IPV6 @@ -2668,24 +2650,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, const void *buf, size_t len, SSL *ssl, void *userp) { - char unknown[32]; - const char *verstr = NULL; - struct connectdata *conn = userp; - int cf_idx = ossl_get_ssl_cf_index(); - struct ssl_connect_data *connssl; + const char *verstr = "???"; + struct Curl_cfilter *cf = userp; struct Curl_easy *data = NULL; - struct Curl_cfilter *cf; - - DEBUGASSERT(cf_idx >= 0); - cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx); - DEBUGASSERT(cf); - connssl = cf->ctx; - DEBUGASSERT(connssl); - DEBUGASSERT(connssl->backend); - data = connssl->backend->logger; + char unknown[32]; - if(!conn || !data || !data->set.fdebug || - (direction != 0 && direction != 1)) + if(!cf) + return; + data = CF_DATA_CURRENT(cf); + if(!data || !data->set.fdebug || (direction && direction != 1)) return; switch(ssl_ver) { @@ -2730,6 +2703,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, * For TLS 1.3, skip notification of the decrypted inner Content-Type. */ if(ssl_ver +#ifdef SSL3_RT_HEADER + && content_type != SSL3_RT_HEADER +#endif #ifdef SSL3_RT_INNER_CONTENT_TYPE && content_type != SSL3_RT_INNER_CONTENT_TYPE #endif @@ -2765,7 +2741,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, } txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), - CFMSG(cf, "%s (%s), %s, %s (%d):\n"), + "%s (%s), %s, %s (%d):\n", verstr, direction?"OUT":"IN", tls_rt_name, msg_name, msg_type); if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { @@ -2983,21 +2959,14 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) struct Curl_easy *data; struct Curl_cfilter *cf; const struct ssl_config_data *config; - curl_socket_t *sockindex_ptr; - int data_idx = ossl_get_ssl_data_index(); - int cf_idx = ossl_get_ssl_cf_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); + struct ssl_connect_data *connssl; bool isproxy; - if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0) - return 0; - - cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx); - data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); + 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 */ - sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx); - if(!cf || !data || !sockindex_ptr) + if(!cf || !data) return 0; isproxy = Curl_ssl_cf_is_proxy(cf); @@ -3091,7 +3060,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, BIO_free(cbio); /* if we didn't end up importing anything, treat that as an error */ - return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); + return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; } static CURLcode populate_x509_store(struct Curl_cfilter *cf, @@ -3110,206 +3079,219 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, const char * const ssl_crlfile = ssl_config->primary.CRLfile; const bool verifypeer = conn_config->verifypeer; bool imported_native_ca = false; + bool imported_ca_info_blob = false; if(!store) return CURLE_OUT_OF_MEMORY; + if(verifypeer) { #if defined(USE_WIN32_CRYPTO) - /* Import certificates from the Windows root certificate store if requested. - https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 - https://datatracker.ietf.org/doc/html/rfc5280 */ - if((conn_config->verifypeer || conn_config->verifyhost) && - (ssl_config->native_ca_store)) { - HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); - - if(hStore) { - PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and is - declared outside of the loop so that rather than malloc/free each - iteration we can grow it with realloc, when necessary. */ - CERT_ENHKEY_USAGE *enhkey_usage = NULL; - DWORD enhkey_usage_size = 0; - - /* This loop makes a best effort to import all valid certificates from - the MS root store. If a certificate cannot be imported it is skipped. - 'result' is used to store only hard-fail conditions (such as out of - memory) that cause an early break. */ - result = CURLE_OK; - for(;;) { - X509 *x509; - FILETIME now; - BYTE key_usage[2]; - DWORD req_size; - const unsigned char *encoded_cert; + /* Import certificates from the Windows root certificate store if + requested. + https://stackoverflow.com/questions/9507184/ + https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://datatracker.ietf.org/doc/html/rfc5280 */ + if(ssl_config->native_ca_store) { + HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); + + if(hStore) { + PCCERT_CONTEXT pContext = NULL; + /* The array of enhanced key usage OIDs will vary per certificate and + is declared outside of the loop so that rather than malloc/free each + iteration we can grow it with realloc, when necessary. */ + CERT_ENHKEY_USAGE *enhkey_usage = NULL; + DWORD enhkey_usage_size = 0; + + /* This loop makes a best effort to import all valid certificates from + the MS root store. If a certificate cannot be imported it is + skipped. 'result' is used to store only hard-fail conditions (such + as out of memory) that cause an early break. */ + result = CURLE_OK; + for(;;) { + X509 *x509; + FILETIME now; + BYTE key_usage[2]; + DWORD req_size; + const unsigned char *encoded_cert; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - char cert_name[256]; + char cert_name[256]; #endif - pContext = CertEnumCertificatesInStore(hStore, pContext); - if(!pContext) - break; + pContext = CertEnumCertificatesInStore(hStore, pContext); + if(!pContext) + break; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, cert_name, sizeof(cert_name))) { - strcpy(cert_name, "Unknown"); - } - infof(data, "SSL: Checking cert \"%s\"", cert_name); + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) { + strcpy(cert_name, "Unknown"); + } + infof(data, "SSL: Checking cert \"%s\"", cert_name); #endif + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; - encoded_cert = (const unsigned char *)pContext->pbCertEncoded; - if(!encoded_cert) - continue; - - GetSystemTimeAsFileTime(&now); - if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || - CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) - continue; - - /* If key usage exists check for signing attribute */ - if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, - pContext->pCertInfo, - key_usage, sizeof(key_usage))) { - if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) continue; - } - else if(GetLastError()) - continue; - - /* If enhanced key usage exists check for server auth attribute. - * - * Note "In a Microsoft environment, a certificate might also have EKU - * extended properties that specify valid uses for the certificate." - * The call below checks both, and behavior varies depending on what is - * found. For more details see CertGetEnhancedKeyUsage doc. - */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { - void *tmp = realloc(enhkey_usage, req_size); - - if(!tmp) { - failf(data, "SSL: Out of memory allocating for OID list"); - result = CURLE_OUT_OF_MEMORY; - break; - } - enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; - enhkey_usage_size = req_size; + /* If key usage exists check for signing attribute */ + if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, + pContext->pCertInfo, + key_usage, sizeof(key_usage))) { + if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; } + else if(GetLastError()) + continue; - if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { - if(!enhkey_usage->cUsageIdentifier) { - /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is - good for all uses. If it returns zero, the certificate has no - valid uses." */ - if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) - continue; + /* If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also have + * EKU extended properties that specify valid uses for the + * certificate." The call below checks both, and behavior varies + * depending on what is found. For more details see + * CertGetEnhancedKeyUsage doc. + */ + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { + if(req_size && req_size > enhkey_usage_size) { + void *tmp = realloc(enhkey_usage, req_size); + + if(!tmp) { + failf(data, "SSL: Out of memory allocating for OID list"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; } - else { - DWORD i; - bool found = false; - - for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { - if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, - enhkey_usage->rgpszUsageIdentifier[i])) { - found = true; - break; - } + + if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { + if(!enhkey_usage->cUsageIdentifier) { + /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate + is good for all uses. If it returns zero, the certificate + has no valid uses." */ + if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) + continue; } + else { + DWORD i; + bool found = false; + + for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { + if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, + enhkey_usage->rgpszUsageIdentifier[i])) { + found = true; + break; + } + } - if(!found) - continue; + if(!found) + continue; + } } + else + continue; } else continue; - } - else - continue; - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if(!x509) - continue; + x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if(!x509) + continue; - /* Try to import the certificate. This may fail for legitimate reasons - such as duplicate certificate, which is allowed by MS but not - OpenSSL. */ - if(X509_STORE_add_cert(store, x509) == 1) { + /* Try to import the certificate. This may fail for legitimate + reasons such as duplicate certificate, which is allowed by MS but + not OpenSSL. */ + if(X509_STORE_add_cert(store, x509) == 1) { #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); + infof(data, "SSL: Imported cert \"%s\"", cert_name); #endif - imported_native_ca = true; + imported_native_ca = true; + } + X509_free(x509); } - X509_free(x509); - } - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); - if(result) - return result; + if(result) + return result; + } + if(imported_native_ca) + infof(data, "successfully imported Windows CA store"); + else + infof(data, "error importing Windows CA store, continuing anyway"); } - if(imported_native_ca) - infof(data, "successfully imported Windows CA store"); - else - infof(data, "error importing Windows CA store, continuing anyway"); - } #endif - - if(ca_info_blob) { - result = load_cacert_from_memory(store, ca_info_blob); - if(result) { - if(result == CURLE_OUT_OF_MEMORY || - (verifypeer && !imported_native_ca)) { + if(ca_info_blob) { + result = load_cacert_from_memory(store, ca_info_blob); + if(result) { failf(data, "error importing CA certificate blob"); return result; } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); + else { + imported_ca_info_blob = true; + infof(data, "successfully imported CA certificate blob"); + } } - } - if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { + if(ssl_cafile || ssl_capath) { #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) - /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - if(ssl_cafile && - !X509_STORE_load_file(store, ssl_cafile)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; - } - if(ssl_capath && - !X509_STORE_load_path(store, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate path: %s", ssl_capath); - return CURLE_SSL_CACERT_BADFILE; - } + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate file, continuing anyway"); + } + if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate path, continuing anyway"); + } #else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ + if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + infof(data, "error setting certificate verify locations," + " continuing anyway"); + } + } #endif - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } #ifdef CURL_CA_FALLBACK - if(verifypeer && - !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { - /* verifying the peer without any CA certificates won't - work so use openssl's built-in default as fallback */ - X509_STORE_set_default_paths(store); - } + if(!ssl_cafile && !ssl_capath && + !imported_native_ca && !imported_ca_info_blob) { + /* verifying the peer without any CA certificates won't + work so use openssl's built-in default as fallback */ + X509_STORE_set_default_paths(store); + } #endif + } if(ssl_crlfile) { /* tell OpenSSL where to find CRL file that is used to check certificate @@ -3440,9 +3422,9 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, } } -static CURLcode set_up_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_backend_data *backend) +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_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); @@ -3462,10 +3444,10 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf, cached_store = get_cached_x509_store(cf, data); if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { - SSL_CTX_set_cert_store(backend->ctx, cached_store); + SSL_CTX_set_cert_store(ssl_ctx, cached_store); } else { - X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); result = populate_x509_store(cf, data, store); if(result == CURLE_OK && cache_criteria_met) { @@ -3476,11 +3458,11 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf, return result; } #else /* HAVE_SSL_X509_STORE_SHARE */ -static CURLcode set_up_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_backend_data *backend) +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx) { - X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); return populate_x509_store(cf, data, store); } @@ -3510,9 +3492,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #endif #endif const long int ssl_version = conn_config->version; -#ifdef USE_OPENSSL_SRP - const enum CURL_TLSAUTH ssl_authtype = ssl_config->primary.authtype; -#endif 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; @@ -3580,8 +3559,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, 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->conn); - set_logger(connssl, data); + SSL_CTX_set_msg_callback_arg(backend->ctx, cf); } #endif @@ -3677,36 +3655,17 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_CTX_set_options(backend->ctx, ctx_options); #ifdef HAS_ALPN - if(cf->conn->bits.tls_enable_alpn) { - int cur = 0; - unsigned char protocols[128]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur++] = ALPN_H2_LENGTH; - - memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - protocols[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + if(connssl->alpn) { + struct alpn_proto_buf proto; - /* expects length prefixed preference ordered list of protocols in wire - * format - */ - if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result || + SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.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 @@ -3764,8 +3723,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #endif #ifdef USE_OPENSSL_SRP - if((ssl_authtype == CURL_TLSAUTH_SRP) && - Curl_auth_allowed_to_host(data)) { + if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { char * const ssl_username = ssl_config->primary.username; char * const ssl_password = ssl_config->primary.password; infof(data, "Using TLS-SRP username: %s", ssl_username); @@ -3789,10 +3747,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif - result = set_up_x509_store(cf, data, backend); - if(result) - return result; - /* 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 @@ -3836,6 +3790,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } + SSL_set_app_data(backend->handle, cf); + #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus) @@ -3863,13 +3819,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif - if(!ossl_attach_data(cf, data)) { - /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */ - failf(data, "SSL: ossl_attach_data failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } + SSL_set_app_data(backend->handle, cf); if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); @@ -3888,13 +3838,26 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_unlock(data); } - bio = BIO_new(bio_cf_method); + backend->bio_method = bio_cf_method_create(); + if(!backend->bio_method) + return CURLE_OUT_OF_MEMORY; + bio = BIO_new(backend->bio_method); if(!bio) return CURLE_OUT_OF_MEMORY; BIO_set_data(bio, cf); +#ifdef HAVE_SSL_SET0_WBIO + /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works + * without backward compat quirks. Every call takes one reference, so we + * up it and pass. SSL* then owns it and will free. + * 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); +#else SSL_set_bio(backend->handle, bio, bio); - +#endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; @@ -3915,6 +3878,16 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ERR_clear_error(); err = SSL_connect(backend->handle); + + if(!backend->x509_store_setup) { + /* After having send off the ClientHello, we prepare the x509 + * store to verify the coming certificate from the server */ + CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(result) + return result; + backend->x509_store_setup = TRUE; + } + #ifndef HAVE_KEYLOG_CALLBACK if(Curl_tls_keylog_enabled()) { /* If key logging is enabled, wait for the handshake to complete and then @@ -3949,7 +3922,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } else { /* untreated error */ - unsigned long errdetail; + sslerr_t errdetail; char error_buffer[256]=""; CURLcode result; long lerr; @@ -4043,26 +4016,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, const unsigned char *neg_protocol; unsigned int len; SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); - if(len) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol); - -#ifdef USE_HTTP2 - if(len == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, neg_protocol, len)) { - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(len == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, VTLS_INFOF_NO_ALPN); - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); } #endif @@ -4205,7 +4160,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = Curl_ossl_verifyhost(data, conn, backend->server_cert); + result = ossl_verifyhost(data, conn, backend->server_cert, + connssl->hostname, connssl->dispname); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4379,7 +4335,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int what; /* check if the connection has already been established */ @@ -4418,8 +4374,9 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, } /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { + if(!nonblocking && + (connssl->connecting_state == ssl_connect_2_reading || + connssl->connecting_state == ssl_connect_2_writing)) { curl_socket_t writefd = ssl_connect_2_writing == connssl->connecting_state?sockfd:CURL_SOCKET_BAD; @@ -4427,7 +4384,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); + timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -4435,11 +4392,6 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, goto out; } if(0 == what) { - if(nonblocking) { - *done = FALSE; - result = CURLE_OK; - goto out; - } /* timeout */ failf(data, "SSL connection timeout"); result = CURLE_OPERATION_TIMEDOUT; @@ -4527,7 +4479,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, 'size_t' */ int err; char error_buffer[256]; - unsigned long sslerror; + sslerr_t sslerror; int memlen; int rc; struct ssl_connect_data *connssl = cf->ctx; @@ -4539,7 +4491,6 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - set_logger(connssl, data); rc = SSL_write(backend->handle, mem, memlen); if(rc <= 0) { @@ -4636,7 +4587,6 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - set_logger(connssl, data); nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); if(nread <= 0) { @@ -4854,89 +4804,6 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, (void *)backend->ctx : (void *)backend->handle; } -static bool ossl_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - const struct ssl_config_data *config; - - DEBUGASSERT(backend); - - /* If we don't have SSL context, do nothing. */ - if(!backend->handle) - return FALSE; - - config = Curl_ssl_cf_get_config(cf, data); - if(config->primary.sessionid) { - int data_idx = ossl_get_ssl_data_index(); - int cf_idx = ossl_get_ssl_cf_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - - if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 && - proxy_idx >= 0) { - int data_status, cf_status, sockindex_status, proxy_status; - - /* Store the data needed for the "new session" callback. - * The sockindex is stored as a pointer to an array element. */ - data_status = SSL_set_ex_data(backend->handle, data_idx, data); - cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf); - sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, - cf->conn->sock + cf->sockindex); -#ifndef CURL_DISABLE_PROXY - proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, - Curl_ssl_cf_is_proxy(cf)? - (void *) 1 : NULL); -#else - proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL); -#endif - if(data_status && cf_status && sockindex_status && proxy_status) - return TRUE; - } - return FALSE; - } - return TRUE; -} - -/* - * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after - * the handshake. If the transfer that sets up the callback gets killed before - * this callback arrives, we must make sure to properly clear the data to - * avoid UAF problems. A future optimization could be to instead store another - * transfer that might still be using the same connection. - */ - -static void ossl_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - - /* If we don't have SSL context, do nothing. */ - if(!backend->handle) - return; - - if(ssl_config->primary.sessionid) { - int data_idx = ossl_get_ssl_data_index(); - int cf_idx = ossl_get_ssl_cf_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - - if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 && - proxy_idx >= 0) { - /* Disable references to data in "new session" callback to avoid - * accessing a stale pointer. */ - SSL_set_ex_data(backend->handle, data_idx, NULL); - SSL_set_ex_data(backend->handle, cf_idx, NULL); - SSL_set_ex_data(backend->handle, sockindex_idx, NULL); - SSL_set_ex_data(backend->handle, proxy_idx, NULL); - } - } -} - static void ossl_free_multi_ssl_backend_data( struct multi_ssl_backend_data *mbackend) { @@ -4990,8 +4857,8 @@ const struct Curl_ssl Curl_ssl_openssl = { #else NULL, /* sha256sum */ #endif - ossl_attach_data, /* use of data in this connection */ - ossl_detach_data, /* remote of data from this connection */ + NULL, /* use of data in this connection */ + NULL, /* remote of data from this connection */ ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ ossl_recv, /* recv decrypted data */ ossl_send, /* send data to encrypt */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index 9df4ecd..950faab 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -56,5 +56,14 @@ CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl); +/** + * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and + * easy handle `data`. Will allow reuse of a shared cache if suitable + * and configured. + */ +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx); + #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 27f4ec8..003533d 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, + * Copyright (C) Jacob Hoffman-Andrews, * <github@hoffman-andrews.com> * * This software is licensed as described in the file COPYING, which @@ -150,6 +150,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, size_t plain_bytes_copied = 0; rustls_result rresult = 0; char errorbuf[255]; + size_t errorlen; rustls_io_result io_error; DEBUGASSERT(backend); @@ -161,7 +162,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, io_error = rustls_connection_read_tls(rconn, read_cb, &io_ctx, &tls_bytes_read); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - infof(data, CFMSG(cf, "cr_recv: EAGAIN or EWOULDBLOCK")); + DEBUGF(LOG_CF(data, cf, "cr_recv: EAGAIN or EWOULDBLOCK")); } else if(io_error) { char buffer[STRERROR_LEN]; @@ -171,12 +172,13 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return -1; } - infof(data, CFMSG(cf, "cr_recv: read %ld TLS bytes"), tls_bytes_read); + DEBUGF(LOG_CF(data, cf, "cr_recv: read %ld TLS bytes", tls_bytes_read)); rresult = rustls_connection_process_new_packets(rconn); if(rresult != RUSTLS_RESULT_OK) { - rustls_error(rresult, errorbuf, sizeof(errorbuf), &n); - failf(data, "%.*s", n, errorbuf); + rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_connection_process_new_packets: %.*s", + errorlen, errorbuf); *err = map_error(rresult); return -1; } @@ -189,14 +191,21 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, plainlen - plain_bytes_copied, &n); if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { - infof(data, CFMSG(cf, "cr_recv: got PLAINTEXT_EMPTY. " - "will try again later.")); + DEBUGF(LOG_CF(data, cf, "cr_recv: got PLAINTEXT_EMPTY. " + "will try again later.")); backend->data_pending = FALSE; break; } + else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { + failf(data, "rustls: peer closed TCP connection " + "without first closing TLS connection"); + *err = CURLE_READ_ERROR; + return -1; + } else if(rresult != RUSTLS_RESULT_OK) { /* n always equals 0 in this case, don't need to check it */ - failf(data, "error in rustls_connection_read: %d", rresult); + rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf); *err = CURLE_READ_ERROR; return -1; } @@ -207,7 +216,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, break; } else { - infof(data, CFMSG(cf, "cr_recv: got %ld plain bytes"), n); + DEBUGF(LOG_CF(data, cf, "cr_recv: got %ld plain bytes", n)); plain_bytes_copied += n; } } @@ -254,22 +263,25 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, size_t tlswritten_total = 0; rustls_result rresult; rustls_io_result io_error; + char errorbuf[256]; + size_t errorlen; DEBUGASSERT(backend); rconn = backend->conn; - infof(data, CFMSG(cf, "cr_send: %ld plain bytes"), plainlen); + DEBUGF(LOG_CF(data, cf, "cr_send: %ld plain bytes", plainlen)); if(plainlen > 0) { rresult = rustls_connection_write(rconn, plainbuf, plainlen, &plainwritten); if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_connection_write"); + rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf); *err = CURLE_WRITE_ERROR; return -1; } else if(plainwritten == 0) { - failf(data, "EOF in rustls_connection_write"); + failf(data, "rustls_connection_write: EOF"); *err = CURLE_WRITE_ERROR; return -1; } @@ -282,8 +294,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, &tlswritten); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - infof(data, CFMSG(cf, "cr_send: EAGAIN after %ld bytes"), - tlswritten_total); + DEBUGF(LOG_CF(data, cf, "cr_send: EAGAIN after %zu bytes", + tlswritten_total)); *err = CURLE_AGAIN; return -1; } @@ -299,7 +311,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, *err = CURLE_WRITE_ERROR; return -1; } - infof(data, CFMSG(cf, "cr_send: wrote %ld TLS bytes"), tlswritten); + DEBUGF(LOG_CF(data, cf, "cr_send: wrote %zu TLS bytes", tlswritten)); tlswritten_total += tlswritten; } @@ -349,22 +361,25 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, char errorbuf[256]; size_t errorlen; int result; - rustls_slice_bytes alpn[2] = { - { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH }, - { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, - }; DEBUGASSERT(backend); rconn = backend->conn; config_builder = rustls_client_config_builder_new(); -#ifdef USE_HTTP2 - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2); -#else - rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1); -#endif - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + if(connssl->alpn) { + struct alpn_proto_buf proto; + rustls_slice_bytes alpn[ALPN_ENTRIES_MAX]; + size_t i; + + for(i = 0; i < connssl->alpn->count; ++i) { + alpn[i].data = (const uint8_t *)connssl->alpn->entries[i]; + alpn[i].len = strlen(connssl->alpn->entries[i]); + } + rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, + connssl->alpn->count); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } if(!verifypeer) { rustls_client_config_builder_dangerous_set_certificate_verifier( config_builder, cr_verify_none); @@ -384,7 +399,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, ca_info_blob->len, verifypeer); if(result != RUSTLS_RESULT_OK) { - failf(data, "failed to parse trusted certificates from blob"); + 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)); @@ -394,7 +409,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_client_config_builder_use_roots(config_builder, roots); rustls_root_cert_store_free(roots); if(result != RUSTLS_RESULT_OK) { - failf(data, "failed to load trusted certificates"); + failf(data, "rustls: failed to load trusted certificates"); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); return CURLE_SSL_CACERT_BADFILE; @@ -404,7 +419,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_client_config_builder_load_roots_from_file( config_builder, ssl_cafile); if(result != RUSTLS_RESULT_OK) { - failf(data, "failed to load trusted certificates"); + failf(data, "rustls: failed to load trusted certificates"); rustls_client_config_free( rustls_client_config_builder_build(config_builder)); return CURLE_SSL_CACERT_BADFILE; @@ -416,7 +431,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, { char *snihost = Curl_ssl_snihost(data, hostname, NULL); if(!snihost) { - failf(data, "Failed to set SNI"); + failf(data, "rustls: failed to get SNI"); return CURLE_SSL_CONNECT_ERROR; } result = rustls_client_connection_new(backend->config, snihost, &rconn); @@ -439,29 +454,7 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, size_t len = 0; rustls_connection_get_alpn_protocol(rconn, &protocol, &len); - if(!protocol) { - infof(data, VTLS_INFOF_NO_ALPN); - return; - } - -#ifdef USE_HTTP2 - if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2); - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(len == ALPN_HTTP_1_1_LENGTH && - 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1); - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - else { - infof(data, "ALPN, negotiated an unrecognized protocol"); - } - - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + Curl_alpn_set_negotiated(cf, data, protocol, len); } static CURLcode @@ -469,7 +462,7 @@ cr_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct ssl_connect_data *const connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; @@ -573,7 +566,7 @@ cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) { struct ssl_connect_data *const connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); struct ssl_backend_data *const backend = connssl->backend; struct rustls_connection *rconn = NULL; @@ -616,7 +609,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) rustls_connection_send_close_notify(backend->conn); n = cr_send(cf, data, NULL, 0, &tmperr); if(n < 0) { - failf(data, "error sending close notify: %d", tmperr); + failf(data, "rustls: error sending close_notify: %d", tmperr); } rustls_connection_free(backend->conn); diff --git a/Utilities/cmcurl/lib/vtls/rustls.h b/Utilities/cmcurl/lib/vtls/rustls.h index 6b393dd..bfbe23d 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.h +++ b/Utilities/cmcurl/lib/vtls/rustls.h @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, + * Copyright (C) Jacob Hoffman-Andrews, * <github@hoffman-andrews.com> * * This software is licensed as described in the file COPYING, which diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 7eab954..452fa40 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -5,9 +5,9 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> - * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Marc Hoersken, <info@marc-hoersken.de> + * Copyright (C) Mark Salisbury, <mark.salisbury@hp.com> * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1105,7 +1105,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef HAS_ALPN /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. Also it doesn't seem to be supported for Wine, see curl bug #983. */ - backend->use_alpn = cf->conn->bits.tls_enable_alpn && + backend->use_alpn = connssl->alpn && !GetProcAddress(GetModuleHandle(TEXT("ntdll")), "wine_get_version") && curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, @@ -1196,6 +1196,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) int list_start_index = 0; unsigned int *extension_len = NULL; unsigned short* list_len = NULL; + struct alpn_proto_buf proto; /* The first four bytes will be an unsigned int indicating number of bytes of data in the rest of the buffer. */ @@ -1215,25 +1216,22 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) list_start_index = cur; -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) { - alpn_buffer[cur++] = ALPN_H2_LENGTH; - memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result) { + failf(data, "Error setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; } -#endif - - alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + memcpy(&alpn_buffer[cur], proto.data, proto.len); + cur += proto.len; *list_len = curlx_uitous(cur - list_start_index); *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short); InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); InitSecBufferDesc(&inbuf_desc, &inbuf, 1); + + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } else { InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); @@ -1727,40 +1725,23 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) if(alpn_result.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success) { - unsigned char alpn = 0; - - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, - alpn_result.ProtocolIdSize, alpn_result.ProtocolId); + unsigned char prev_alpn = cf->conn->alpn; -#ifdef USE_HTTP2 - if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) { - alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, - ALPN_HTTP_1_1_LENGTH)) { - alpn = CURL_HTTP_VERSION_1_1; - } + Curl_alpn_set_negotiated(cf, data, alpn_result.ProtocolId, + alpn_result.ProtocolIdSize); if(backend->recv_renegotiating) { - if(alpn != cf->conn->alpn) { + if(prev_alpn != cf->conn->alpn && + prev_alpn != CURL_HTTP_VERSION_NONE) { + /* Renegotiation selected a different protocol now, we cannot + * deal with this */ failf(data, "schannel: server selected an ALPN protocol too late"); return CURLE_SSL_CONNECT_ERROR; } } - else - cf->conn->alpn = alpn; } else { if(!backend->recv_renegotiating) - infof(data, VTLS_INFOF_NO_ALPN); - } - - if(!backend->recv_renegotiating) { - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + Curl_alpn_set_negotiated(cf, data, NULL, 0); } } #endif @@ -1841,7 +1822,7 @@ schannel_connect_common(struct Curl_cfilter *cf, { CURLcode result; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); timediff_t timeout_ms; int what; @@ -2056,7 +2037,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - what = SOCKET_WRITABLE(cf->conn->sock[cf->sockindex], timeout_ms); + what = SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h index 6d4235a..7fae39f 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.h +++ b/Utilities/cmcurl/lib/vtls/schannel.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al. - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>, 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 @@ -179,8 +179,8 @@ struct ssl_backend_data { size_t encdata_offset, decdata_offset; unsigned char *encdata_buffer, *decdata_buffer; /* encdata_is_incomplete: if encdata contains only a partial record that - can't be decrypted without another Curl_read_plain (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes + can't be decrypted without another recv() (that is, status is + SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds more bytes into encdata then set this back to false. */ bool encdata_is_incomplete; unsigned long req_flags, ret_flags; diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index e499216..d75ee8d 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -5,9 +5,9 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> - * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Marc Hoersken, <info@marc-hoersken.de> + * Copyright (C) Mark Salisbury, <mark.salisbury@hp.com> + * 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/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index d903c53..2e98169 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -5,8 +5,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>. + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -137,14 +137,6 @@ #include "memdebug.h" -#define DEBUG_CF 0 - -#if DEBUG_CF -#define CF_DEBUGF(x) x -#else -#define CF_DEBUGF(x) do { } while(0) -#endif - /* From MacTypes.h (which we can't include because it isn't present in iOS: */ #define ioErr -36 #define paramErr -50 @@ -840,15 +832,15 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result; OSStatus rtn = noErr; DEBUGASSERT(data); nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); - CF_DEBUGF(infof(data, CFMSG(cf, "bio_read(len=%zu) -> %zd, result=%d"), - *dataLength, nread, result)); + DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", + *dataLength, nread, result)); if(nread < 0) { switch(result) { case CURLE_OK: @@ -876,15 +868,15 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection, struct Curl_cfilter *cf = (struct Curl_cfilter *)connection; struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result; OSStatus rtn = noErr; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); - CF_DEBUGF(infof(data, CFMSG(cf, "bio_send(len=%zu) -> %zd, result=%d"), - *dataLength, nwritten, result)); + DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", + *dataLength, nwritten, result)); if(nwritten <= 0) { if(result == CURLE_AGAIN) { rtn = errSSLWouldBlock; @@ -1644,7 +1636,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, const bool verifypeer = conn_config->verifypeer; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; - bool isproxy = Curl_ssl_cf_is_proxy(cf); #ifdef ENABLE_IPV6 struct in6_addr addr; #else @@ -1657,7 +1648,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); - CF_DEBUGF(infof(data, CFMSG(cf, "connect_step1"))); + DEBUGF(LOG_CF(data, cf, "connect_step1")); GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -1805,33 +1796,28 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(cf->conn->bits.tls_enable_alpn) { + if(connssl->alpn) { if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { + struct alpn_proto_buf proto; + size_t i; + CFStringRef cstr; CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!isproxy || !cf->conn->bits.tunnel_proxy) -#endif - ) { - CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2)); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); + for(i = 0; i < connssl->alpn->count; ++i) { + cstr = CFStringCreateWithCString(NULL, connssl->alpn->entries[i], + kCFStringEncodingUTF8); + if(!cstr) + return CURLE_OUT_OF_MEMORY; + CFArrayAppendValue(alpnArr, cstr); + CFRelease(cstr); } -#endif - - CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1)); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - /* expects length prefixed preference ordered list of protocols in wire - * format - */ err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); if(err != noErr) infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d", err); CFRelease(alpnArr); + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } } #endif @@ -2302,7 +2288,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, /* This is not a PEM file, probably a certificate in DER format. */ rc = append_cert_to_array(data, certbuf, buflen, array); if(rc != CURLE_OK) { - CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed"))); + DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); result = rc; goto out; } @@ -2316,7 +2302,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, rc = append_cert_to_array(data, der, derlen, array); free(der); if(rc != CURLE_OK) { - CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed"))); + DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); result = rc; goto out; } @@ -2332,7 +2318,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, goto out; } - CF_DEBUGF(infof(data, CFMSG(cf, "setting %d trust anchors"), n)); + DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n)); ret = SecTrustSetAnchorCertificates(trust, array); if(ret != noErr) { failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); @@ -2354,11 +2340,11 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, switch(trust_eval) { case kSecTrustResultUnspecified: /* what does this really mean? */ - CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Unspecified"))); + DEBUGF(LOG_CF(data, cf, "trust result: Unspecified")); result = CURLE_OK; goto out; case kSecTrustResultProceed: - CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Proceed"))); + DEBUGF(LOG_CF(data, cf, "trust result: Proceed")); result = CURLE_OK; goto out; @@ -2391,7 +2377,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, size_t buflen; if(ca_info_blob) { - CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from config blob"))); + DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob")); certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); if(!certbuf) { return CURLE_OUT_OF_MEMORY; @@ -2401,8 +2387,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, certbuf[ca_info_blob->len]='\0'; } else if(cafile) { - CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from file '%s'"), - cafile)); + DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile)); if(read_cert(cafile, &certbuf, &buflen) < 0) { failf(data, "SSL: failed to read or invalid CA certificate"); return CURLE_SSL_CACERT_BADFILE; @@ -2538,7 +2523,7 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); DEBUGASSERT(backend); - CF_DEBUGF(infof(data, CFMSG(cf, "connect_step2"))); + DEBUGF(LOG_CF(data, cf, "connect_step2")); /* Here goes nothing: */ check_handshake: @@ -3003,7 +2988,7 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; - CF_DEBUGF(infof(data, CFMSG(cf, "connect_step3"))); + DEBUGF(LOG_CF(data, cf, "connect_step3")); /* There is no step 3! * Well, okay, let's collect server certificates, and if verbose mode is on, * let's print the details of the server certificates. */ @@ -3022,7 +3007,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, { CURLcode result; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int what; /* check if the connection has already been established */ @@ -3112,7 +3097,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } if(ssl_connect_done == connssl->connecting_state) { - CF_DEBUGF(infof(data, CFMSG(cf, "connected"))); + DEBUGF(LOG_CF(data, cf, "connected")); connssl->state = ssl_connection_complete; *done = TRUE; } @@ -3158,7 +3143,7 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); if(backend->ssl_ctx) { - CF_DEBUGF(infof(data, CFMSG(cf, "close"))); + DEBUGF(LOG_CF(data, cf, "close")); (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext) @@ -3200,9 +3185,10 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, rc = 0; - what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], SSL_SHUTDOWN_TIMEOUT); + what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), + SSL_SHUTDOWN_TIMEOUT); - CF_DEBUGF(infof(data, CFMSG(cf, "shutdown"))); + DEBUGF(LOG_CF(data, cf, "shutdown")); while(loop--) { if(what < 0) { /* anything that gets here is fatally bad */ @@ -3229,7 +3215,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, if(nread <= 0) break; - what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0); + what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); } return rc; @@ -3271,7 +3257,7 @@ static int sectransp_check_cxn(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ssl_ctx) { - CF_DEBUGF(infof(data, CFMSG(cf, "check connection"))); + DEBUGF(LOG_CF(data, cf, "check connection")); err = SSLGetSessionState(backend->ssl_ctx, &state); if(err == noErr) return state == kSSLConnected || state == kSSLHandshake; @@ -3292,7 +3278,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ssl_ctx) { /* SSL is in use */ - CF_DEBUGF(infof(data, CFMSG(cf, "data_pending"))); + DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; diff --git a/Utilities/cmcurl/lib/vtls/sectransp.h b/Utilities/cmcurl/lib/vtls/sectransp.h index 2d53b7c..0f1085a 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.h +++ b/Utilities/cmcurl/lib/vtls/sectransp.h @@ -7,8 +7,8 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>. - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>. + * 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/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index 873ee6b..15f6844 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -73,6 +73,7 @@ #include "curl_memory.h" #include "memdebug.h" + /* convenience macro to check if this handle is using a shared SSL session */ #define SSLSESSION_SHARED(data) (data->share && \ (data->share->specifier & \ @@ -150,7 +151,6 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, #ifdef USE_TLS_SRP !Curl_timestrcmp(data->username, needle->username) && !Curl_timestrcmp(data->password, needle->password) && - (data->authtype == needle->authtype) && #endif strcasecompare(data->cipher_list, needle->cipher_list) && strcasecompare(data->cipher_list13, needle->cipher_list13) && @@ -173,9 +173,6 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, dest->verifystatus = source->verifystatus; dest->sessionid = source->sessionid; dest->ssl_options = source->ssl_options; -#ifdef USE_TLS_SRP - dest->authtype = source->authtype; -#endif CLONE_BLOB(cert_blob); CLONE_BLOB(ca_info_blob); @@ -272,8 +269,8 @@ void Curl_ssl_cleanup(void) static bool ssl_prefs_check(struct Curl_easy *data) { /* check for CURLOPT_SSLVERSION invalid parameter value */ - const long sslver = data->set.ssl.primary.version; - if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) { + const unsigned char sslver = data->set.ssl.primary.version; + if(sslver >= CURL_SSLVERSION_LAST) { failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION"); return FALSE; } @@ -293,7 +290,8 @@ static bool ssl_prefs_check(struct Curl_easy *data) return TRUE; } -static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data) +static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, + const struct alpn_spec *alpn) { struct ssl_connect_data *ctx; @@ -302,6 +300,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data) if(!ctx) return NULL; + ctx->alpn = alpn; ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); if(!ctx->backend) { free(ctx); @@ -318,13 +317,6 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) } } -static void cf_ctx_set_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - if(cf->ctx) - ((struct ssl_connect_data *)cf->ctx)->call_data = data; -} - static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; @@ -339,7 +331,6 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) result = Curl_ssl->connect_blocking(cf, data); if(!result) { - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ DEBUGASSERT(connssl->state == ssl_connection_complete); } @@ -615,19 +606,20 @@ int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) { struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - (void)data; - if(connssl->connecting_state == ssl_connect_2_writing) { - /* write mode */ - socks[0] = cf->conn->sock[FIRSTSOCKET]; - return GETSOCK_WRITESOCK(0); - } - if(connssl->connecting_state == ssl_connect_2_reading) { - /* read mode */ - socks[0] = cf->conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0); + if(sock != CURL_SOCKET_BAD) { + if(connssl->connecting_state == ssl_connect_2_writing) { + /* write mode */ + socks[0] = sock; + return GETSOCK_WRITESOCK(0); + } + if(connssl->connecting_state == ssl_connect_2_reading) { + /* read mode */ + socks[0] = sock; + return GETSOCK_READSOCK(0); + } } - return GETSOCK_BLANK; } @@ -685,20 +677,6 @@ void Curl_ssl_version(char *buffer, size_t size) #endif } -/* - * This function tries to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn) -{ - struct Curl_cfilter *cf = Curl_ssl_cf_get_ssl(conn->cfilter[FIRSTSOCKET]); - return cf? Curl_ssl->check_cxn(cf, data) : -1; -} - void Curl_ssl_free_certinfo(struct Curl_easy *data) { struct curl_certinfo *ci = &data->info.certs; @@ -1430,53 +1408,80 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, #ifdef USE_SSL +static void free_hostname(struct ssl_connect_data *connssl) +{ + if(connssl->dispname != connssl->hostname) + free(connssl->dispname); + free(connssl->hostname); + connssl->hostname = connssl->dispname = NULL; +} + static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - /* TODO: close_one closes BOTH conn->ssl AND conn->proxy_ssl for this - * sockindex (if in use). Gladly, it is safe to call more than once. */ if(connssl) { Curl_ssl->close(cf, data); connssl->state = ssl_connection_none; + free_hostname(connssl); } cf->connected = FALSE; } -static void reinit_hostname(struct Curl_cfilter *cf) +static CURLcode reinit_hostname(struct Curl_cfilter *cf) { struct ssl_connect_data *connssl = cf->ctx; + const char *ehostname, *edispname; + int eport; + /* We need the hostname for SNI negotiation. Once handshaked, this + * remains the SNI hostname for the TLS connection. But when the + * connection is reused, the settings in cf->conn might change. + * So we keep a copy of the hostname we use for SNI. + */ #ifndef CURL_DISABLE_PROXY if(Curl_ssl_cf_is_proxy(cf)) { - /* TODO: there is not definition for a proxy setup on a secondary conn */ - connssl->hostname = cf->conn->http_proxy.host.name; - connssl->dispname = cf->conn->http_proxy.host.dispname; - connssl->port = cf->conn->http_proxy.port; + ehostname = cf->conn->http_proxy.host.name; + edispname = cf->conn->http_proxy.host.dispname; + eport = cf->conn->http_proxy.port; } else #endif { - /* TODO: secondaryhostname is set to the IP address we connect to - * in the FTP handler, it is assumed that host verification uses the - * hostname from FIRSTSOCKET */ - if(cf->sockindex == SECONDARYSOCKET && 0) { - connssl->hostname = cf->conn->secondaryhostname; - connssl->dispname = connssl->hostname; - connssl->port = cf->conn->secondary_port; + ehostname = cf->conn->host.name; + edispname = cf->conn->host.dispname; + eport = cf->conn->remote_port; + } + + /* change if ehostname changed */ + if(ehostname && (!connssl->hostname + || strcmp(ehostname, connssl->hostname))) { + free_hostname(connssl); + connssl->hostname = strdup(ehostname); + if(!connssl->hostname) { + free_hostname(connssl); + return CURLE_OUT_OF_MEMORY; } + if(!edispname || !strcmp(ehostname, edispname)) + connssl->dispname = connssl->hostname; else { - connssl->hostname = cf->conn->host.name; - connssl->dispname = cf->conn->host.dispname; - connssl->port = cf->conn->remote_port; + connssl->dispname = strdup(edispname); + if(!connssl->dispname) { + free_hostname(connssl); + return CURLE_OUT_OF_MEMORY; + } } } - DEBUGASSERT(connssl->hostname); + connssl->port = eport; + return CURLE_OK; } static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - cf_ctx_set_data(cf, data); + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); cf_close(cf, data); + CF_DATA_RESTORE(cf, save); cf_ctx_free(cf->ctx); cf->ctx = NULL; } @@ -1484,10 +1489,12 @@ static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) static void ssl_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - cf_ctx_set_data(cf, data); + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); cf_close(cf, data); cf->next->cft->close(cf->next, data); - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); } static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, @@ -1495,6 +1502,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, bool blocking, bool *done) { struct ssl_connect_data *connssl = cf->ctx; + struct cf_call_data save; CURLcode result; if(cf->connected) { @@ -1502,7 +1510,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - cf_ctx_set_data(cf, data); + CF_DATA_SAVE(save, cf, data); (void)connssl; DEBUGASSERT(data->conn); DEBUGASSERT(data->conn == cf->conn); @@ -1513,10 +1521,10 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, if(result || !*done) goto out; - /* TODO: right now we do not fully control when hostname is set, - * assign it on each connect call. */ - reinit_hostname(cf); *done = FALSE; + result = reinit_hostname(cf); + if(result) + goto out; if(blocking) { result = ssl_connect(cf, data); @@ -1528,26 +1536,26 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, if(!result && *done) { cf->connected = TRUE; - if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf)) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ + connssl->handshake_done = Curl_now(); DEBUGASSERT(connssl->state == ssl_connection_complete); } out: - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); return result; } static bool ssl_cf_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { + struct cf_call_data save; bool result; - cf_ctx_set_data(cf, (struct Curl_easy *)data); - if(cf->ctx && Curl_ssl->data_pending(cf, data)) + CF_DATA_SAVE(save, cf, data); + if(Curl_ssl->data_pending(cf, data)) result = TRUE; else result = cf->next->cft->has_data_pending(cf->next, data); - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); return result; } @@ -1555,12 +1563,13 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err) { + struct cf_call_data save; ssize_t nwritten; + CF_DATA_SAVE(save, cf, data); *err = CURLE_OK; - cf_ctx_set_data(cf, data); nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); return nwritten; } @@ -1568,12 +1577,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { + struct cf_call_data save; ssize_t nread; + CF_DATA_SAVE(save, cf, data); *err = CURLE_OK; - cf_ctx_set_data(cf, data); nread = Curl_ssl->recv_plain(cf, data, buf, len, err); - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); return nread; } @@ -1581,39 +1591,72 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *socks) { + struct cf_call_data save; int result; - cf_ctx_set_data(cf, data); + CF_DATA_SAVE(save, cf, data); result = Curl_ssl->get_select_socks(cf, data, socks); - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); return result; } -static void ssl_cf_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) { - if(Curl_ssl->attach_data) { - cf_ctx_set_data(cf, data); - Curl_ssl->attach_data(cf, data); - cf_ctx_set_data(cf, NULL); + struct ssl_connect_data *connssl = cf->ctx; + struct cf_call_data save; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_CONN_REPORT_STATS: + if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done); + break; + case CF_CTRL_DATA_ATTACH: + if(Curl_ssl->attach_data) { + CF_DATA_SAVE(save, cf, data); + Curl_ssl->attach_data(cf, data); + CF_DATA_RESTORE(cf, save); + } + break; + case CF_CTRL_DATA_DETACH: + if(Curl_ssl->detach_data) { + CF_DATA_SAVE(save, cf, data); + Curl_ssl->detach_data(cf, data); + CF_DATA_RESTORE(cf, save); + } + break; + default: + break; } + return CURLE_OK; } -static void ssl_cf_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) +static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data) { - if(Curl_ssl->detach_data) { - cf_ctx_set_data(cf, data); - Curl_ssl->detach_data(cf, data); - cf_ctx_set_data(cf, NULL); - } + struct cf_call_data save; + bool result; + /* + * This function tries to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ + CF_DATA_SAVE(save, cf, data); + result = Curl_ssl->check_cxn(cf, data) != 0; + CF_DATA_RESTORE(cf, save); + return result; } -static const struct Curl_cftype cft_ssl = { +struct Curl_cftype Curl_cft_ssl = { "SSL", CF_TYPE_SSL, + CURL_LOG_DEFAULT, ssl_cf_destroy, - Curl_cf_def_setup, ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, @@ -1621,15 +1664,17 @@ static const struct Curl_cftype cft_ssl = { ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, - ssl_cf_attach_data, - ssl_cf_detach_data, + ssl_cf_cntrl, + cf_ssl_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, }; -static const struct Curl_cftype cft_ssl_proxy = { +struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", CF_TYPE_SSL, + CURL_LOG_DEFAULT, ssl_cf_destroy, - Curl_cf_def_setup, ssl_cf_connect, ssl_cf_close, Curl_cf_def_get_host, @@ -1637,65 +1682,107 @@ static const struct Curl_cftype cft_ssl_proxy = { ssl_cf_data_pending, ssl_cf_send, ssl_cf_recv, - ssl_cf_attach_data, - ssl_cf_detach_data, + ssl_cf_cntrl, + cf_ssl_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, }; -CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) { - struct Curl_cfilter *cf; + struct Curl_cfilter *cf = NULL; struct ssl_connect_data *ctx; CURLcode result; DEBUGASSERT(data->conn); - ctx = cf_ctx_new(data); + + ctx = cf_ctx_new(data, Curl_alpn_get_spec(data, conn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = Curl_cf_create(&cf, &cft_ssl, ctx); - if(result) - goto out; - - Curl_conn_cf_add(data, conn, sockindex, cf); - - result = CURLE_OK; + result = Curl_cf_create(&cf, &Curl_cft_ssl, ctx); out: if(result) cf_ctx_free(ctx); + *pcf = result? NULL : cf; return result; } -#ifndef CURL_DISABLE_PROXY -CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) { struct Curl_cfilter *cf; + CURLcode result; + + result = cf_ssl_create(&cf, data, conn); + if(!result) + Curl_conn_cf_add(data, conn, sockindex, cf); + return result; +} + +CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_ssl_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#ifndef CURL_DISABLE_PROXY +static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = NULL; struct ssl_connect_data *ctx; CURLcode result; - ctx = cf_ctx_new(data); + ctx = cf_ctx_new(data, Curl_alpn_get_proxy_spec(data, conn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } + result = Curl_cf_create(&cf, &Curl_cft_ssl_proxy, ctx); - result = Curl_cf_create(&cf, &cft_ssl_proxy, ctx); +out: if(result) - goto out; + cf_ctx_free(ctx); + *pcf = result? NULL : cf; + return result; +} - Curl_conn_cf_add(data, conn, sockindex, cf); +CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) +{ + struct Curl_cfilter *cf; + CURLcode result; - result = CURLE_OK; + result = cf_ssl_proxy_create(&cf, data, conn); + if(!result) + Curl_conn_cf_add(data, conn, sockindex, cf); + return result; +} -out: - if(result) - cf_ctx_free(ctx); +CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_ssl_proxy_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); return result; } @@ -1717,9 +1804,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, /* get first filter in chain, if any is present */ cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]); if(cf) { - cf_ctx_set_data(cf, data); + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); result = Curl_ssl->get_internals(cf->ctx, info); - cf_ctx_set_data(cf, NULL); + CF_DATA_RESTORE(cf, save); } } return result; @@ -1733,7 +1821,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, (void)data; for(; cf; cf = cf->next) { - if(cf->cft == &cft_ssl) { + if(cf->cft == &Curl_cft_ssl) { if(Curl_ssl->shut_down(cf, data)) result = CURLE_SSL_SHUTDOWN_FAILED; Curl_conn_cf_discard(cf, data); @@ -1749,7 +1837,7 @@ static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn, struct Curl_cfilter *cf, *lowest_ssl_cf = NULL; for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) { - if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy) { + if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) { lowest_ssl_cf = cf; if(cf->connected || (cf->next && cf->next->connected)) { /* connected or about to start */ @@ -1762,7 +1850,7 @@ static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn, bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) { - return (cf->cft == &cft_ssl_proxy); + return (cf->cft == &Curl_cft_ssl_proxy); } struct ssl_config_data * @@ -1814,10 +1902,142 @@ Curl_ssl_get_primary_config(struct Curl_easy *data, struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf) { for(; cf; cf = cf->next) { - if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy) + if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) return cf; } return NULL; } +static const struct alpn_spec ALPN_SPEC_H10 = { + { ALPN_HTTP_1_0 }, 1 +}; +static const struct alpn_spec ALPN_SPEC_H11 = { + { ALPN_HTTP_1_1 }, 1 +}; +#ifdef USE_HTTP2 +static const struct alpn_spec ALPN_SPEC_H2_H11 = { + { ALPN_H2, ALPN_HTTP_1_1 }, 2 +}; +#endif + +const struct alpn_spec * +Curl_alpn_get_spec(struct Curl_easy *data, struct connectdata *conn) +{ + if(!conn->bits.tls_enable_alpn) + return NULL; + if(data->state.httpwant == CURL_HTTP_VERSION_1_0) + return &ALPN_SPEC_H10; +#ifdef USE_HTTP2 + if(data->state.httpwant >= CURL_HTTP_VERSION_2) + return &ALPN_SPEC_H2_H11; +#endif + return &ALPN_SPEC_H11; +} + +const struct alpn_spec * +Curl_alpn_get_proxy_spec(struct Curl_easy *data, struct connectdata *conn) +{ + if(!conn->bits.tls_enable_alpn) + return NULL; + if(data->state.httpwant == CURL_HTTP_VERSION_1_0) + return &ALPN_SPEC_H10; + return &ALPN_SPEC_H11; +} + +CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, + const struct alpn_spec *spec) +{ + size_t i, len; + int off = 0; + unsigned char blen; + + memset(buf, 0, sizeof(*buf)); + for(i = 0; spec && i < spec->count; ++i) { + len = strlen(spec->entries[i]); + if(len >= ALPN_NAME_MAX) + return CURLE_FAILED_INIT; + blen = (unsigned char)len; + if(off + blen + 1 >= (int)sizeof(buf->data)) + return CURLE_FAILED_INIT; + buf->data[off++] = blen; + memcpy(buf->data + off, spec->entries[i], blen); + off += blen; + } + buf->len = off; + return CURLE_OK; +} + +CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, + const struct alpn_spec *spec) +{ + size_t i, len; + size_t off = 0; + + memset(buf, 0, sizeof(*buf)); + for(i = 0; spec && i < spec->count; ++i) { + len = strlen(spec->entries[i]); + if(len >= ALPN_NAME_MAX) + return CURLE_FAILED_INIT; + if(off + len + 2 >= (int)sizeof(buf->data)) + return CURLE_FAILED_INIT; + if(off) + buf->data[off++] = ','; + memcpy(buf->data + off, spec->entries[i], len); + off += len; + } + buf->data[off] = '\0'; + buf->len = (int)off; + return CURLE_OK; +} + +CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *proto, + size_t proto_len) +{ + int can_multi = 0; + + if(proto && proto_len) { + if(proto_len == ALPN_HTTP_1_1_LENGTH && + !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { + cf->conn->alpn = CURL_HTTP_VERSION_1_1; + } + else if(proto_len == ALPN_HTTP_1_0_LENGTH && + !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) { + cf->conn->alpn = CURL_HTTP_VERSION_1_0; + } +#ifdef USE_HTTP2 + else if(proto_len == ALPN_H2_LENGTH && + !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) { + cf->conn->alpn = CURL_HTTP_VERSION_2; + can_multi = 1; + } +#endif +#ifdef USE_HTTP3 + else if(proto_len == ALPN_H3_LENGTH && + !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) { + cf->conn->alpn = CURL_HTTP_VERSION_3; + can_multi = 1; + } +#endif + else { + cf->conn->alpn = CURL_HTTP_VERSION_NONE; + failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto); + /* TODO: do we want to fail this? Previous code just ignored it and + * some vtls backends even ignore the return code of this function. */ + /* return CURLE_NOT_BUILT_IN; */ + goto out; + } + infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto); + } + else { + cf->conn->alpn = CURL_HTTP_VERSION_NONE; + infof(data, VTLS_INFOF_NO_ALPN); + } + +out: + Curl_multiuse_state(data, can_multi? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + return CURLE_OK; +} + #endif /* USE_SSL */ diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 5ad64fc..0d9e74a 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -27,7 +27,6 @@ struct connectdata; struct ssl_config_data; -struct ssl_connect_data; struct ssl_primary_config; struct Curl_ssl_session; @@ -53,6 +52,7 @@ struct Curl_ssl_session; /* Curl_multi SSL backend-specific data; declared differently by each SSL backend */ struct multi_ssl_backend_data; +struct Curl_cfilter; CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, const curl_ssl_backend ***avail); @@ -68,8 +68,53 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" +#define ALPN_HTTP_1_0_LENGTH 8 +#define ALPN_HTTP_1_0 "http/1.0" #define ALPN_H2_LENGTH 2 #define ALPN_H2 "h2" +#define ALPN_H3_LENGTH 2 +#define ALPN_H3 "h3" + +/* conservative sizes on the ALPN entries and count we are handling, + * we can increase these if we ever feel the need or have to accommodate + * ALPN strings from the "outside". */ +#define ALPN_NAME_MAX 10 +#define ALPN_ENTRIES_MAX 3 +#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) + +struct alpn_spec { + const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX]; + size_t count; /* number of entries */ +}; + +struct alpn_proto_buf { + unsigned char data[ALPN_PROTO_BUF_MAX]; + int len; +}; + +CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, + const struct alpn_spec *spec); +CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, + const struct alpn_spec *spec); + +CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *proto, + size_t proto_len); + +/** + * Get the ALPN specification to use for talking to remote host. + * May return NULL if ALPN is disabled on the connection. + */ +const struct alpn_spec * +Curl_alpn_get_spec(struct Curl_easy *data, struct connectdata *conn); + +/** + * Get the ALPN specification to use for talking to the proxy. + * May return NULL if ALPN is disabled on the connection. + */ +const struct alpn_spec * +Curl_alpn_get_proxy_spec(struct Curl_easy *data, struct connectdata *conn); char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); @@ -95,7 +140,6 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data); /* init the SSL session ID cache */ CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t); void Curl_ssl_version(char *buffer, size_t size); -int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn); /* Certificate information list handling. */ @@ -156,6 +200,9 @@ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, struct connectdata *conn, int sockindex); +CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, int sockindex); @@ -163,6 +210,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data, struct connectdata *conn, int sockindex); +CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); #endif /* !CURL_DISABLE_PROXY */ /** @@ -208,6 +257,9 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option); void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, CURLINFO info, int n); +extern struct Curl_cftype Curl_cft_ssl; +extern struct Curl_cftype Curl_cft_ssl_proxy; + #else /* if not USE_SSL */ /* When SSL support is not present, just define away these function calls */ @@ -218,7 +270,6 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN #define Curl_ssl_engines_list(x) NULL #define Curl_ssl_initsessions(x,y) CURLE_OK -#define Curl_ssl_check_cxn(d,x) 0 #define Curl_ssl_free_certinfo(x) Curl_nop_stmt #define Curl_ssl_kill_session(x) Curl_nop_stmt #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index 6710a2b..a20ca7d 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -33,18 +33,20 @@ struct ssl_connect_data { ssl_connection_state state; ssl_connect_state connecting_state; - const char *hostname; /* hostnaem for verification */ - const char *dispname; /* display version of hostname */ + char *hostname; /* hostname for verification */ + char *dispname; /* display version of hostname */ int port; /* remote port at origin */ + const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ struct ssl_backend_data *backend; /* vtls backend specific props */ - struct Curl_easy *call_data; /* data handle used in current call, - * same as parameter passed, but available - * here for backend internal callbacks - * that need it. NULLed after at the - * end of each vtls filter invcocation. */ + struct cf_call_data call_data; /* data handle used in current call */ + struct curltime handshake_done; /* time when handshake finished */ }; +#define CF_CTX_CALL_DATA(cf) \ + ((struct ssl_connect_data *)(cf)->ctx)->call_data + + /* Definitions for SSL Implementations */ struct Curl_ssl { diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index 7cc4774..2e57899 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -218,29 +218,9 @@ static const struct group_name_map gnm[] = { { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" }, { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" }, { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" }, - { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" }, - { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" }, - { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" }, - { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" }, - { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" }, - { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" }, - { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" }, - { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" }, - { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" }, - { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" }, - { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" }, - { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" }, - { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" }, - { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" }, - { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" }, - { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" }, - { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" }, { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, - { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" }, - { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" }, - { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" }, { 0, NULL } }; #endif @@ -299,8 +279,7 @@ static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_OK; @@ -315,8 +294,7 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_OK; @@ -633,29 +611,18 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif #ifdef HAVE_ALPN - if(cf->conn->bits.tls_enable_alpn) { - char protocols[128]; - *protocols = '\0'; - - /* wolfSSL's ALPN protocol name list format is a comma separated string of - protocols in descending order of preference, eg: "h2,http/1.1" */ - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) { - strcpy(protocols + strlen(protocols), ALPN_H2 ","); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + if(connssl->alpn) { + struct alpn_proto_buf proto; + CURLcode result; - if(wolfSSL_UseALPN(backend->handle, protocols, - (unsigned)strlen(protocols), + result = Curl_alpn_to_proto_str(&proto, connssl->alpn); + if(result || + wolfSSL_UseALPN(backend->handle, (char *)proto.data, proto.len, WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif /* HAVE_ALPN */ @@ -707,7 +674,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #else /* USE_BIO_CHAIN */ /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(backend->handle, (int)cf->conn->sock[cf->sockindex])) { + if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -883,25 +850,11 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); if(rc == SSL_SUCCESS) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol); - - if(protocol_len == ALPN_HTTP_1_1_LENGTH && - !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) - cf->conn->alpn = CURL_HTTP_VERSION_1_1; -#ifdef USE_HTTP2 - else if(data->state.httpwant >= CURL_HTTP_VERSION_2 && - protocol_len == ALPN_H2_LENGTH && - !memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH)) - cf->conn->alpn = CURL_HTTP_VERSION_2; -#endif - else - infof(data, "ALPN, unrecognized protocol %.*s", protocol_len, - protocol); - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol, + protocol_len); } else if(rc == SSL_ALPN_NOT_FOUND) - infof(data, VTLS_INFOF_NO_ALPN); + Curl_alpn_set_negotiated(cf, data, NULL, 0); else { failf(data, "ALPN, failure getting protocol, error %d", rc); return CURLE_SSL_CONNECT_ERROR; @@ -1166,7 +1119,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, { CURLcode result; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int what; /* check if the connection has already been established */ diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h index b2e7c3f..a5ed848 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.h +++ b/Utilities/cmcurl/lib/vtls/wolfssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index 4c1c9a8..39e4fb3 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -48,6 +48,7 @@ #include "curl_ctype.h" #include "hostcheck.h" #include "vtls/vtls.h" +#include "vtls/vtls_int.h" #include "sendf.h" #include "inet_pton.h" #include "curl_base64.h" @@ -1313,7 +1314,8 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, /* Get the server IP address. */ #ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) + if(cf->conn->bits.ipv6_ip && + Curl_inet_pton(AF_INET6, connssl->hostname, &addr)) addrlen = sizeof(struct in6_addr); else #endif @@ -1348,19 +1350,18 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, break; switch(name.tag) { case 2: /* DNS name. */ - matched = 0; len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, name.beg, name.end); - if(len > 0) { - if(size_t)len == strlen(dnsname) - matched = Curl_cert_hostcheck(dnsname, (size_t)len, - connssl->hostname, hostlen); - free(dnsname); - } + if(len > 0 && (size_t)len == strlen(dnsname)) + matched = Curl_cert_hostcheck(dnsname, (size_t)len, + connssl->hostname, hostlen); + else + matched = 0; + free(dnsname); break; case 7: /* IP address. */ - matched = (name.end - name.beg) == addrlen && + matched = (size_t)(name.end - name.beg) == addrlen && !memcmp(&addr, name.beg, addrlen); break; } @@ -1406,8 +1407,10 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, failf(data, "SSL: unable to obtain common name from peer certificate"); else { len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end); - if(len < 0) + if(len < 0) { + free(dnsname); return CURLE_OUT_OF_MEMORY; + } if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ failf(data, "SSL: illegal cert name field"); else if(Curl_cert_hostcheck((const char *) dnsname, diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h index eb8e959..5496de4 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.h +++ b/Utilities/cmcurl/lib/vtls/x509asn1.h @@ -8,7 +8,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/warnless.c b/Utilities/cmcurl/lib/warnless.c index b00d7a5..10c91fb 100644 --- a/Utilities/cmcurl/lib/warnless.c +++ b/Utilities/cmcurl/lib/warnless.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/warnless.h b/Utilities/cmcurl/lib/warnless.h index 4367099..99b2433 100644 --- a/Utilities/cmcurl/lib/warnless.h +++ b/Utilities/cmcurl/lib/warnless.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/wildcard.c b/Utilities/cmcurl/lib/wildcard.c index a3e24b6..3b81c7a 100644 --- a/Utilities/cmcurl/lib/wildcard.c +++ b/Utilities/cmcurl/lib/wildcard.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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/wildcard.h b/Utilities/cmcurl/lib/wildcard.h index 21e933b..9dccab0 100644 --- a/Utilities/cmcurl/lib/wildcard.h +++ b/Utilities/cmcurl/lib/wildcard.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.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/ws.c b/Utilities/cmcurl/lib/ws.c index c1b2622..0fc5e56 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -115,12 +115,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) return result; } -CURLcode Curl_ws_accept(struct Curl_easy *data) +/* + * 'nread' is number of bytes of websocket data already in the buffer at + * 'mem'. + */ +CURLcode Curl_ws_accept(struct Curl_easy *data, + const char *mem, size_t nread) { struct SingleRequest *k = &data->req; struct HTTP *ws = data->req.p.http; struct connectdata *conn = data->conn; struct websocket *wsp = &data->req.p.http->ws; + struct ws_conn *wsc = &conn->proto.ws; CURLcode result; /* Verify the Sec-WebSocket-Accept response. @@ -149,13 +155,21 @@ CURLcode Curl_ws_accept(struct Curl_easy *data) infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]); + Curl_dyn_init(&wsc->early, data->set.buffer_size); + if(nread) { + result = Curl_dyn_addn(&wsc->early, mem, nread); + if(result) + return result; + infof(data, "%zu bytes websocket payload", nread); + wsp->stillb = Curl_dyn_ptr(&wsc->early); + wsp->stillblen = Curl_dyn_len(&wsc->early); + } k->upgr101 = UPGR101_RECEIVED; if(data->set.connect_only) /* switch off non-blocking sockets */ (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE); - wsp->oleft = 0; return result; } @@ -183,12 +197,12 @@ static void ws_decode_shift(struct Curl_easy *data, size_t spent) /* ws_decode() decodes a binary frame into structured WebSocket data, - wpkt - the incoming raw data. If NULL, work on the already buffered data. - ilen - the size of the provided data, perhaps too little, perhaps too much - out - stored pointed to extracted data + data - the transfer + inbuf - incoming raw data. If NULL, work on the already buffered data. + inlen - size of the provided data, perhaps too little, perhaps too much + headlen - stored length of the frame header olen - stored length of the extracted data oleft - number of unread bytes pending to that belongs to this frame - more - if there is more data in there flags - stored bitmask about the frame Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it @@ -246,6 +260,9 @@ static CURLcode ws_decode(struct Curl_easy *data, infof(data, "WS: received OPCODE PONG"); *flags |= CURLWS_PONG; break; + default: + failf(data, "WS: unknown opcode: %x", opcode); + return CURLE_RECV_ERROR; } if(inbuf[1] & WSBIT_MASK) { @@ -296,7 +313,7 @@ static CURLcode ws_decode(struct Curl_easy *data, *oleft = 0; /* bytes yet to come (for this frame) */ } - infof(data, "WS: received %zu bytes payload (%zu left, buflen was %zu)", + infof(data, "WS: received %Ou bytes payload (%Ou left, buflen was %zu)", payloadsize, *oleft, inlen); return CURLE_OK; } @@ -339,9 +356,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */, result = ws_decode(data, wsbuf, buflen, &headlen, &write_len, &fb_left, &recvflags); - consumed += headlen; - wsbuf += headlen; - buflen -= headlen; if(result == CURLE_AGAIN) /* insufficient amount of data, keep it for later. * we pretend to have written all since we have a copy */ @@ -350,6 +364,10 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */, infof(data, "WS: decode error %d", (int)result); return nitems - 1; } + consumed += headlen; + wsbuf += headlen; + buflen -= headlen; + /* New frame. store details about the frame to be reachable with curl_ws_meta() from within the write callback */ ws->ws.frame.age = 0; @@ -392,7 +410,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */, return nitems; } - CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, size_t buflen, size_t *nread, struct curl_ws_frame **metap) @@ -409,7 +426,7 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, return result; while(!done) { - size_t write_len; + size_t datalen; unsigned int recvflags; if(!wsp->stillblen) { @@ -419,26 +436,32 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, data->set.buffer_size, &n); if(result) return result; - if(!n) + if(!n) { /* connection closed */ + infof(data, "connection expectedly closed?"); return CURLE_GOT_NOTHING; + } wsp->stillb = data->state.buffer; wsp->stillblen = n; } - infof(data, "WS: got %u websocket bytes to decode", - (int)wsp->stillblen); + infof(data, "WS: %u bytes left to decode", (int)wsp->stillblen); if(!wsp->frame.bytesleft) { size_t headlen; curl_off_t oleft; /* detect new frame */ result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen, - &headlen, &write_len, &oleft, &recvflags); + &headlen, &datalen, &oleft, &recvflags); if(result == CURLE_AGAIN) /* a packet fragment only */ break; else if(result) return result; + if(datalen > buflen) { + size_t diff = datalen - buflen; + datalen = buflen; + oleft += diff; + } wsp->stillb += headlen; wsp->stillblen -= headlen; wsp->frame.offset = 0; @@ -447,40 +470,45 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, } else { /* existing frame, remaining payload handling */ - write_len = wsp->frame.bytesleft; - if(write_len > wsp->stillblen) - write_len = wsp->stillblen; + datalen = wsp->frame.bytesleft; + if(datalen > wsp->stillblen) + datalen = wsp->stillblen; + if(datalen > buflen) + datalen = buflen; + + wsp->frame.offset += wsp->frame.len; + wsp->frame.bytesleft -= datalen; } + wsp->frame.len = datalen; /* auto-respond to PINGs */ if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) { - infof(data, "WS: auto-respond to PING with a PONG"); + size_t nsent = 0; + infof(data, "WS: auto-respond to PING with a PONG, %zu bytes payload", + datalen); /* send back the exact same content as a PONG */ - result = curl_ws_send(data, wsp->stillb, write_len, - &write_len, 0, CURLWS_PONG); + result = curl_ws_send(data, wsp->stillb, datalen, &nsent, 0, + CURLWS_PONG); if(result) return result; + infof(data, "WS: bytesleft %zu datalen %zu", + wsp->frame.bytesleft, datalen); + /* we handled the data part of the PING, advance over that */ + wsp->stillb += nsent; + wsp->stillblen -= nsent; } - else if(write_len || !wsp->frame.bytesleft) { - if(write_len > buflen) - write_len = buflen; + else if(datalen) { /* copy the payload to the user buffer */ - memcpy(buffer, wsp->stillb, write_len); - *nread = write_len; + memcpy(buffer, wsp->stillb, datalen); + *nread = datalen; done = TRUE; - } - if(write_len) { - /* update buffer and frame info */ - wsp->frame.offset += write_len; - DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)write_len); - if(wsp->frame.bytesleft) - wsp->frame.bytesleft -= write_len; - DEBUGASSERT(write_len <= wsp->stillblen); - wsp->stillblen -= write_len; + + wsp->stillblen -= datalen; if(wsp->stillblen) - wsp->stillb += write_len; - else + wsp->stillb += datalen; + else { wsp->stillb = NULL; + } } } *metap = &wsp->frame; @@ -555,17 +583,27 @@ static size_t ws_packethead(struct Curl_easy *data, } if(!(flags & CURLWS_CONT)) { - /* if not marked as continuing, assume this is the final fragment */ - firstbyte |= WSBIT_FIN | opcode; + if(!ws->ws.contfragment) + /* not marked as continuing, this is the final fragment */ + firstbyte |= WSBIT_FIN | opcode; + else + /* marked as continuing, this is the final fragment; set CONT + opcode and FIN bit */ + firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT; + ws->ws.contfragment = FALSE; + infof(data, "WS: set FIN"); } else if(ws->ws.contfragment) { /* the previous fragment was not a final one and this isn't either, keep a CONT opcode and no FIN bit */ firstbyte |= WSBIT_OPCODE_CONT; + infof(data, "WS: keep CONT, no FIN"); } else { + firstbyte = opcode; ws->ws.contfragment = TRUE; + infof(data, "WS: set CONT, no FIN"); } out[0] = firstbyte; if(len > 65535) { @@ -686,8 +724,14 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer, infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", headlen + buflen, written); - *sent = written; + if(!result) { + /* the *sent number only counts "payload", excluding the header */ + if((size_t)written > headlen) + *sent = written - headlen; + else + *sent = 0; + } return result; } @@ -698,6 +742,20 @@ void Curl_ws_done(struct Curl_easy *data) Curl_dyn_free(&wsp->buf); } +CURLcode Curl_ws_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection) +{ + struct ws_conn *wsc = &conn->proto.ws; + (void)data; + (void)dead_connection; + Curl_dyn_free(&wsc->early); + + /* make sure this is non-blocking to avoid getting stuck in shutdown */ + (void)curlx_nonblock(conn->sock[FIRSTSOCKET], TRUE); + return CURLE_OK; +} + CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) { /* we only return something for websocket, called from within the callback diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h index 2f3ed2d..176dda4 100644 --- a/Utilities/cmcurl/lib/ws.h +++ b/Utilities/cmcurl/lib/ws.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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 @@ -46,24 +46,28 @@ struct websocket { size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete websocket frame uses */ struct curl_ws_frame frame; /* the struct used for frame state */ - curl_off_t oleft; /* outstanding number of payload bytes left from the - server */ size_t stillblen; /* number of bytes left in the buffer to deliver in the next curl_ws_recv() call */ - char *stillb; /* the stillblen pending bytes are here */ + const char *stillb; /* the stillblen pending bytes are here */ curl_off_t sleft; /* outstanding number of payload bytes left to send */ unsigned int xori; /* xor index */ }; -CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); -CURLcode Curl_ws_accept(struct Curl_easy *data); +struct ws_conn { + struct dynbuf early; /* data already read when switching to ws */ +}; +CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); +CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len); size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp); void Curl_ws_done(struct Curl_easy *data); - +CURLcode Curl_ws_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool dead_connection); #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 #endif /* HEADER_CURL_WS_H */ @@ -569,7 +569,6 @@ KWSYS_FILES="\ RegularExpression.hxx \ Status.hxx \ String.h \ - String.hxx \ System.h \ SystemTools.hxx \ Terminal.h" |